diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e7b5cb5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["crossorigin"] +} diff --git a/extension/manifest.json b/extension/manifest.json index ecf172f..e11ac51 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -30,5 +30,5 @@ "name": "Twitch Notes", "permissions": ["*://*.twitch.tv/*"], "version": "0.0.1", - "web_accessible_resources": ["twitchnotes.js", "twitchnotes.css"] + "web_accessible_resources": ["twitch-notes.js", "twitch-notes.css"] } diff --git a/extension/run.js b/extension/run.js index 7e1bfd4..b446b3f 100644 --- a/extension/run.js +++ b/extension/run.js @@ -1,8 +1,4 @@ (function twitchNotes() { - if (!chrome) { - return; - } - const head = document.getElementsByTagName("head")[0]; if (!head) { return; @@ -11,16 +7,18 @@ if (!document.querySelector("script#twitchNotesScript")) { const script = document.createElement("script"); script.type = "text/javascript"; - script.src = chrome.runtime.getURL("twitchnotes.js"); + script.src = chrome.runtime.getURL("twitch-notes.js"); script.id = "twitchNotesScript"; head.appendChild(script); } if (!document.querySelector("style#twitchNotesStyle")) { - const style = document.createElement("style"); - style.src = chrome.runtime.getURL("twitchnotes.css"); + const style = document.createElement("link"); + style.setAttribute("href", chrome.runtime.getURL("twitch-notes.css")); + style.setAttribute("type", "text/css"); + style.setAttribute("rel", "stylesheet"); + style.setAttribute("crossorigin", "anonymous"); style.id = "twitchNotesStyle"; - head.appendChild(style); } })(); diff --git a/extension/twitchnotes.css b/extension/twitch-notes.css similarity index 76% rename from extension/twitchnotes.css rename to extension/twitch-notes.css index 47b58c1..7baa397 100644 --- a/extension/twitchnotes.css +++ b/extension/twitch-notes.css @@ -7,11 +7,6 @@ --background-code: #1f1e1e; } -.twitchnote img { - height: 18px; - padding: 0 4px 0; -} - .twitch-note-container { position: fixed; z-index: 1000000; @@ -88,6 +83,29 @@ line-height: 32px; } +.twitch-notes-settings-button { + z-index: var(--z-index-default) !important; + position: relative !important; + background-color: var(--color-background-button-primary-default); + color: var(--color-text-button-primary); + text-decoration: none; + cursor: pointer; + display: inline-flex; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; + vertical-align: middle; + overflow: hidden; + white-space: nowrap; + user-select: none; + font-weight: var(--font-weight-semibold); + border-radius: var(--border-radius-medium); + font-size: var(--button-text-default); + height: var(--button-size-default); + padding: 0 var(--button-padding-x); +} + .twitch-note-settings-container { position: absolute; z-index: var(--z-index-balloon); diff --git a/extension/twitch-notes.js b/extension/twitch-notes.js new file mode 100644 index 0000000..354088e --- /dev/null +++ b/extension/twitch-notes.js @@ -0,0 +1,224 @@ +(function runTwitchNotes() { + // Select the node that will be observed for mutations + + let mousePosition = { x: 0, y: 0 }; + let lastMousePosition = { x: 0, y: 0 }; + let openContainers = {}; + let isMouseDown = false; + let activeContainer = null; + let settingsWindowOpen = false; + + let LS_UserList = "twitch-note-all-users-list"; + let LS_Prefix = "twitch-note-"; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + function createSettingsButton(buttonContainer) { + const c1 = document.createElement("div"); + c1.style.marginLeft = "0.5rem !important"; + const c2 = document.createElement("div"); + c2.style.display = "inline-flex !important"; + + const settingsButton = document.createElement("button"); + settingsButton.classList.add("twitch-notes-settings-button"); + settingsButton.innerHTML = "Notes"; + settingsButton.addEventListener("click", () => { + console.log("TN Settings"); + }); + + c2.appendChild(settingsButton); + c1.appendChild(c2); + + buttonContainer.lastChild.insertBefore( + c1, + buttonContainer.lastChild.lastChild + ); + } + + function getSavedUserList() { + return JSON.parse(localStorage.getItem(LS_UserList) || "[]"); + } + + function addUserToSavedList(username) { + let userList = getSavedUserList(); + + if (!userList.includes(username)) { + userList.push(username); + localStorage.setItem(LS_UserList, JSON.stringify(userList)); + } + } + + function saveNote(username, note) { + addUserToSavedList(username); + localStorage.setItem(LS_Prefix + username, note); + } + function getNote(username) { + return localStorage.getItem(LS_Prefix + username) || ""; + } + + function handleMouseMove(event) { + event = event || window.event; + if (isMouseDown) { + if (activeContainer) { + const dx = mousePosition.x - lastMousePosition.x; + const dy = mousePosition.y - lastMousePosition.y; + openContainers[activeContainer].style.top = + parseInt(openContainers[activeContainer].style.top) + dy + "px"; + openContainers[activeContainer].style.left = + parseInt(openContainers[activeContainer].style.left) + dx + "px"; + } + } + updateMousePosition(event.clientX, event.clientY); + } + + function handleMouseUp(event) { + activeContainer = null; + isMouseDown = false; + } + + function updateMousePosition(x, y) { + lastMousePosition = { ...mousePosition }; + mousePosition.x = x; + mousePosition.y = y; + } + + function removeContainer(username) { + if (!openContainers[username]) return; + openContainers[username].remove(); + delete openContainers[username]; + } + + function addContainer(username, container) { + if (openContainers[username]) return; + openContainers[username] = container; + document.body.appendChild(container); + } + + function openTwitchNote(username) { + // CONTAINER + const container = document.createElement("div"); + container.style.left = mousePosition.x + "px"; + container.style.top = mousePosition.y + 10 + "px"; + container.classList.add("twitch-note-container"); + + // HEADER + const header = document.createElement("div"); + header.classList.add("twitch-note-header"); + header.addEventListener("mousedown", function close() { + activeContainer = username; + isMouseDown = true; + }); + const closeButton = document.createElement("span"); + closeButton.classList.add("twitch-note-close-button"); + closeButton.innerText = "X"; + closeButton.addEventListener("click", function close() { + removeContainer(username); + }); + header.appendChild(closeButton); + const title = document.createElement("span"); + title.classList.add("twitch-note-title"); + title.innerHTML = username; + header.appendChild(title); + container.appendChild(header); + + // BODY + const content = document.createElement("div"); + content.classList.add("twitch-note-content"); + content.setAttribute("contentEditable", true); + content.innerHTML = getNote(username); + + container.appendChild(content); + + const saveButton = document.createElement("div"); + saveButton.classList.add("twitch-note-save-button"); + saveButton.innerText = "SAVE"; + saveButton.addEventListener("click", function () { + saveNote(username, content.innerHTML); + removeContainer(username); + }); + + container.appendChild(saveButton); + + addContainer(username, container); + } + + let giveUpAt = Date.now() + 20000; + + let changeInProgress = false; + + let targetNode; + let buttonContainer; + + do { + targetNode = document.getElementsByClassName( + "chat-scrollable-area__message-container" + )[0]; + } while (!targetNode || giveUpAt < Date.now()); + + do { + buttonContainer = document.getElementsByClassName( + "chat-input__buttons-container" + )[0]; + } while (!buttonContainer || giveUpAt < Date.now()); + + createSettingsButton(buttonContainer); + + // Options for the observer (which mutations to observe) + const config = { attributes: true, childList: true, subtree: true }; + + // Callback function to execute when mutations are observed + const callback = (mutationList, observer) => { + if (!changeInProgress) { + for (const mutation of mutationList) { + if (mutation.type === "childList") { + changeInProgress = true; + let nodes = document.getElementsByClassName("chat-line__message"); + let users = []; + for (let node of nodes) { + const n = node.querySelector(".chat-author__display-name"); + if (!n) continue; + const usernameContainer = node.querySelector( + ".chat-line__username-container" + ); + + const username = n.attributes["data-a-user"].value; + if (username) { + let noteButton = usernameContainer.querySelector(".twitchnote"); + if (!noteButton) { + const twitchNote = document.createElement("span"); + twitchNote.classList.add("twitchnote"); + twitchNote.style.cursor = "pointer"; + twitchNote.addEventListener("click", () => { + openTwitchNote(username); + }); + + const img = document.createElement("img"); + img.src = "https://cdn.rdarius.lt/icons/32-id-card.png"; + img.style.height = "18px"; + img.style.paddingRight = "4px"; + twitchNote.appendChild(img); + + usernameContainer.insertBefore( + twitchNote, + usernameContainer.firstChild + ); + } + if (!users[username]) { + users[username] = 0; + } + users[username]++; + } + } + changeInProgress = false; + } + } + } + }; + + // Create an observer instance linked to the callback function + const observer = new MutationObserver(callback); + + // Start observing the target node for configured mutations + observer.observe(targetNode, config); +})(); diff --git a/extension/twitchnotes.js b/extension/twitchnotes.js deleted file mode 100644 index 98a6655..0000000 --- a/extension/twitchnotes.js +++ /dev/null @@ -1,211 +0,0 @@ -let TN_mousePosition = { x: 0, y: 0 }; -let TN_lastMousePosition = { x: 0, y: 0 }; -let TN_openContainers = {}; -let TN_isMouseDown = false; -let TN_activeContainer = null; -let TN_settingsWindowOpen = false; - -document.addEventListener("mousemove", handleMouseMove); -document.addEventListener("mouseup", handleMouseUp); - -function createSettingsButton(buttonContainer) { - const settingsButton = document.createElement("div"); - settingsButton.innerHTML = "Notes"; - settingsButton.addEventListener("click", () => {}); - - buttonContainer.lastChild.insertBefore( - settingsButton, - buttonContainer.lastChild.firstChild - ); -} - -function getSavedUserList() { - return JSON.parse(localStorage.getItem("twitch-note-all-users-list") || "[]"); -} - -function addUserToSavedList(username) { - let userList = getSavedUserList(); - - if (!userList.includes(username)) { - userList.push(username); - localStorage.setItem( - "twitch-note-all-users-list", - JSON.stringify(userList) - ); - } -} - -function saveNote(username, note) { - addUserToSavedList(username); - localStorage.setItem("twitch-note-" + username, note); -} -function getNote(username) { - return localStorage.getItem("twitch-note-" + username) || ""; -} - -function handleMouseMove(event) { - event = event || window.event; - if (TN_isMouseDown) { - if (TN_activeContainer) { - const dx = TN_mousePosition.x - TN_lastMousePosition.x; - const dy = TN_mousePosition.y - TN_lastMousePosition.y; - TN_openContainers[TN_activeContainer].style.top = - parseInt(TN_openContainers[TN_activeContainer].style.top) + dy + "px"; - TN_openContainers[TN_activeContainer].style.left = - parseInt(TN_openContainers[TN_activeContainer].style.left) + dx + "px"; - } - } - updateMousePosition(event.clientX, event.clientY); -} - -function handleMouseUp(event) { - TN_activeContainer = null; - TN_isMouseDown = false; -} - -function updateMousePosition(x, y) { - TN_lastMousePosition = { ...TN_mousePosition }; - TN_mousePosition.x = x; - TN_mousePosition.y = y; -} - -function removeContainer(username) { - if (!TN_openContainers[username]) return; - TN_openContainers[username].remove(); - delete TN_openContainers[username]; -} - -function addContainer(username, container) { - if (TN_openContainers[username]) return; - TN_openContainers[username] = container; - document.body.appendChild(container); -} - -function openTwitchNote(username) { - // CONTAINER - const container = document.createElement("div"); - container.style.left = TN_mousePosition.x + "px"; - container.style.top = TN_mousePosition.y + 10 + "px"; - container.classList.add("twitch-note-container"); - - // HEADER - const header = document.createElement("div"); - header.classList.add("twitch-note-header"); - header.addEventListener("mousedown", function close() { - TN_activeContainer = username; - TN_isMouseDown = true; - }); - const closeButton = document.createElement("span"); - closeButton.classList.add("twitch-note-close-button"); - closeButton.innerText = "X"; - closeButton.addEventListener("click", function close() { - removeContainer(username); - }); - header.appendChild(closeButton); - const title = document.createElement("span"); - title.classList.add("twitch-note-title"); - title.innerHTML = username; - header.appendChild(title); - container.appendChild(header); - - // BODY - const content = document.createElement("div"); - content.classList.add("twitch-note-content"); - content.setAttribute("contentEditable", true); - content.innerHTML = getNote(username); - - container.appendChild(content); - - const saveButton = document.createElement("div"); - saveButton.classList.add("twitch-note-save-button"); - saveButton.innerText = "SAVE"; - saveButton.addEventListener("click", function () { - saveNote(username, content.innerHTML); - removeContainer(username); - }); - - container.appendChild(saveButton); - - addContainer(username, container); -} - -(function runTwitchNotes() { - // Select the node that will be observed for mutations - - let giveUpAt = Date.now() + 20000; - - let changeInProgress = false; - - let targetNode; - let buttonContainer; - - do { - targetNode = document.getElementsByClassName( - "chat-scrollable-area__message-container" - )[0]; - } while (!targetNode || giveUpAt < Date.now()); - - do { - buttonContainer = document.getElementsByClassName( - "chat-input__buttons-container" - )[0]; - } while (!buttonContainer || giveUpAt < Date.now()); - - createSettingsButton(buttonContainer); - - // Options for the observer (which mutations to observe) - const config = { attributes: true, childList: true, subtree: true }; - - // Callback function to execute when mutations are observed - const callback = (mutationList, observer) => { - if (!changeInProgress) { - for (const mutation of mutationList) { - if (mutation.type === "childList") { - changeInProgress = true; - let nodes = document.getElementsByClassName("chat-line__message"); - let users = []; - for (let node of nodes) { - const n = node.querySelector(".chat-author__display-name"); - if (!n) continue; - const usernameContainer = node.querySelector( - ".chat-line__username-container" - ); - - const username = n.attributes["data-a-user"].value; - if (username) { - let noteButton = usernameContainer.querySelector(".twitchnote"); - if (!noteButton) { - const twitchNote = document.createElement("span"); - twitchNote.classList.add("twitchnote"); - twitchNote.style.cursor = "pointer"; - twitchNote.addEventListener("click", () => { - openTwitchNote(username); - }); - - const img = document.createElement("img"); - img.src = "https://cdn.rdarius.lt/icons/32-id-card.png"; - twitchNote.appendChild(img); - - usernameContainer.insertBefore( - twitchNote, - usernameContainer.firstChild - ); - } - if (!users[username]) { - users[username] = 0; - } - users[username]++; - } - } - changeInProgress = false; - } - } - } - }; - - // Create an observer instance linked to the callback function - const observer = new MutationObserver(callback); - - // Start observing the target node for configured mutations - observer.observe(targetNode, config); -})();