diff --git a/configs.js b/configs.js index 6907e60..49582a9 100644 --- a/configs.js +++ b/configs.js @@ -3,7 +3,7 @@ const configs = (function () { // settings const enableLimit = true; // true or false - const limit = 20; // integer + const limit = 5; // integer // animation const scrollSpeed = 30; // milliseconds (DO NOT PUT BELOW 5, COMPUTER MIGHT CRASH) the lower the value, the faster the scroll @@ -149,6 +149,14 @@ const configs = (function () { "!help", ]; + // Admin clear all except streamer - please add commands following the exact format + const adminClearNotStreamerCommands = [ + "!clearnotstreamer", + "!aclearnotstreamer", + "!adminclearnotstreamer", + "!clearns", + ]; + // Admin delete - please add commands following the exact format const adminDeleteCommands = [ "!taskadel", @@ -179,14 +187,17 @@ const configs = (function () { const noTaskAdded = "Looks like you already hit the limit of incomplete tasks, {user}"; const noTaskContent = "Try using !task the-task-you-are-working-on {user}"; - const taskDeleted = "Task '{task}' has been deleted successfully, {user}"; + const taskDeleted = 'Task "{task}" has been deleted successfully, {user}'; const specifyTaskIndex = "Try specifying the index of the task {user}"; const duplicateTask = "Looks like you already have the task '{message}' up there {user}!"; + const clearTasksExceptBroadcaster = + "All tasks have been cleared except for the streamer's, {user}!"; + const adminDeleteTasks = "All of {mentioned}'s tasks have been deleted"; - const taskFinished = "Good job on finishing the task '{task}', {user}!"; + const taskFinished = 'Good job on finishing the task "{task}", {user}!'; const taskEdited = "Task '{originalTask}' has been edited to '{task}' successfully, {user}"; const noTaskEdit = "Try doing !edit [index] [new task] {user}"; @@ -283,6 +294,7 @@ const configs = (function () { checkCommands, clearMyDoneCommands, adminDeleteCommands, + adminClearNotStreamerCommands, adminClearDoneCommands, adminClearAllCommands, additionalCommands, @@ -302,6 +314,7 @@ const configs = (function () { noTaskA, notMod, help, + clearTasksExceptBroadcaster, adminDeleteTasks, clearedMyDone, clearedAll, diff --git a/scripts/index.js b/scripts/index.js index 8ec2427..6c9dd3d 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -42,9 +42,15 @@ ComfyJS.onCommand = (user, command, message, flags, extra) => { // task has no content respond(responses.noTaskContent, params); } else { - params.task = addStatus; + let addResponse = responses.taskAdded; + params.task = addStatus.task; + + if (addStatus.tasksFailedToAdd !== "") { + addResponse += ` | Failed to add task(s): "${addStatus.tasksFailedToAdd}"`; + } + // task added - respond(responses.taskAdded, params); + respond(addResponse, params); } } else if (commands.editTaskCommands.includes(command)) { let editStatus = editTask(user, message); @@ -71,8 +77,14 @@ ComfyJS.onCommand = (user, command, message, flags, extra) => { respond(responses.specifyTaskIndex, params); } else { // task deleted - params.task = removeStatus; - respond(responses.taskDeleted, params); + let deletedResponse = responses.taskDeleted; + params.task = removeStatus.removedTasks; + + if (removeStatus.failedTasks !== "") { + deletedResponse += ` | Failed to delete task(s): "${removeStatus.failedTasks}"`; + } + + respond(deletedResponse, params); } } else if (commands.finishTaskCommands.includes(command)) { let finishStatus = markTaskDone(user, message); @@ -85,8 +97,15 @@ ComfyJS.onCommand = (user, command, message, flags, extra) => { respond(responses.specifyTaskIndex, params); } else { // task finished - params.task = finishStatus; - respond(responses.taskFinished, params); + let finishedResponse = responses.taskFinished; + + params.task = finishStatus.markedTasks; + + if (finishStatus.failedTasks !== "") { + finishedResponse += ` | Failed to finish task(s): "${finishStatus.failedTasks}"`; + } + + respond(finishedResponse, params); } } else if (commands.checkCommands.includes(command)) { if (message === "") { @@ -137,7 +156,7 @@ ComfyJS.onCommand = (user, command, message, flags, extra) => { respond(responses.clearedAll, params); } } else if (commands.clearMyDoneCommands.includes(command)) { - let response = markOwnTaskDone(user); + let response = clearOwnDoneTasks(user); if (response === 0) { // no tasks @@ -146,6 +165,9 @@ ComfyJS.onCommand = (user, command, message, flags, extra) => { } respond(responses.clearedMyDone, params); + } else if (commands.adminClearNotStreamerCommands.includes(command)) { + clearAllExceptStreamer(auth.channel); + respond(responses.clearTasksExceptBroadcaster, params); } else if (commands.helpCommands.includes(command)) { respond(responses.help, params); } else if (commands.additionalCommands[command]) { diff --git a/scripts/taskList.js b/scripts/taskList.js index 46566c3..a52f880 100644 --- a/scripts/taskList.js +++ b/scripts/taskList.js @@ -13,6 +13,7 @@ const settings = configs.settings; const styles = configs.styles; const scrollSpeed = configs.animation.scrollSpeed; let scrolling = false; +let primaryAnimation, secondaryAnimation; function loadGoogleFont(font) { WebFont.load({ @@ -183,22 +184,47 @@ function addTask(username, userColor, task) { return 2; } + let tasksFailedToAdd = []; + // if task has commas if (task.includes(",")) { let tasksToAdd = task.split(",").map((t) => t.trim()); + + if ( + incompleteTasksCount(username) + tasksToAdd.length > + settings.limit && + settings.enableLimit + ) { + tasksFailedToAdd = tasksToAdd.slice( + settings.limit - incompleteTasksCount(username), + tasksToAdd.length + ); + + tasksToAdd = tasksToAdd.slice( + 0, + settings.limit - incompleteTasksCount(username) + ); + } + for (const t of tasksToAdd) { tasks[username].todos.push({ text: t, done: false }); } localStorage.setItem(`tasks`, JSON.stringify(tasks)); renderTaskListToDOM(); - return tasksToAdd.join('", "'); + return { + task: tasksToAdd.join('", "'), + tasksFailedToAdd: tasksFailedToAdd.join('", "') || "", + }; } tasks[username].todos.push({ text: task, done: false }); localStorage.setItem(`tasks`, JSON.stringify(tasks)); renderTaskListToDOM(); - return task; + return { + task: task, + tasksFailedToAdd: "", + }; } // 0: user has no tasks @@ -206,32 +232,103 @@ function addTask(username, userColor, task) { // removed task: success function removeTask(username, task) { const tasks = JSON.parse(localStorage.tasks); + const incompleteTasks = tasks[username].todos.filter((t) => !t.done); + if (!tasks[username] || tasks[username].todos.length === 0) { return 0; } - let index = tasks[username].todos.findIndex( - (t) => t.text.toLowerCase() === task.toLowerCase() - ); - if (index === -1) { - if (isInt(task)) { - index = parseInt(task) - 1; // ACTUAL INDEX - } else if (tasks[username].todos.length === 1) { - index = 0; - } else { - return 1; - } - if (index < 0 || index > tasks[username].todos.length - 1) { - return 1; - } + // match regex: integers separated by space e.g. '1 2 3 4' + if (task.match(/^(\d+ )*\d+$/)) { + // insert commas between integers + task = task.replace(/(\d+)/g, "$1,"); + // remove trailing comma + task = task.slice(0, -1); } - let removedTask = tasks[username].todos[index].text; + let removedTaskIndex = []; + let tasksRemoved = []; + let tasksFailedToRemove = []; - tasks[username].todos.splice(index, 1); - localStorage.setItem(`tasks`, JSON.stringify(tasks)); - renderTaskListToDOM(); + // check if there's a comma in the task (multiple tasks) + if (task.includes(",")) { + let tasksToRemove = task.split(",").map((t) => t.trim()); + for (const t of tasksToRemove) { + let index = tasks[username].todos.findIndex( + (task) => task.text.toLowerCase() === t.toLowerCase() + ); - return removedTask; + if (index === -1) { + if (isInt(t)) { + index = parseInt(t) - 1; // ACTUAL INDEX + } else if (incompleteTasks.length) { + index = tasks[username].todos.findIndex((t) => !t.done); + tasksRemoved.push(tasks[username].todos[index].text); + removedTaskIndex.push(index); + } else { + tasksFailedToRemove.push(t); + continue; + } + + if (index < 0 || index > tasks[username].todos.length - 1) { + tasksFailedToRemove.push(t); + continue; + } else { + tasksRemoved.push(tasks[username].todos[index].text); + removedTaskIndex.push(index); + } + } else { + tasksRemoved.push(tasks[username].todos[index].text); + removedTaskIndex.push(index); + } + } + + if (tasksRemoved.length === 0) { + return 1; + } + + // sort removedTaskIndex in descending order + removedTaskIndex.sort((a, b) => b - a); + + // remove tasks from tasks array + for (const index of removedTaskIndex) { + tasks[username].todos.splice(index, 1); + } + + localStorage.setItem(`tasks`, JSON.stringify(tasks)); + renderTaskListToDOM(); + return { + removedTasks: tasksRemoved.join('", "'), + failedTasks: tasksFailedToRemove.join('", "'), + }; + } else { + let index = tasks[username].todos.findIndex( + (t) => t.text.toLowerCase() === task.toLowerCase() + ); + + if (index === -1) { + if (isInt(task)) { + index = parseInt(task) - 1; // ACTUAL INDEX + } else if (incompleteTasks.length) { + index = tasks[username].todos.findIndex((t) => !t.done); + } else { + return 1; + } + + if (index < 0 || index > tasks[username].todos.length - 1) { + return 1; + } + } + + task = tasks[username].todos[index].text; + + tasks[username].todos.splice(index, 1); + localStorage.setItem(`tasks`, JSON.stringify(tasks)); + renderTaskListToDOM(); + return { + removedTasks: task, + failedTasks: "", + }; + } } // function to determine if string is integer @@ -245,7 +342,7 @@ function isInt(value) { // 0: user has no tasks // 1: success -function markOwnTaskDone(username) { +function clearOwnDoneTasks(username) { const tasks = JSON.parse(localStorage.tasks); if (!tasks[username] || tasks[username].todos.length === 0) { @@ -268,42 +365,93 @@ function markOwnTaskDone(username) { // task: succcess function markTaskDone(username, task) { const tasks = JSON.parse(localStorage.tasks); + const incompleteTasks = tasks[username].todos.filter((t) => !t.done); + // user does not have any tasks if (!tasks[username] || tasks[username].todos.length === 0) { return 0; } - let index = tasks[username].todos.findIndex( - (t) => t.text.toLowerCase() === task.toLowerCase() - ); - // filter by t.done is false - let incompleteTaskCount = tasks[username].todos.filter( - (t) => !t.done - ).length; - if (incompleteTaskCount === 1) { - index = tasks[username].todos.findIndex((t) => !t.done); + // match regex: integers separated by space e.g. '1 2 3 4' + if (task.match(/^(\d+ )*\d+$/)) { + // insert commas between integers + task = task.replace(/(\d+)/g, "$1,"); + // remove trailing comma + task = task.slice(0, -1); } - if (index === -1) { - if (isInt(task)) { - index = parseInt(task) - 1; // ACTUAL INDEX - } else { + let tasksMarkedComplete = []; + let tasksFailedToComplete = []; + + // check if there's a comma in the task (multiple tasks) + if (task.includes(",")) { + let tasksToMarkDone = task.split(",").map((t) => t.trim()); + for (const t of tasksToMarkDone) { + let index = tasks[username].todos.findIndex( + (task) => task.text.toLowerCase() === t.toLowerCase() + ); + + if (index === -1) { + if (isInt(t)) { + index = parseInt(t) - 1; // ACTUAL INDEX + } else if (incompleteTasks.length) { + index = tasks[username].todos.findIndex((t) => !t.done); + tasksMarkedComplete.push(tasks[username].todos[index].text); + } else { + tasksFailedToComplete.push(t); + continue; + } + + if (index < 0 || index > tasks[username].todos.length - 1) { + tasksFailedToComplete.push(t); + continue; + } else { + tasksMarkedComplete.push(tasks[username].todos[index].text); + } + } else { + tasksMarkedComplete.push(tasks[username].todos[index].text); + } + tasks[username].todos[index].done = true; + } + + if (tasksMarkedComplete.length === 0) { return 1; } - if (index < 0 || index > tasks[username].todos.length - 1) { - return 1; + localStorage.setItem(`tasks`, JSON.stringify(tasks)); + renderTaskListToDOM(); + return { + markedTasks: tasksMarkedComplete.join('", "'), + failedTasks: tasksFailedToComplete.join('", "'), + }; + } else { + let index = tasks[username].todos.findIndex( + (t) => t.text.toLowerCase() === task.toLowerCase() + ); + + if (index === -1) { + if (isInt(task)) { + index = parseInt(task) - 1; // ACTUAL INDEX + } else if (incompleteTasks.length) { + index = tasks[username].todos.findIndex((t) => !t.done); + } else { + return 1; + } + + if (index < 0 || index > tasks[username].todos.length - 1) { + return 1; + } } + + task = tasks[username].todos[index].text; + tasks[username].todos[index].done = true; + localStorage.setItem(`tasks`, JSON.stringify(tasks)); + renderTaskListToDOM(); + return { + markedTasks: task, + failedTasks: "", + }; } - - let completedTask = tasks[username].todos[index].text; - - tasks[username].todos[index].done = true; - - localStorage.setItem(`tasks`, JSON.stringify(tasks)); - renderTaskListToDOM(); - - return completedTask; } // 0: user has no tasks @@ -406,13 +554,7 @@ function incompleteTasksCount(username) { if (!tasks[username]) { return 0; } - let count = 0; - for (const task of tasks[username].todos) { - if (!task.done) { - count++; - } - } - return count; + return tasks[username].todos.filter((t) => !t.done).length; } function clearUserTasks(username) { @@ -421,6 +563,7 @@ function clearUserTasks(username) { return; } tasks[username].todos = []; + localStorage.setItem(`tasks`, JSON.stringify(tasks)); renderTaskListToDOM(); } @@ -430,16 +573,22 @@ function clearAllDoneTasks() { for (const user in tasks) { tasks[user].todos = tasks[user].todos.filter((t) => !t.done); } + cancelAnimation(); + scrolling = false; + checkToAnimate(); localStorage.setItem(`tasks`, JSON.stringify(tasks)); renderTaskListToDOM(); } function clearAllTasks() { resetDB(); + cancelAnimation(); + scrolling = false; + checkToAnimate(); renderTaskListToDOM(); } -function smutOnly(streamer) { +function clearAllExceptStreamer(streamer) { // clear all tasks except for broadcasters const tasks = JSON.parse(localStorage.tasks); for (const user in tasks) { @@ -449,7 +598,13 @@ function smutOnly(streamer) { } localStorage.setItem(`tasks`, JSON.stringify(tasks)); + clearAllDoneTasks(); + + cancelAnimation(); + scrolling = false; + checkToAnimate(); + renderTaskListToDOM(); } @@ -522,30 +677,30 @@ function tests1() { let listOfStreamers = [ `cloudydayzzz`, `berryspace`, - `MohFocus`, - `xeno_hiraeth`, - `euphie___`, - `unknownnie`, - `theyolotato`, - `charliosaurus`, - `jutstreams`, - `mikewhatwhere`, - `studypaws`, - `pcc_lanezzz`, - `workwithjandj`, - `studylena`, + // `MohFocus`, + // `xeno_hiraeth`, + // `euphie___`, + // `unknownnie`, + // `theyolotato`, + // `charliosaurus`, + // `jutstreams`, + // `mikewhatwhere`, + // `studypaws`, + // `pcc_lanezzz`, + // `workwithjandj`, + // `studylena`, ]; for (let i = 0; i < listOfStreamers.length; i++) { - addTask(listOfStreamers[i], "#ffc0cb", `task ${i + 1}`); - addTask(listOfStreamers[i], "#ffc0cb", `another task ${i + 1}`); + addTask( + listOfStreamers[i], + "#ffc0cb", + `task 1, task 2, task 3, task 4, task 5` + ); + markTaskDone(listOfStreamers[i], `1, 2, 3, 4, 5`); } } -function tests2() { - smutOnly("theyolotato"); -} - async function checkToAnimate() { // compare task container height and task wrapper height @@ -580,11 +735,11 @@ async function checkToAnimate() { easing: "linear", }; - let primaryAnimation = document + primaryAnimation = document .querySelector(".primary") .animate(primaryKeyFrames, options); - let secondaryAnimation = document + secondaryAnimation = document .querySelector(".secondary") .animate(secondaryKeyFrames, options); @@ -598,22 +753,25 @@ async function checkToAnimate() { scrolling = false; checkToAnimate(); } - } else if (!scrolling) { + } else if (scrolling) { document.querySelector(".secondary").style.display = "none"; // cancel animations - document - .querySelector(".task-container") - .getAnimations() - .forEach((a) => { - a.cancel(); - }); + cancelAnimation(); scrolling = false; } } +function cancelAnimation() { + if (primaryAnimation && secondaryAnimation) { + primaryAnimation.cancel(); + secondaryAnimation.cancel(); + } +} + (function () { - setupDB(); + resetDB(); + tests1(); importStyles(); renderTaskListToDOM(); })();