first commit
This commit is contained in:
commit
dae6c59363
13 changed files with 1637 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.vscode/*
|
||||
styles/taskList.css.map
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 RyanPython
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
207
README.md
Normal file
207
README.md
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
# Multi-Task widget Infinity (PRIVATE)
|
||||
|
||||

|
||||
|
||||
It's basically the same as the original "Multi-Task widget" but with infinite scroll! (more aesthetic, hehe)
|
||||
|
||||
## Logs
|
||||
|
||||
---
|
||||
|
||||
I'll just update you guys on the Discord
|
||||
|
||||
---
|
||||
|
||||
<!-- directory -->
|
||||
|
||||
## Content
|
||||
|
||||
- [Multi-Task widget Infinity (PRIVATE)](#multi-task-widget-infinity-private)
|
||||
- [Logs](#logs)
|
||||
- [Content](#content)
|
||||
- [Commands](#commands)
|
||||
- [Moderators only](#moderators-only)
|
||||
- [Installation](#installation)
|
||||
- [Method 1 (fastest, easiest way)](#method-1-fastest-easiest-way)
|
||||
- [Method 2 (better if you're using an alt account as a bot)](#method-2-better-if-youre-using-an-alt-account-as-a-bot)
|
||||
- [Customization settings](#customization-settings)
|
||||
- [settings](#settings)
|
||||
- [task list styles](#task-list-styles)
|
||||
- [header](#header)
|
||||
- [body](#body)
|
||||
- [task](#task)
|
||||
- [Credits](#credits)
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
### Moderators only
|
||||
|
||||
- !clear - Clear all tasks
|
||||
- !cleardone - Clear all done tasks
|
||||
- !adel @user - Remove all tasks from a user
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Method 1 (fastest, easiest way)
|
||||
|
||||
1. Install the zip or clone the repository
|
||||
|
||||
2. Go to https://twitchapps.com/tmi/ to create an oauth token (it acts as a password)
|
||||
|
||||
3. Setup authentication in `auth.js`
|
||||
|
||||
4. Setup `Browser Source` in OBS studio or other streaming software with the following settings:
|
||||
|
||||
- Local File: `checked`
|
||||
- Local File: `index.html`
|
||||
|
||||
### Method 2 (better if you're using an alt account as a bot)
|
||||
|
||||
1. Install the zip or clone the repository
|
||||
|
||||
2. Create a Twitch application [here](https://dev.twitch.tv/console/apps) \(Log in with your alternate Twitch account if you wish to use a different account as a bot account\)
|
||||
|
||||
3. Copy the `Client ID` from the application
|
||||
|
||||
4. Create token using `get_token.txt`. Replace `CLIENT_ID` with the `Client ID` from the application
|
||||
|
||||
5. Setup authentication in `auth.js`
|
||||
|
||||
6. Setup `Browser Source` in OBS studio or other streaming software with the following settings:
|
||||
|
||||
- Local File: `checked`
|
||||
- Local File: `index.html`
|
||||
|
||||
---
|
||||
|
||||
## Customization settings
|
||||
|
||||
Edit `configs.js` to edit the style of the task list
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### settings
|
||||
|
||||
`showDoneTasks`:
|
||||
|
||||
**true**: show the done tasks
|
||||
|
||||
**false**: hide (and remove) the done tasks
|
||||
|
||||
`enableLimit`:
|
||||
|
||||
**true**: limit the number of incomplete tasks stored in the list
|
||||
|
||||
**false**: unlimited incomplete tasks
|
||||
|
||||
`limit`: The number of incomplete tasks to store in the list (integer)
|
||||
|
||||
`OneScrollPerMS`: Per MS, how many pixels to scroll (integer)
|
||||
|
||||
`pauseBetweenScrolls`: How many MS to pause between each end of the scroll (integer)
|
||||
|
||||
### task list styles
|
||||
|
||||
`taskListWidth`: width of the task list (px)
|
||||
|
||||
`taskListHeight`: height of the task list (px)
|
||||
|
||||
`taskListBackgroundColor`: background color of the task list (hex only)
|
||||
|
||||
`taskListBackgroundOpacity`: opacity of the task list background (0.0 - 1.0)
|
||||
|
||||
`taskListBorderWidth`: width of the task list border (px)
|
||||
|
||||
`taskListBorderColor`: color of the task list border (hex or name)
|
||||
|
||||
`taskListBorderRadius`: how round the task list border should be (px)
|
||||
|
||||
`taskListHorizontalPadding`: horizontal padding of the task list (px)
|
||||
|
||||
`taskListVerticalPadding`: vertical padding of the task list (px)
|
||||
|
||||
### header
|
||||
|
||||
`headerBackgroundColor`: background color of the header (hex only)
|
||||
|
||||
`headerBackgroundOpacity`: opacity of the header background (0.0 - 1.0)
|
||||
|
||||
`headerBorderWidth`: width of the header border (px)
|
||||
|
||||
`headerBorderColor`: color of the header border (hex or name)
|
||||
|
||||
`headerBorderRadius`: how round the header border should be (px)
|
||||
|
||||
`headerFontFamily`: font family of the header (font name)
|
||||
|
||||
`headerGoogleFont`: whether to use Google font for the header (true or false)
|
||||
|
||||
`headerFontSize`: font size of the header (px)
|
||||
|
||||
`headerFontWeight`: font weight of the header (normal, bold, or number)
|
||||
|
||||
`headerFontColor`: font color of the header (hex or name)
|
||||
|
||||
### body
|
||||
|
||||
`bodyBackgroundColor`: background color of the body (hex only)
|
||||
|
||||
`bodyBackgroundOpacity`: opacity of the body background (0.0 - 1.0)
|
||||
|
||||
`bodyBorderWidth`: width of the body border (px)
|
||||
|
||||
`bodyBorderColor`: color of the body border (hex or name)
|
||||
|
||||
`bodyBorderRadius`: how round the body border should be (px)
|
||||
|
||||
`bodyHorizontalPadding`: horizontal padding of the body (px)
|
||||
|
||||
`bodyVerticalPadding`: vertical padding of the body (px)
|
||||
|
||||
### task
|
||||
|
||||
`lineHeight`: line height of the task (number)
|
||||
|
||||
`usernameFontWeight`: font weight of the username (normal, bold, or number)
|
||||
|
||||
`usernameColor`: color of the username (hex or name or "" for Twitch user color)
|
||||
|
||||
`taskBackgroundColor`: background color of the task (hex only)
|
||||
|
||||
`taskBackgroundOpacity`: opacity of the task background (0.0 - 1.0)
|
||||
|
||||
`taskFontFamily`: font family of the task (font name)
|
||||
|
||||
`taskGoogleFont`: whether to use Google font for the task (true or false)
|
||||
|
||||
`taskFontSize`: font size of the task (px)
|
||||
|
||||
`taskFontColor`: font color of the task (hex or name)
|
||||
|
||||
`taskBorderColor`: color of the task border (hex or name)
|
||||
|
||||
`taskBorderWidth`: width of the task border (px)
|
||||
|
||||
`taskBorderRadius`: how round the task border should be (px)
|
||||
|
||||
`taskMarginBottom`: bottom margin of the task (px)
|
||||
|
||||
`taskHorizontalPadding`: horizontal padding of the task (px)
|
||||
|
||||
`taskVerticalPadding`: vertical padding of the task (px)
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
**Author:** [**@RyanPython**](https://twitch.tv/RyanPython)
|
||||
|
||||
**Special thanks to:**
|
||||
|
||||
- [**@Instafluff**](https://twitch.tv/Instafluff) \(for the Comfy JS library\)
|
||||
12
auth.js
Normal file
12
auth.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const auth = (function () {
|
||||
// Authentication and channels - required
|
||||
const channel = ""; // your channel
|
||||
const username = ""; // bot account
|
||||
const oauth = ""; // should be oauth:xxxxxxxxxxxx from the bot account DO NOT INCLUDE oauth:
|
||||
|
||||
return {
|
||||
channel,
|
||||
username,
|
||||
oauth,
|
||||
};
|
||||
})();
|
||||
313
configs.js
Normal file
313
configs.js
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
const configs = (function () {
|
||||
"use strict";
|
||||
|
||||
// settings
|
||||
const enableLimit = true; // true or false
|
||||
const limit = 20; // integer
|
||||
|
||||
// animation
|
||||
const scrollSpeed = 30; // milliseconds (DO NOT PUT BELOW 5, COMPUTER MIGHT CRASH) the lower the value, the faster the scroll
|
||||
const pauseBetweenScrolls = 2000; // milliseconds
|
||||
const gapBetweenScrolls = "100px"; // px
|
||||
|
||||
// STYLES
|
||||
// task list
|
||||
const taskListWidth = "400px"; // px
|
||||
const taskListHeight = "300px"; // px
|
||||
|
||||
const taskListBackgroundColor = "#000000"; // hex only
|
||||
const taskListBackgroundOpacity = 0; // 0.0 - 1.0
|
||||
|
||||
const taskListBorderWidth = "0px"; // px
|
||||
const taskListBorderColor = "#ffffff"; // hex or name
|
||||
const taskListBorderRadius = "5px"; // px
|
||||
|
||||
const taskListHorizontalPadding = "0px"; // px
|
||||
const taskListVerticalPadding = "0px"; // px
|
||||
|
||||
// header
|
||||
const headerHorizontalPadding = "10px"; // px
|
||||
const headerVerticalPadding = "10px"; // px
|
||||
|
||||
const headerBackgroundColor = "#000000"; // hex only
|
||||
const headerBackgroundOpacity = 0.8; // 0.0 - 1.0
|
||||
|
||||
const headerBorderWidth = "2px"; // px
|
||||
const headerBorderColor = "#ffffff"; // hex or name
|
||||
const headerBorderRadius = "5px"; // px
|
||||
|
||||
const headerFontFamily = "Fredoka"; // font name
|
||||
const headerGoogleFont = true; // true: use google font, false: use system font
|
||||
|
||||
const headerFontSize = "25px"; // px
|
||||
const headerFontWeight = "bold"; // normal or bold or number
|
||||
|
||||
const headerFontColor = "#ffffff"; // hex or name
|
||||
|
||||
// body
|
||||
const bodyBackgroundColor = "#000000"; // hex only
|
||||
const bodyBackgroundOpacity = 0; // 0.0 - 1.0
|
||||
|
||||
const bodyBorderWidth = "0px"; // px
|
||||
const bodyBorderColor = "#fff"; // hex or name
|
||||
const bodyBorderRadius = "5px"; // px
|
||||
|
||||
// task
|
||||
const lineHeight = 1.5; // number
|
||||
const usernameFontWeight = "bold"; // normal or bold or number
|
||||
const usernameColor = "#ffc0cb"; // hex or name or "" for twitch user color
|
||||
|
||||
const taskWidth = "98%";
|
||||
|
||||
const taskBackgroundColor = "#000000"; // hex only
|
||||
const taskBackgroundOpacity = 0.7; // 0.0 - 1.0
|
||||
|
||||
const taskFontFamily = "Poppins"; // font name
|
||||
const taskGoogleFont = true; // true: use google font, false: use system font
|
||||
|
||||
const taskFontSize = "20px"; // px
|
||||
const taskFontColor = "#ffffff"; // hex or name
|
||||
|
||||
const taskBorderColor = "#000000"; // hex or name
|
||||
const taskBorderWidth = "0px"; // px
|
||||
const taskBorderRadius = "5px"; // px
|
||||
|
||||
const taskMarginBottom = "5px"; // px
|
||||
const taskHorizontalPadding = "20px"; // px
|
||||
const taskVerticalPadding = "10px";
|
||||
|
||||
// Add task commands - please add commands in the exact format
|
||||
const addTaskCommands = [
|
||||
"!taska",
|
||||
"!taskadd",
|
||||
"!atask",
|
||||
"!addtask",
|
||||
"!task",
|
||||
"!add",
|
||||
"!todo",
|
||||
];
|
||||
|
||||
// Edit task commands - please add commands in the exact format
|
||||
const editTaskCommands = [
|
||||
"!taske",
|
||||
"!taskedit",
|
||||
"!etask",
|
||||
"!edittask",
|
||||
"!edit",
|
||||
];
|
||||
|
||||
// Delete task commands - please add commands in the exact format
|
||||
const deleteTaskCommands = [
|
||||
"!taskd",
|
||||
"!taskdel",
|
||||
"!taskdelete",
|
||||
"!deltask",
|
||||
"!deletetask",
|
||||
"!taskr",
|
||||
"!taskremove",
|
||||
"!rtask",
|
||||
"!removetask",
|
||||
"!remove",
|
||||
"!delete",
|
||||
];
|
||||
|
||||
// Finish task commands - please add commands in the exact format
|
||||
const finishTaskCommands = [
|
||||
"!taskf",
|
||||
"!taskfinish",
|
||||
"!ftask",
|
||||
"!finishtask",
|
||||
"!taskd",
|
||||
"!taskdone",
|
||||
"!donetask",
|
||||
"!dtask",
|
||||
"!finish",
|
||||
"!done",
|
||||
"!finished",
|
||||
];
|
||||
|
||||
// Check task commands - please add commands in the exact format
|
||||
const checkCommands = [
|
||||
"!taskc",
|
||||
"!taskcheck",
|
||||
"!ctask",
|
||||
"!checktask",
|
||||
"!mytask",
|
||||
"!check",
|
||||
];
|
||||
|
||||
// Help commands - please add commands in the exact format
|
||||
const helpCommands = [
|
||||
"!taskh",
|
||||
"!taskhelp",
|
||||
"!htask",
|
||||
"!helptask",
|
||||
"!tasks",
|
||||
"!help",
|
||||
];
|
||||
|
||||
// Admin delete - please add commands following the exact format
|
||||
const adminDeleteCommands = [
|
||||
"!taskadel",
|
||||
"!adel",
|
||||
"!adelete",
|
||||
"!admindelete",
|
||||
];
|
||||
|
||||
// Admin clear done - please add commands following the exact format
|
||||
const adminClearDoneCommands = [
|
||||
"!acleardone",
|
||||
"!admincleardone",
|
||||
"!cleardone",
|
||||
];
|
||||
|
||||
const adminClearAllCommands = [
|
||||
"!clearall",
|
||||
"!allclear",
|
||||
"!adminclearall",
|
||||
"!adminallclear",
|
||||
"!aclearall",
|
||||
"!aclear",
|
||||
"!clear",
|
||||
];
|
||||
|
||||
// Responses
|
||||
const taskAdded = 'The task "{message}" has been added, {user}!';
|
||||
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 specifyTaskIndex = "Try specifying the index of the task {user}";
|
||||
|
||||
const duplicateTask =
|
||||
"Looks like you already have the task '{message}' up there {user}!";
|
||||
|
||||
const adminDeleteTasks = "All of {mentioned}'s tasks have been deleted";
|
||||
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}";
|
||||
|
||||
const noTask = "Looks like you don't have a task up there {user}";
|
||||
const noTaskA = "Looks like there is no task from that user there {user}";
|
||||
const notMod = "Permission denied, {user}; Mods only";
|
||||
const clearedAll = "All tasks have been cleared!";
|
||||
const clearedDone = "All completed tasks have been cleared!";
|
||||
const specifyUser = "Try specifying a user to delete their tasks {user}";
|
||||
const help = `{user} Use the following commands to help you out - !task !edit !remove !done. For mods, you can do !adel @user. More commmands here: https://github.com/liyunze-coding/Chat-Task-Tic-Overlay/blob/main/MultiTask.md/`;
|
||||
// to edit check task command, go to
|
||||
// scripts/taskList.js
|
||||
// function checkTasks(username)
|
||||
|
||||
const additionalCommands = {
|
||||
"!botcred":
|
||||
"{user} Ryan is the creator of this bot! You can find him on https://github.com/liyunze-coding or https://www.twitch.tv/ryanpython",
|
||||
};
|
||||
|
||||
const titles = [
|
||||
"!taskhelp",
|
||||
"!task",
|
||||
"!edit",
|
||||
"!remove",
|
||||
"!done",
|
||||
"!botcred",
|
||||
];
|
||||
|
||||
const settings = {
|
||||
enableLimit,
|
||||
limit,
|
||||
headerGoogleFont,
|
||||
taskGoogleFont,
|
||||
};
|
||||
|
||||
const styles = {
|
||||
taskListWidth,
|
||||
taskListHeight,
|
||||
taskListBackgroundColor,
|
||||
taskListBackgroundOpacity,
|
||||
taskListBorderWidth,
|
||||
taskListBorderColor,
|
||||
taskListBorderRadius,
|
||||
taskListHorizontalPadding,
|
||||
taskListVerticalPadding,
|
||||
headerHorizontalPadding,
|
||||
headerVerticalPadding,
|
||||
headerBackgroundColor,
|
||||
headerBackgroundOpacity,
|
||||
headerBorderWidth,
|
||||
headerBorderColor,
|
||||
headerBorderRadius,
|
||||
headerFontSize,
|
||||
headerFontWeight,
|
||||
headerFontFamily,
|
||||
headerFontColor,
|
||||
bodyBackgroundColor,
|
||||
bodyBackgroundOpacity,
|
||||
bodyBorderWidth,
|
||||
bodyBorderColor,
|
||||
bodyBorderRadius,
|
||||
taskWidth,
|
||||
lineHeight,
|
||||
usernameFontWeight,
|
||||
usernameColor,
|
||||
taskBackgroundColor,
|
||||
taskBackgroundOpacity,
|
||||
taskFontFamily,
|
||||
taskFontSize,
|
||||
taskFontColor,
|
||||
taskBorderColor,
|
||||
taskBorderWidth,
|
||||
taskBorderRadius,
|
||||
taskMarginBottom,
|
||||
taskHorizontalPadding,
|
||||
taskVerticalPadding,
|
||||
};
|
||||
|
||||
const animation = {
|
||||
scrollSpeed,
|
||||
pauseBetweenScrolls,
|
||||
gapBetweenScrolls,
|
||||
titles,
|
||||
};
|
||||
|
||||
const commands = {
|
||||
addTaskCommands,
|
||||
editTaskCommands,
|
||||
deleteTaskCommands,
|
||||
finishTaskCommands,
|
||||
helpCommands,
|
||||
checkCommands,
|
||||
adminDeleteCommands,
|
||||
adminClearDoneCommands,
|
||||
adminClearAllCommands,
|
||||
additionalCommands,
|
||||
};
|
||||
|
||||
const responses = {
|
||||
taskAdded,
|
||||
noTaskAdded,
|
||||
noTaskContent,
|
||||
taskDeleted,
|
||||
specifyTaskIndex,
|
||||
duplicateTask,
|
||||
taskFinished,
|
||||
taskEdited,
|
||||
noTaskEdit,
|
||||
noTask,
|
||||
noTaskA,
|
||||
notMod,
|
||||
help,
|
||||
adminDeleteTasks,
|
||||
clearedAll,
|
||||
clearedDone,
|
||||
specifyUser,
|
||||
};
|
||||
|
||||
return {
|
||||
settings,
|
||||
styles,
|
||||
animation,
|
||||
commands,
|
||||
responses,
|
||||
};
|
||||
})();
|
||||
5
get_token.txt
Normal file
5
get_token.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
https://id.twitch.tv/oauth2/authorize
|
||||
?client_id=CLIENT_ID_HERE
|
||||
&redirect_uri=http://localhost
|
||||
&response_type=token
|
||||
&scope=channel:moderate+chat:edit+chat:read+channel:manage:broadcast+user:edit:broadcast+channel:read:redemptions+user:read:email
|
||||
BIN
images/labels.png
Normal file
BIN
images/labels.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 263 KiB |
BIN
images/preview.png
Normal file
BIN
images/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
44
index.html
Normal file
44
index.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/comfy.js@latest/dist/comfy.min.js"></script>
|
||||
|
||||
<script src="./auth.js"></script>
|
||||
<script src="./configs.js"></script>
|
||||
<script defer src="scripts/index.js"></script>
|
||||
<script defer src="scripts/taskList.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="styles/taskList.css" />
|
||||
|
||||
<title>Task List</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<div class="header">
|
||||
<div class="title">!taskhelp</div>
|
||||
<div class="task-count">0/0</div>
|
||||
</div>
|
||||
<div class="task-wrapper">
|
||||
<div class="task-container primary">
|
||||
<!--
|
||||
<div class="task">
|
||||
<div class="username">RyanPython</div>
|
||||
<ol class="user-tasks">
|
||||
<li class="todo">complete website</li>
|
||||
<li class="todo">work on overlay</li>
|
||||
</ol>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
<div class="task-container secondary"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
147
scripts/index.js
Normal file
147
scripts/index.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
const commands = configs.commands;
|
||||
const responses = configs.responses;
|
||||
|
||||
let params = {};
|
||||
|
||||
function respond(template, params) {
|
||||
ComfyJS.Say(
|
||||
template
|
||||
.replace("{user}", `@${params.user}`)
|
||||
.replace("{message}", params.message)
|
||||
.replace("{mentioned}", `@${params.mentioned}`)
|
||||
.replace("{task}", params.task)
|
||||
.replace("{originalTask}", params.originalTask)
|
||||
);
|
||||
}
|
||||
|
||||
function isMod(flags) {
|
||||
return flags.broadcaster || flags.mod;
|
||||
}
|
||||
|
||||
ComfyJS.onCommand = (user, command, message, flags, extra) => {
|
||||
command = `!${command.toLowerCase()}`;
|
||||
|
||||
params = {
|
||||
user: user,
|
||||
message: message,
|
||||
mentioned: "",
|
||||
task: "",
|
||||
originalTask: "",
|
||||
};
|
||||
|
||||
if (commands.addTaskCommands.includes(command)) {
|
||||
let addStatus = addTask(user, extra.userColor, message);
|
||||
|
||||
if (addStatus === 0) {
|
||||
// limit has reached
|
||||
respond(responses.noTaskAdded, params);
|
||||
} else if (addStatus === 1) {
|
||||
// duplicate task
|
||||
respond(responses.duplicateTask, params);
|
||||
} else if (addStatus === 2) {
|
||||
// task has no content
|
||||
respond(responses.noTaskContent, params);
|
||||
} else if (addStatus === 3) {
|
||||
// task added
|
||||
respond(responses.taskAdded, params);
|
||||
}
|
||||
} else if (commands.editTaskCommands.includes(command)) {
|
||||
let editStatus = editTask(user, message);
|
||||
|
||||
if (editStatus === 0) {
|
||||
// no task
|
||||
respond(responses.noTask, params);
|
||||
} else if (editStatus === 1) {
|
||||
// invalid input
|
||||
respond(responses.noTaskEdit, params);
|
||||
} else {
|
||||
// task edited
|
||||
params.task = editStatus[1];
|
||||
params.originalTask = editStatus[0];
|
||||
respond(responses.taskEdited, params);
|
||||
}
|
||||
} else if (commands.deleteTaskCommands.includes(command)) {
|
||||
let removeStatus = removeTask(user, message);
|
||||
if (removeStatus === 0) {
|
||||
// no task
|
||||
respond(responses.noTask, params);
|
||||
} else if (removeStatus === 1) {
|
||||
// invalid input
|
||||
respond(responses.specifyTaskIndex, params);
|
||||
} else {
|
||||
// task deleted
|
||||
params.task = removeStatus;
|
||||
respond(responses.taskDeleted, params);
|
||||
}
|
||||
} else if (commands.finishTaskCommands.includes(command)) {
|
||||
let finishStatus = markTaskDone(user, message);
|
||||
|
||||
if (finishStatus === 0) {
|
||||
// user has no tasks
|
||||
respond(responses.noTask, params);
|
||||
} else if (finishStatus === 1) {
|
||||
// invalid input
|
||||
respond(responses.specifyTaskIndex, params);
|
||||
} else {
|
||||
// task finished
|
||||
params.task = finishStatus;
|
||||
respond(responses.taskFinished, params);
|
||||
}
|
||||
} else if (commands.checkCommands.includes(command)) {
|
||||
if (message === "") {
|
||||
let response = checkTasks(user);
|
||||
if (response === 0) {
|
||||
return respond(responses.noTask, params);
|
||||
}
|
||||
return ComfyJS.Say(response);
|
||||
} else {
|
||||
let mentioned = message.replace("@", "");
|
||||
let response = checkTasks(mentioned);
|
||||
if (response === 0) {
|
||||
// no tasks
|
||||
return respond(responses.noTaskA, params);
|
||||
}
|
||||
return ComfyJS.Say(response);
|
||||
}
|
||||
} else if (commands.adminDeleteCommands.includes(command)) {
|
||||
if (!isMod(flags)) {
|
||||
respond(responses.notMod, params);
|
||||
return;
|
||||
}
|
||||
let mentioned = message.replace("@", "");
|
||||
|
||||
if (mentioned === "") {
|
||||
respond(responses.specifyUser, params);
|
||||
return;
|
||||
}
|
||||
|
||||
clearUserTasks(mentioned);
|
||||
params.mentioned = mentioned;
|
||||
respond(responses.adminDeleteTasks, params);
|
||||
return mentioned;
|
||||
} else if (commands.adminClearDoneCommands.includes(command)) {
|
||||
if (!isMod(flags)) {
|
||||
respond(responses.notMod, params);
|
||||
return;
|
||||
} else {
|
||||
clearAllDoneTasks();
|
||||
respond(responses.clearedDone, params);
|
||||
}
|
||||
} else if (commands.adminClearAllCommands.includes(command)) {
|
||||
if (!isMod(flags)) {
|
||||
respond(responses.notMod, params);
|
||||
return;
|
||||
} else {
|
||||
clearAllTasks();
|
||||
respond(responses.clearedAll, params);
|
||||
}
|
||||
} else if (commands.helpCommands.includes(command)) {
|
||||
respond(responses.help, params);
|
||||
} else if (commands.additionalCommands[command]) {
|
||||
respond(commands.additionalCommands[command], params);
|
||||
} else {
|
||||
// command not found
|
||||
}
|
||||
};
|
||||
|
||||
ComfyJS.Init(auth.username, `oauth:${auth.oauth}`, [auth.channel]);
|
||||
592
scripts/taskList.js
Normal file
592
scripts/taskList.js
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
/*
|
||||
DB structure:
|
||||
|
||||
- tasks
|
||||
- username:
|
||||
- todos (array)
|
||||
- [{text, done}, {text, done}]
|
||||
- done
|
||||
- userColor
|
||||
*/
|
||||
|
||||
const settings = configs.settings;
|
||||
const styles = configs.styles;
|
||||
const scrollSpeed = configs.animation.scrollSpeed;
|
||||
let scrolling = false;
|
||||
|
||||
function loadGoogleFont(font) {
|
||||
WebFont.load({
|
||||
google: {
|
||||
families: [font],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// convert taskListBorderColor to task-list-border-color
|
||||
function convertToCSSVar(name) {
|
||||
let cssVar = name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
||||
return `--${cssVar}`;
|
||||
}
|
||||
|
||||
// hex to rgb that accepts 3 or 6 digits
|
||||
function hexToRgb(hex) {
|
||||
// remove # if present
|
||||
if (hex[0] === "#") {
|
||||
hex = hex.slice(1);
|
||||
}
|
||||
|
||||
let r = 0,
|
||||
g = 0,
|
||||
b = 0;
|
||||
|
||||
if (hex.length == 3) {
|
||||
// 3 digits
|
||||
r = "0x" + hex[0] + hex[0];
|
||||
g = "0x" + hex[1] + hex[1];
|
||||
b = "0x" + hex[2] + hex[2];
|
||||
} else if (hex.length == 6) {
|
||||
// 6 digits
|
||||
r = "0x" + hex[0] + hex[1];
|
||||
g = "0x" + hex[2] + hex[3];
|
||||
b = "0x" + hex[4] + hex[5];
|
||||
}
|
||||
|
||||
// interger value of rgb
|
||||
r = +r;
|
||||
g = +g;
|
||||
b = +b;
|
||||
|
||||
return `${r}, ${g}, ${b}`;
|
||||
}
|
||||
|
||||
// import styles from configs
|
||||
function importStyles() {
|
||||
const styles = configs.styles;
|
||||
|
||||
// fonts
|
||||
if (configs.settings.headerGoogleFont) {
|
||||
loadGoogleFont(styles.headerFontFamily);
|
||||
}
|
||||
if (configs.settings.taskGoogleFont) {
|
||||
loadGoogleFont(styles.taskFontFamily);
|
||||
}
|
||||
|
||||
const stylesToImport = Object.keys(styles).filter((style) => {
|
||||
return !style.includes("Background");
|
||||
});
|
||||
|
||||
stylesToImport.forEach((style) => {
|
||||
document.documentElement.style.setProperty(
|
||||
convertToCSSVar(style),
|
||||
styles[style]
|
||||
);
|
||||
});
|
||||
|
||||
let backgroundStyles = Object.keys(styles).filter((style) => {
|
||||
return style.includes("Background");
|
||||
});
|
||||
|
||||
// use regex to filter out after "Background"
|
||||
backgroundStyles = backgroundStyles.map((style) => {
|
||||
return style.replace(/Background.*/, "");
|
||||
});
|
||||
|
||||
// loop through backgroundstyles
|
||||
backgroundStyles.forEach((style) => {
|
||||
// get background color and opacity
|
||||
let backgroundColor = styles[`${style}BackgroundColor`];
|
||||
let backgroundOpacity = styles[`${style}BackgroundOpacity`];
|
||||
|
||||
let cssStyle = convertToCSSVar(style);
|
||||
|
||||
// set background color
|
||||
document.documentElement.style.setProperty(
|
||||
`${cssStyle}-background-color`,
|
||||
`rgba(${hexToRgb(backgroundColor)}, ${backgroundOpacity})`
|
||||
);
|
||||
});
|
||||
|
||||
// interval the task title
|
||||
setInterval(async () => {
|
||||
let taskTitle = document.querySelector(".title");
|
||||
let taskTitleText = taskTitle.innerText;
|
||||
|
||||
// cycle through a list of titles
|
||||
let titles = configs.animation.titles;
|
||||
|
||||
// get current title
|
||||
let currentTitle = titles.indexOf(taskTitleText);
|
||||
|
||||
// if current title is the last title, set it to the first title
|
||||
if (currentTitle === titles.length - 1) {
|
||||
currentTitle = 0;
|
||||
} else {
|
||||
currentTitle++;
|
||||
}
|
||||
|
||||
// on change title, add fade animation
|
||||
taskTitle.classList.add("fade");
|
||||
await sleep(500);
|
||||
|
||||
// set new title
|
||||
taskTitle.innerText = titles[currentTitle];
|
||||
|
||||
await sleep(100);
|
||||
|
||||
// remove fade animation
|
||||
taskTitle.classList.remove("fade");
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
function resetDB() {
|
||||
localStorage.clear();
|
||||
setupDB();
|
||||
}
|
||||
|
||||
function setupDB() {
|
||||
if (!localStorage.tasks) {
|
||||
localStorage.setItem(`tasks`, "{}");
|
||||
}
|
||||
}
|
||||
|
||||
// 0: limit has reached
|
||||
// 1: duplicate task
|
||||
// 2: invalid input
|
||||
// 3: success
|
||||
function addTask(username, userColor, task) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
if (!tasks[username]) {
|
||||
tasks[username] = {
|
||||
todos: [],
|
||||
done: [],
|
||||
userColor: userColor,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
incompleteTasksCount(username) >= settings.limit &&
|
||||
settings.enableLimit
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (
|
||||
tasks[username].todos.find(
|
||||
(t) => t.text.toLowerCase() === task.toLowerCase()
|
||||
)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if task is whitespace or empty
|
||||
if (!task || !task.trim()) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
tasks[username].todos.push({ text: task, done: false });
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
renderTaskListToDOM();
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 0: user has no tasks
|
||||
// 1: invalid input
|
||||
// removed task: success
|
||||
function removeTask(username, task) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
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;
|
||||
}
|
||||
}
|
||||
let removedTask = tasks[username].todos[index].text;
|
||||
|
||||
tasks[username].todos.splice(index, 1);
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
renderTaskListToDOM();
|
||||
|
||||
return removedTask;
|
||||
}
|
||||
|
||||
// function to determine if string is integer
|
||||
function isInt(value) {
|
||||
return (
|
||||
!isNaN(value) &&
|
||||
parseInt(Number(value)) == value &&
|
||||
!isNaN(parseInt(value, 10))
|
||||
);
|
||||
}
|
||||
|
||||
// 0: user has no tasks
|
||||
// 1: invalid input
|
||||
// task: succcess
|
||||
function markTaskDone(username, task) {
|
||||
const tasks = JSON.parse(localStorage.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);
|
||||
}
|
||||
|
||||
if (index === -1) {
|
||||
if (isInt(task)) {
|
||||
index = parseInt(task) - 1; // ACTUAL INDEX
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (index < 0 || index > tasks[username].todos.length - 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 1: invalid input
|
||||
// [original task, new task]: succcess
|
||||
function editTask(username, message) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
let noSpecifiedIndex = false;
|
||||
|
||||
if (!tasks[username] || tasks[username].todos.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// example 'message': 1 new task
|
||||
let index = parseInt(message.split(" ")[0]) - 1; // ACTUAL INDEX
|
||||
|
||||
let incompleteTaskCount = tasks[username].todos.filter(
|
||||
(t) => !t.done
|
||||
).length;
|
||||
|
||||
if (incompleteTaskCount === 1 && index !== 0) {
|
||||
// find index of incomplete task
|
||||
index = tasks[username].todos.findIndex((t) => !t.done);
|
||||
noSpecifiedIndex = true;
|
||||
}
|
||||
|
||||
if (isNaN(index)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let newTask = message.split(" ").slice(1).join(" ");
|
||||
|
||||
if (noSpecifiedIndex) {
|
||||
newTask = message;
|
||||
}
|
||||
|
||||
// if newTask is empty or whitespace, return 1
|
||||
if (!newTask || !newTask.trim()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (index < 0 || index > tasks[username].todos.length - 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let originalTask = tasks[username].todos[index].text;
|
||||
|
||||
tasks[username].todos[index].text = newTask;
|
||||
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
renderTaskListToDOM();
|
||||
|
||||
return [originalTask, newTask];
|
||||
}
|
||||
|
||||
// 0: user has no tasks
|
||||
function checkTasks(username) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
if (!tasks[username]) {
|
||||
return 0;
|
||||
}
|
||||
// filter completed tasks
|
||||
const incompleteTasks = tasks[username].todos.filter((t) => !t.done);
|
||||
const completedTasks = tasks[username].todos.filter((t) => t.done);
|
||||
|
||||
// format incomplete tasks into string: 1. task 1 | 2. task 2 | 3. task 3...
|
||||
let reply = `@${username} incomplete tasks (${incompleteTasks.length}) : `;
|
||||
let taskIndex;
|
||||
for (let i = 0; i < incompleteTasks.length; i++) {
|
||||
// get index of task by task name
|
||||
taskIndex =
|
||||
tasks[username].todos.findIndex(
|
||||
(t) =>
|
||||
t.text.toLowerCase() ===
|
||||
incompleteTasks[i].text.toLowerCase()
|
||||
) + 1;
|
||||
reply += `${taskIndex}. ${incompleteTasks[i].text} | `;
|
||||
}
|
||||
reply = reply.slice(0, -3);
|
||||
|
||||
// format completed tasks into string: 1. task 1 | 2. task 2 | 3. task 3...
|
||||
reply += `\n|| completed tasks (${completedTasks.length}) : `;
|
||||
|
||||
for (let j = 0; j < completedTasks.length; j++) {
|
||||
taskIndex =
|
||||
tasks[username].todos.findIndex(
|
||||
(t) =>
|
||||
t.text.toLowerCase() ===
|
||||
completedTasks[j].text.toLowerCase()
|
||||
) + 1;
|
||||
reply += `${taskIndex}. ${completedTasks[j].text} | `;
|
||||
}
|
||||
reply = reply.slice(0, -3);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
function incompleteTasksCount(username) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
if (!tasks[username]) {
|
||||
return 0;
|
||||
}
|
||||
let count = 0;
|
||||
for (const task of tasks[username].todos) {
|
||||
if (!task.done) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function clearUserTasks(username) {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
if (!tasks[username]) {
|
||||
return;
|
||||
}
|
||||
tasks[username].todos = [];
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
renderTaskListToDOM();
|
||||
}
|
||||
|
||||
function clearAllDoneTasks() {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
for (const user in tasks) {
|
||||
tasks[user].todos = tasks[user].todos.filter((t) => !t.done);
|
||||
}
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
renderTaskListToDOM();
|
||||
}
|
||||
|
||||
function clearAllTasks() {
|
||||
resetDB();
|
||||
renderTaskListToDOM();
|
||||
}
|
||||
|
||||
function smutOnly(streamer) {
|
||||
// clear all tasks except for broadcasters
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
for (const user in tasks) {
|
||||
if (user.toLowerCase() !== streamer.toLowerCase()) {
|
||||
tasks[user].todos = [];
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(`tasks`, JSON.stringify(tasks));
|
||||
clearAllDoneTasks();
|
||||
renderTaskListToDOM();
|
||||
}
|
||||
|
||||
function renderTaskListToDOM() {
|
||||
const tasks = JSON.parse(localStorage.tasks);
|
||||
|
||||
// console.log(tasks);
|
||||
|
||||
const taskContainers = document.querySelectorAll(".task-container");
|
||||
taskContainers.forEach((taskList) => {
|
||||
taskList.innerHTML = "";
|
||||
|
||||
let totalTaskCount = 0;
|
||||
let completedTasksCount = 0;
|
||||
for (const user in tasks) {
|
||||
const userTasks = tasks[user];
|
||||
if (userTasks.todos.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const userColor = userTasks.userColor;
|
||||
|
||||
const taskDiv = document.createElement("div");
|
||||
taskDiv.classList.add("task");
|
||||
taskList.appendChild(taskDiv);
|
||||
|
||||
const usernameDiv = document.createElement("div");
|
||||
usernameDiv.classList.add("username");
|
||||
usernameDiv.innerText = user;
|
||||
|
||||
if (styles.usernameColor === "") {
|
||||
usernameDiv.style.color = userColor;
|
||||
} else {
|
||||
usernameDiv.style.color = styles.usernameColor;
|
||||
}
|
||||
|
||||
taskDiv.appendChild(usernameDiv);
|
||||
|
||||
const olListDiv = document.createElement("ol");
|
||||
olListDiv.classList.add("user-tasks");
|
||||
taskDiv.appendChild(olListDiv);
|
||||
|
||||
for (const task of userTasks.todos) {
|
||||
const taskElement = document.createElement("li");
|
||||
taskElement.classList.add("todo");
|
||||
|
||||
totalTaskCount++;
|
||||
|
||||
if (task.done) {
|
||||
taskElement.classList.add("done");
|
||||
completedTasksCount++;
|
||||
}
|
||||
|
||||
taskElement.innerText = task.text;
|
||||
olListDiv.appendChild(taskElement);
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector(
|
||||
".task-count"
|
||||
).innerText = `${completedTasksCount}/${totalTaskCount}`;
|
||||
});
|
||||
checkToAnimate();
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// unit tests
|
||||
function tests1() {
|
||||
let listOfStreamers = [
|
||||
`cloudydayzzz`,
|
||||
`berryspace`,
|
||||
`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}`);
|
||||
}
|
||||
}
|
||||
|
||||
function tests2() {
|
||||
smutOnly("theyolotato");
|
||||
}
|
||||
|
||||
async function checkToAnimate() {
|
||||
// compare task container height and task wrapper height
|
||||
|
||||
let taskContainer = document.querySelector(".task-container");
|
||||
let taskContainerHeight = taskContainer.scrollHeight;
|
||||
|
||||
let taskWrapper = document.querySelector(".task-wrapper");
|
||||
let taskWrapperHeight = taskWrapper.clientHeight;
|
||||
|
||||
console.log(taskContainerHeight > taskWrapperHeight);
|
||||
|
||||
// scroll task wrapper up and down once
|
||||
if (taskContainerHeight > taskWrapperHeight) {
|
||||
if (!scrolling) {
|
||||
document.querySelector(".secondary").style.display = "flex";
|
||||
|
||||
let finalHeight = taskContainerHeight + 100;
|
||||
|
||||
let primaryKeyFrames = [
|
||||
{ transform: `translateY(0)` },
|
||||
{ transform: `translateY(-${finalHeight}px)` },
|
||||
];
|
||||
|
||||
let secondaryKeyFrames = [
|
||||
{ transform: `translateY(${finalHeight}px)` },
|
||||
{ transform: `translateY(0)` },
|
||||
];
|
||||
|
||||
let scrollingSpeed = (finalHeight / scrollSpeed) * 1000;
|
||||
|
||||
let options = {
|
||||
duration: scrollingSpeed,
|
||||
iterations: 1,
|
||||
easing: "linear",
|
||||
};
|
||||
|
||||
let primaryAnimation = document
|
||||
.querySelector(".primary")
|
||||
.animate(primaryKeyFrames, options);
|
||||
|
||||
let secondaryAnimation = document
|
||||
.querySelector(".secondary")
|
||||
.animate(secondaryKeyFrames, options);
|
||||
|
||||
primaryAnimation.play();
|
||||
secondaryAnimation.play();
|
||||
|
||||
scrolling = true;
|
||||
|
||||
await sleep(scrollingSpeed);
|
||||
|
||||
scrolling = false;
|
||||
checkToAnimate();
|
||||
}
|
||||
} else if (!scrolling) {
|
||||
document.querySelector(".secondary").style.display = "none";
|
||||
|
||||
// cancel animations
|
||||
document
|
||||
.querySelector(".task-container")
|
||||
.getAnimations()
|
||||
.forEach((a) => {
|
||||
a.cancel();
|
||||
});
|
||||
scrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
setupDB();
|
||||
importStyles();
|
||||
renderTaskListToDOM();
|
||||
})();
|
||||
123
styles/taskList.css
Normal file
123
styles/taskList.css
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
:root {
|
||||
--task-list-width: 400px;
|
||||
--task-list-height: 300px;
|
||||
--task-list-background-color: rgba(0, 0, 0, 0.8);
|
||||
--task-list-border-width: 2px;
|
||||
--task-list-border-color: white;
|
||||
--task-list-border-radius: 5px;
|
||||
--task-list-horizontal-padding: 0px;
|
||||
--task-list-vertical-padding: 0px;
|
||||
--header-horizontal-padding: 10px;
|
||||
--header-vertical-padding: 10px;
|
||||
--header-background-color: rgba(0, 0, 0, 0.8);
|
||||
--header-border-width: 2px;
|
||||
--header-border-color: white;
|
||||
--header-border-radius: 5px;
|
||||
--header-font-size: 20px;
|
||||
--header-font-family: sans-serif;
|
||||
--header-font-color: white;
|
||||
--header-font-weight: bold;
|
||||
--body-background-color: rgba(0, 0, 0, 0.7);
|
||||
--body-border-width: 5px;
|
||||
--body-border-color: white;
|
||||
--body-border-radius: 5px;
|
||||
--line-height: 1.5;
|
||||
--username-font-weight: bold;
|
||||
--task-background-color: rgba(0, 0, 0, 0.7);
|
||||
--task-font-family: sans-serif;
|
||||
--task-font-size: 25px;
|
||||
--task-font-color: white;
|
||||
--task-border-color: black;
|
||||
--task-border-width: 0px;
|
||||
--task-border-radius: 5px;
|
||||
--task-horizontal-padding: 0px;
|
||||
--task-vertical-padding: 0px;
|
||||
--task-margin-bottom: 5px;
|
||||
--task-horizontal-padding: 5px;
|
||||
--task-vertical-padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
width: var(--task-list-width);
|
||||
height: var(--task-list-height);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(--task-list-vertical-padding) var(--task-list-horizontal-padding);
|
||||
background-color: var(--task-list-background-color);
|
||||
border: solid var(--task-list-border-width) var(--task-list-border-color);
|
||||
border-radius: var(--task-list-border-radius);
|
||||
}
|
||||
#main-container .header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: var(--header-vertical-padding) var(--header-horizontal-padding);
|
||||
border: solid var(--header-border-width) var(--header-border-color);
|
||||
border-radius: var(--header-border-radius);
|
||||
color: var(--header-font-color);
|
||||
background: var(--header-background-color);
|
||||
font-family: var(--header-font-family);
|
||||
font-size: var(--header-font-size);
|
||||
font-weight: var(--header-font-weight);
|
||||
}
|
||||
#main-container .header .title {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
#main-container .header .fade {
|
||||
opacity: 0;
|
||||
}
|
||||
#main-container .task-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: var(--body-background-color);
|
||||
color: var(--task-font-color);
|
||||
font-family: var(--task-font-family);
|
||||
font-size: var(--task-font-size);
|
||||
line-height: var(--line-height);
|
||||
border: var(--body-border-width) solid var(--body-border-color);
|
||||
border-radius: var(--body-border-radius);
|
||||
z-index: 1;
|
||||
}
|
||||
#main-container .task-wrapper .task-container {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
#main-container .task-wrapper .task-container .task {
|
||||
padding: var(--task-vertical-padding) var(--task-horizontal-padding);
|
||||
background: var(--task-background-color);
|
||||
margin-bottom: var(--task-margin-bottom);
|
||||
width: var(--task-width);
|
||||
border: solid var(--task-border-width) var(--task-border-color);
|
||||
border-radius: var(--task-border-radius);
|
||||
}
|
||||
#main-container .task-wrapper .task-container .task .username {
|
||||
font-weight: var(--username-font-weight);
|
||||
}
|
||||
#main-container .task-wrapper .task-container .task .user-tasks {
|
||||
list-style-position: inside;
|
||||
}
|
||||
#main-container .task-wrapper .task-container .task .user-tasks .todo {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#main-container .task-wrapper .task-container .task .user-tasks .done {
|
||||
text-decoration: line-through;
|
||||
}/*# sourceMappingURL=taskList.css.map */
|
||||
171
styles/taskList.scss
Normal file
171
styles/taskList.scss
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
:root {
|
||||
--task-list-width: 400px;
|
||||
--task-list-height: 300px;
|
||||
|
||||
--task-list-background-color: rgba(0, 0, 0, 0.8);
|
||||
|
||||
--task-list-border-width: 2px;
|
||||
--task-list-border-color: white;
|
||||
--task-list-border-radius: 5px;
|
||||
|
||||
--task-list-horizontal-padding: 0px;
|
||||
--task-list-vertical-padding: 0px;
|
||||
|
||||
--header-horizontal-padding: 10px;
|
||||
--header-vertical-padding: 10px;
|
||||
|
||||
--header-background-color: rgba(0, 0, 0, 0.8);
|
||||
|
||||
--header-border-width: 2px;
|
||||
--header-border-color: white;
|
||||
--header-border-radius: 5px;
|
||||
|
||||
--header-font-size: 20px;
|
||||
--header-font-family: sans-serif;
|
||||
--header-font-color: white;
|
||||
--header-font-weight: bold;
|
||||
|
||||
--body-background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
--body-border-width: 5px;
|
||||
--body-border-color: white;
|
||||
--body-border-radius: 5px;
|
||||
|
||||
--line-height: 1.5;
|
||||
--username-font-weight: bold;
|
||||
|
||||
--task-background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
--task-font-family: sans-serif;
|
||||
--task-font-size: 25px;
|
||||
--task-font-color: white;
|
||||
|
||||
--task-border-color: black;
|
||||
--task-border-width: 0px;
|
||||
--task-border-radius: 5px;
|
||||
|
||||
--task-horizontal-padding: 0px;
|
||||
--task-vertical-padding: 0px;
|
||||
|
||||
--task-margin-bottom: 5px;
|
||||
--task-horizontal-padding: 5px;
|
||||
--task-vertical-padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
width: var(--task-list-width);
|
||||
height: var(--task-list-height);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
padding: var(--task-list-vertical-padding)
|
||||
var(--task-list-horizontal-padding);
|
||||
|
||||
background-color: var(--task-list-background-color);
|
||||
|
||||
border: solid var(--task-list-border-width) var(--task-list-border-color);
|
||||
border-radius: var(--task-list-border-radius);
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: var(--header-vertical-padding) var(--header-horizontal-padding);
|
||||
|
||||
border: solid var(--header-border-width) var(--header-border-color);
|
||||
border-radius: var(--header-border-radius);
|
||||
|
||||
color: var(--header-font-color);
|
||||
background: var(--header-background-color);
|
||||
|
||||
font-family: var(--header-font-family);
|
||||
font-size: var(--header-font-size);
|
||||
font-weight: var(--header-font-weight);
|
||||
|
||||
.title {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
.fade {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.task-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
position: relative;
|
||||
|
||||
background: var(--body-background-color);
|
||||
|
||||
color: var(--task-font-color);
|
||||
font-family: var(--task-font-family);
|
||||
font-size: var(--task-font-size);
|
||||
|
||||
line-height: var(--line-height);
|
||||
|
||||
border: var(--body-border-width) solid var(--body-border-color);
|
||||
border-radius: var(--body-border-radius);
|
||||
|
||||
z-index: 1;
|
||||
|
||||
.task-container {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
|
||||
left: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
.task {
|
||||
padding: var(--task-vertical-padding)
|
||||
var(--task-horizontal-padding);
|
||||
background: var(--task-background-color);
|
||||
margin-bottom: var(--task-margin-bottom);
|
||||
|
||||
width: var(--task-width);
|
||||
|
||||
border: solid var(--task-border-width) var(--task-border-color);
|
||||
border-radius: var(--task-border-radius);
|
||||
|
||||
.username {
|
||||
font-weight: var(--username-font-weight);
|
||||
}
|
||||
|
||||
.user-tasks {
|
||||
// put list inner
|
||||
list-style-position: inside;
|
||||
|
||||
.todo {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.done {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue