Implemented rate-mits (kinda) and updated tipping example with whitelisted content
This commit is contained in:
parent
48c664d492
commit
6197f32e3c
7 changed files with 212 additions and 109 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
public/media/svg/watch--green.svg
Normal file
10
public/media/svg/watch--green.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg
|
||||||
|
fill="#3edb8d"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 96 96"
|
||||||
|
width="24px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
>
|
||||||
|
<path d="M72, 21.174V12c0-6.63-5.37-12-12-12H36c-6.63, 0-12, 5.37-12, 12v9.174C16.637, 27.766, 12, 37.34, 12, 48s4.637, 20.234, 12, 26.826 V84c0, 6.63, 5.37, 12, 12, 12h24c6.63, 0, 12-5.37, 12-12v-9.174C79.363, 68.234, 84, 58.66, 84, 48S79.363, 27.766, 72, 21.174z M32, 12 c0-2.21, 1.79-4, 4-4h24c2.21, 0, 4, 1.79, 4, 4v3.751C59.18, 13.354, 53.749, 12, 48, 12s-11.179, 1.354-16, 3.751V12z M64, 84 c0, 2.21-1.79, 4-4, 4H36c-2.21, 0-4-1.79-4-4v-3.751C36.821, 82.646, 42.251, 84, 48, 84s11.18-1.354, 16-3.751V84z M48, 76 c-15.464, 0-28-12.536-28-28s12.536-28, 28-28s28, 12.536, 28, 28S63.464, 76, 48, 76z M56, 60c-1.023, 0-2.047-0.391-2.828-1.172l-8-8 C44.422, 50.078, 44, 49.061, 44, 48V32c0-2.209, 1.791-4, 4-4s4, 1.791, 4, 4v14.343l6.828, 6.829c1.562, 1.562, 1.562, 4.095, 0, 5.656 C58.047, 59.609, 57.023, 60, 56, 60z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 983 B |
3
public/media/svg/watch.svg
Normal file
3
public/media/svg/watch.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<path d="M72, 21.174V12c0-6.63-5.37-12-12-12H36c-6.63, 0-12, 5.37-12, 12v9.174C16.637, 27.766, 12, 37.34, 12, 48s4.637, 20.234, 12, 26.826 V84c0, 6.63, 5.37, 12, 12, 12h24c6.63, 0, 12-5.37, 12-12v-9.174C79.363, 68.234, 84, 58.66, 84, 48S79.363, 27.766, 72, 21.174z M32, 12 c0-2.21, 1.79-4, 4-4h24c2.21, 0, 4, 1.79, 4, 4v3.751C59.18, 13.354, 53.749, 12, 48, 12s-11.179, 1.354-16, 3.751V12z M64, 84 c0, 2.21-1.79, 4-4, 4H36c-2.21, 0-4-1.79-4-4v-3.751C36.821, 82.646, 42.251, 84, 48, 84s11.18-1.354, 16-3.751V84z M48, 76 c-15.464, 0-28-12.536-28-28s12.536-28, 28-28s28, 12.536, 28, 28S63.464, 76, 48, 76z M56, 60c-1.023, 0-2.047-0.391-2.828-1.172l-8-8 C44.422, 50.078, 44, 49.061, 44, 48V32c0-2.209, 1.791-4, 4-4s4, 1.791, 4, 4v14.343l6.828, 6.829c1.562, 1.562, 1.562, 4.095, 0, 5.656 C58.047, 59.609, 57.023, 60, 56, 60z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 934 B |
|
@ -3,6 +3,17 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 1rem;
|
top: 1rem;
|
||||||
|
|
||||||
|
&.waiting::before {
|
||||||
|
top: 0; right: 0;
|
||||||
|
bottom: 0; left: 0;
|
||||||
|
|
||||||
|
background-color: rgba($white, 0.7);
|
||||||
|
content: "";
|
||||||
|
cursor: url("/assets/media/svg/watch--green.svg"), auto;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
}
|
}
|
||||||
|
|
74
server.js
74
server.js
|
@ -102,7 +102,7 @@ fastify.ready(err => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "landed on tour":
|
case "landed on tour":
|
||||||
generateTrendingContent(1, result => {
|
generateContent(1, result => {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
"html": result,
|
"html": result,
|
||||||
"message": "updated html",
|
"message": "updated html",
|
||||||
|
@ -113,7 +113,7 @@ fastify.ready(err => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "request for tour, example 1":
|
case "request for tour, example 1":
|
||||||
generateTrendingContent(1, result => {
|
generateContent(1, result => {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
"html": result,
|
"html": result,
|
||||||
"message": "updated html",
|
"message": "updated html",
|
||||||
|
@ -128,7 +128,7 @@ fastify.ready(err => {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "request for tour, example 3":
|
case "request for tour, example 3":
|
||||||
generateTrendingContent(3, result => {
|
generateContent(3, result => {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
"html": result,
|
"html": result,
|
||||||
"message": "updated html",
|
"message": "updated html",
|
||||||
|
@ -347,21 +347,73 @@ function generateMemeCreator(socket) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateTrendingContent(exampleNumber, displayTrendingContent) {
|
function generateContent(exampleNumber, displayTrendingContent) {
|
||||||
return getTrendingContent().then(response => {
|
if (exampleNumber === 1) {
|
||||||
if (!response || !response.success || response.success !== true || !response.data) return "";
|
return getTrendingContent().then(response => {
|
||||||
|
if (!response || !response.success || response.success !== true || !response.data) return "";
|
||||||
|
|
||||||
|
const rawContentCollection = [];
|
||||||
|
const renderedContentCollection = [];
|
||||||
|
const trendingContentData = response.data;
|
||||||
|
|
||||||
|
for (const data of trendingContentData) {
|
||||||
|
rawContentCollection.push(fetchMetadata({ claim: data.url, method: "resolve", example: exampleNumber }));
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(rawContentCollection).then(collection => {
|
||||||
|
for (const part of collection) {
|
||||||
|
if (
|
||||||
|
!part.value.stream.metadata.nsfw &&
|
||||||
|
part.value.stream.metadata.thumbnail &&
|
||||||
|
part.channel_name
|
||||||
|
) {
|
||||||
|
renderedContentCollection.push(`
|
||||||
|
<figure class="tour__content__trend">
|
||||||
|
<img alt="${part.name}" data-action="choose claim" data-claim-id="${exampleNumber === 1 ? part.name : part.claim_id}" src="${part.value.stream.metadata.thumbnail}"/>
|
||||||
|
|
||||||
|
<figcaption data-action="choose claim" data-claim-id="${exampleNumber === 1 ? part.name : part.claim_id}">
|
||||||
|
${part.value.stream.metadata.title}
|
||||||
|
<span>${part.channel_name}</span>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayTrendingContent(renderedContentCollection.join(""));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exampleNumber === 3) {
|
||||||
|
const approvedUrls = [
|
||||||
|
"LBRY#3db81c073f82fd1bb670c65f526faea3b8546720",
|
||||||
|
"correlation-can-imply-causation#173412f5b1b7aa63a752e8832406aafd9f1ecb4e",
|
||||||
|
"thanos-is-the-protagonist-how-infinity#2a7f5db2678177435b1dee6c9e38e035ead450b6nyte",
|
||||||
|
"epic-arcade-mode-duos-nickatnyte-molt#d81bac6d49b1f92e58c37a5f633a27a45b43405e",
|
||||||
|
"political-correctness-a-force-for-good-a#b4668c0bd096317b44c40738c099b6618095e75f",
|
||||||
|
"10-secrets-hidden-inside-famous-logos#007789cc45cbb4255cf02ba77cbf84ca8e3d7561",
|
||||||
|
"ever-wonder-how-bitcoin-and-other#1ac47b8b3def40a25850dc726a09ce23d09e7009",
|
||||||
|
"bankrupt-pan-am#784b3c215a6f06b663fc1aa292bcb19f29c489bb",
|
||||||
|
"minecraft-in-real-life-iron-man#758dd6497cdfc401ae1f25984738d024d47b50af",
|
||||||
|
"ethan-shows-kyle-warframe-skyvault#8a7401b88d5ed0376d98f16808194d4dcb05b284"
|
||||||
|
];
|
||||||
|
|
||||||
const rawContentCollection = [];
|
const rawContentCollection = [];
|
||||||
const renderedContentCollection = [];
|
const renderedContentCollection = [];
|
||||||
const trendingContentData = response.data;
|
|
||||||
|
|
||||||
for (const data of trendingContentData) {
|
for (const url of approvedUrls) {
|
||||||
rawContentCollection.push(fetchMetadata({ claim: data.url, method: "resolve", example: exampleNumber }));
|
rawContentCollection.push(fetchMetadata({ claim: url, method: "resolve", example: exampleNumber }));
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(rawContentCollection).then(collection => {
|
Promise.all(rawContentCollection).then(collection => {
|
||||||
for (const part of collection) {
|
for (const part of collection) {
|
||||||
if (!part.value.stream.metadata.nsfw && part.value.stream.metadata.thumbnail && part.channel_name) {
|
if (
|
||||||
|
part &&
|
||||||
|
part.value &&
|
||||||
|
part.value.stream.metadata.thumbnail &&
|
||||||
|
part.channel_name
|
||||||
|
) {
|
||||||
renderedContentCollection.push(`
|
renderedContentCollection.push(`
|
||||||
<figure class="tour__content__trend">
|
<figure class="tour__content__trend">
|
||||||
<img alt="${part.name}" data-action="choose claim" data-claim-id="${exampleNumber === 1 ? part.name : part.claim_id}" src="${part.value.stream.metadata.thumbnail}"/>
|
<img alt="${part.name}" data-action="choose claim" data-claim-id="${exampleNumber === 1 ? part.name : part.claim_id}" src="${part.value.stream.metadata.thumbnail}"/>
|
||||||
|
@ -377,7 +429,7 @@ function generateTrendingContent(exampleNumber, displayTrendingContent) {
|
||||||
|
|
||||||
displayTrendingContent(renderedContentCollection.join(""));
|
displayTrendingContent(renderedContentCollection.join(""));
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTrendingContent() {
|
function getTrendingContent() {
|
||||||
|
|
|
@ -16,89 +16,12 @@ if (window.location.href.search && window.location.href.split("?url=")[1]) { //
|
||||||
$("body").on("click", "[data-action]", event => {
|
$("body").on("click", "[data-action]", event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
let exampleNumber;
|
$(".tour").addClass("waiting");
|
||||||
const data = event.currentTarget.dataset;
|
|
||||||
|
|
||||||
if (!parseInt($(".tour__sidebar__example.active")[0].dataset.example)) return;
|
setTimeout(() => {
|
||||||
exampleNumber = parseInt($(".tour__sidebar__example.active")[0].dataset.example);
|
handleExamples(event);
|
||||||
|
$(".tour").removeClass("waiting");
|
||||||
switch(data.action) {
|
}, 1500); // "rate-limit" to allow example divs time to populate
|
||||||
case "choose claim":
|
|
||||||
fetchMetadata(exampleNumber, data.claimId);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "execute claim":
|
|
||||||
if (!$("#fetch-claim-uri").val()) return;
|
|
||||||
fetchMetadata(exampleNumber, $("#fetch-claim-uri").val());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "tour, example 1":
|
|
||||||
if ($("#tour-loader").hasClass("tour__content__meme")) {
|
|
||||||
$("#tour-loader").removeClass("tour__content__meme").addClass("tour__content__trends");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#fetch-claim-uri").val(""); // reset URL bar
|
|
||||||
if ($("#tour-url")[0].style.display === "none") $("#tour-url").show();
|
|
||||||
|
|
||||||
$(".tour__sidebar__example").removeClass("active");
|
|
||||||
$(".tour__sidebar__example:nth-child(1)").addClass("active");
|
|
||||||
|
|
||||||
$("#tour-loader").empty().show();
|
|
||||||
$("#tour-results").empty().show();
|
|
||||||
|
|
||||||
send(JSON.stringify({
|
|
||||||
"message": `request for ${data.action}`
|
|
||||||
}));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "tour, example 2":
|
|
||||||
if ($("#tour-loader").hasClass("tour__content__trends")) {
|
|
||||||
$("#tour-loader").removeClass("tour__content__trends").addClass("tour__content__meme");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#fetch-claim-uri").val(""); // reset URL bar
|
|
||||||
$("#tour-url").hide();
|
|
||||||
|
|
||||||
$(".tour__sidebar__example").removeClass("active");
|
|
||||||
$(".tour__sidebar__example:nth-child(2)").addClass("active");
|
|
||||||
|
|
||||||
$("#tour-loader").empty().show();
|
|
||||||
$("#tour-results").empty().show();
|
|
||||||
|
|
||||||
send(JSON.stringify({
|
|
||||||
"message": `request for ${data.action}`
|
|
||||||
}));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "tour, example 3":
|
|
||||||
if ($("#tour-loader").hasClass("tour__content__meme")) {
|
|
||||||
$("#tour-loader").removeClass("tour__content__meme").addClass("tour__content__trends");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#fetch-claim-uri").val(""); // reset URL bar
|
|
||||||
if ($("#tour-url")[0].style.display === "none") $("#tour-url").show();
|
|
||||||
|
|
||||||
$(".tour__sidebar__example").removeClass("active");
|
|
||||||
$(".tour__sidebar__example:nth-child(3)").addClass("active");
|
|
||||||
|
|
||||||
$("#tour-loader").empty().show();
|
|
||||||
$("#tour-results").empty().show();
|
|
||||||
|
|
||||||
send(JSON.stringify({
|
|
||||||
"message": `request for ${data.action}`
|
|
||||||
}));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "upload image":
|
|
||||||
fetchMetadata(exampleNumber, getMemeInfo());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("body").on("click", ".tour__content__meme__canvas__thumbnail", event => {
|
$("body").on("click", ".tour__content__meme__canvas__thumbnail", event => {
|
||||||
|
@ -119,6 +42,18 @@ $("body").on("keyup", "#meme-top-line, #meme-bottom-line", () => updateCanvas())
|
||||||
|
|
||||||
// H E L P E R S
|
// H E L P E R S
|
||||||
|
|
||||||
|
function clearCanvas(canvas) {
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.globalCompositeOperation = "copy";
|
||||||
|
ctx.strokeStyle = "transparent";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.lineTo(0, 0);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
function detectLanguageAndUpdate() { // eslint-disable-line
|
function detectLanguageAndUpdate() { // eslint-disable-line
|
||||||
const compare = (array1, array2) => array2.filter(value => array2.indexOf(value)); // compare two arrays and get match(es)
|
const compare = (array1, array2) => array2.filter(value => array2.indexOf(value)); // compare two arrays and get match(es)
|
||||||
const memeLocaleObject = $("#meme-language").children();
|
const memeLocaleObject = $("#meme-language").children();
|
||||||
|
@ -138,7 +73,28 @@ function detectLanguageAndUpdate() { // eslint-disable-line
|
||||||
) $("#meme-language").children(`option[value="${compare(memeLocales, userLocales)[0]}"]`).attr("selected", true);
|
) $("#meme-language").children(`option[value="${compare(memeLocales, userLocales)[0]}"]`).attr("selected", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function debounce(func, wait, immediate) {
|
||||||
|
let timeout;
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
const context = this;
|
||||||
|
const args = arguments;
|
||||||
|
|
||||||
|
const later = () => {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) func.apply(context, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const callNow = immediate && !timeout;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
if (callNow) func.apply(context, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function initializeTour() {
|
function initializeTour() {
|
||||||
|
$(".tour").addClass("waiting");
|
||||||
$("#fetch-claim-uri").val("").focus(); // reset
|
$("#fetch-claim-uri").val("").focus(); // reset
|
||||||
$(".tour__sidebar__example:nth-child(1)").addClass("active");
|
$(".tour__sidebar__example:nth-child(1)").addClass("active");
|
||||||
|
|
||||||
|
@ -146,10 +102,9 @@ function initializeTour() {
|
||||||
"message": "landed on tour"
|
"message": "landed on tour"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/*
|
setTimeout(() => {
|
||||||
TODO:
|
$(".tour").removeClass("waiting");
|
||||||
- Account for someone wanting to make multiple resolves
|
}, 2000);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -243,19 +198,91 @@ function getMemeInfo() { // TODO: Error handling
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleExamples = debounce(event => {
|
||||||
|
let exampleNumber;
|
||||||
|
const data = event.currentTarget.dataset;
|
||||||
|
|
||||||
|
if (!parseInt($(".tour__sidebar__example.active")[0].dataset.example)) return;
|
||||||
|
exampleNumber = parseInt($(".tour__sidebar__example.active")[0].dataset.example);
|
||||||
|
|
||||||
function clearCanvas(canvas) {
|
switch(data.action) {
|
||||||
const ctx = canvas.getContext("2d");
|
case "choose claim":
|
||||||
|
fetchMetadata(exampleNumber, data.claimId);
|
||||||
|
break;
|
||||||
|
|
||||||
ctx.save();
|
case "execute claim":
|
||||||
ctx.globalCompositeOperation = "copy";
|
if (!$("#fetch-claim-uri").val()) return;
|
||||||
ctx.strokeStyle = "transparent";
|
fetchMetadata(exampleNumber, $("#fetch-claim-uri").val());
|
||||||
ctx.beginPath();
|
break;
|
||||||
ctx.lineTo(0, 0);
|
|
||||||
ctx.stroke();
|
case "tour, example 1":
|
||||||
ctx.restore();
|
if ($("#tour-loader").hasClass("tour__content__meme")) {
|
||||||
}
|
$("#tour-loader").removeClass("tour__content__meme").addClass("tour__content__trends");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#fetch-claim-uri").val(""); // reset URL bar
|
||||||
|
if ($("#tour-url")[0].style.display === "none") $("#tour-url").show();
|
||||||
|
|
||||||
|
$(".tour__sidebar__example").removeClass("active");
|
||||||
|
$(".tour__sidebar__example:nth-child(1)").addClass("active");
|
||||||
|
|
||||||
|
$("#tour-loader").empty().show();
|
||||||
|
$("#tour-results").empty().show();
|
||||||
|
|
||||||
|
send(JSON.stringify({
|
||||||
|
"message": `request for ${data.action}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "tour, example 2":
|
||||||
|
if ($("#tour-loader").hasClass("tour__content__trends")) {
|
||||||
|
$("#tour-loader").removeClass("tour__content__trends").addClass("tour__content__meme");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#fetch-claim-uri").val(""); // reset URL bar
|
||||||
|
$("#tour-url").hide();
|
||||||
|
|
||||||
|
$(".tour__sidebar__example").removeClass("active");
|
||||||
|
$(".tour__sidebar__example:nth-child(2)").addClass("active");
|
||||||
|
|
||||||
|
$("#tour-loader").empty().show();
|
||||||
|
$("#tour-results").empty().show();
|
||||||
|
|
||||||
|
send(JSON.stringify({
|
||||||
|
"message": `request for ${data.action}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "tour, example 3":
|
||||||
|
if ($("#tour-loader").hasClass("tour__content__meme")) {
|
||||||
|
$("#tour-loader").removeClass("tour__content__meme").addClass("tour__content__trends");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#fetch-claim-uri").val(""); // reset URL bar
|
||||||
|
if ($("#tour-url")[0].style.display === "none") $("#tour-url").show();
|
||||||
|
|
||||||
|
$(".tour__sidebar__example").removeClass("active");
|
||||||
|
$(".tour__sidebar__example:nth-child(3)").addClass("active");
|
||||||
|
|
||||||
|
$("#tour-loader").empty().show();
|
||||||
|
$("#tour-results").empty().show();
|
||||||
|
|
||||||
|
send(JSON.stringify({
|
||||||
|
"message": `request for ${data.action}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "upload image":
|
||||||
|
fetchMetadata(exampleNumber, getMemeInfo());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
|
||||||
function initCanvas() { // eslint-disable-line
|
function initCanvas() { // eslint-disable-line
|
||||||
const canvas = document.getElementById("meme-canvas");
|
const canvas = document.getElementById("meme-canvas");
|
||||||
|
|
Loading…
Add table
Reference in a new issue