diff --git a/extension/manifest.json b/extension/manifest.json index ca99878..ec5e4b6 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -40,5 +40,5 @@ ] } ], - "permissions": ["webNavigation", "webRequest"] + "permissions": ["webNavigation"] } diff --git a/package.json b/package.json index f9e885f..2d36b8a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Add notes to twitch users (only visible to you)", "main": "index.js", "scripts": { - "build": "webpack" + "build": "webpack", + "zip": "cd extension && zip ../twitchNotes.zip ./*" }, "repository": { "type": "git", diff --git a/src/HTMLTemplates.ts b/src/HTMLTemplates.ts index 95367f5..3178f9b 100644 --- a/src/HTMLTemplates.ts +++ b/src/HTMLTemplates.ts @@ -1,505 +1,3 @@ -import { - CustomAElement, - CustomButtonElement, - CustomDivElement, - CustomHTMLElement, - CustomInputElement, - CustomPElement, CustomSpanElement -} from "./CustomHTMLElement"; -import NoteStorage from "./NoteStorage"; -import Mouse from "./Mouse"; -import NoteContainers from "./NoteContainers"; - -export function toggleSettingsList() { - const settingsWindow = document.querySelector('.twitch-notes-settings-container') as HTMLElement; - if (!settingsWindow) { - const settingsContainer = new CustomDivElement('twitch-notes-settings-container'); - const settingsBalloon = new CustomDivElement('twitch-notes-settings-balloon'); - const settingsPopover = new CustomDivElement('twitch-notes-settings-popover'); - const settingsHeader = new CustomDivElement('twitch-notes-settings-header'); - const settingsHeaderLeftElement = new CustomDivElement('twitch-notes-settings-header-left-element'); - const settingsHeaderCenterElement = new CustomDivElement('twitch-notes-settings-header-center-element'); - const settingsHeaderCenterElementContent = new CustomPElement('twitch-notes-settings-header-center-element-content','Twitch Notes Settings'); - const settingsHeaderRightElement = new CustomDivElement('twitch-notes-settings-header-right-element'); - const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); - const settingsScrollableArea = new CustomDivElement('twitch-notes-settings-scrollable-area'); - const settingsContent = new CustomDivElement('twitch-notes-settings-content'); - const exportNotes = createChatSettingsLine('Export Notes'); - const importNotes = createChatSettingsLine('Import Notes'); - const clearData = createChatSettingsLine('Clear Data'); - const openAllNotes = createChatSettingsLine('View All Notes'); - - settingsContainer.appendCustomChild(settingsBalloon); - settingsBalloon.appendCustomChild(settingsPopover); - settingsPopover.appendCustomChild(settingsHeader); - settingsHeader.appendCustomChild(settingsHeaderLeftElement); - settingsHeader.appendCustomChild(settingsHeaderCenterElement); - settingsHeaderCenterElement.appendCustomChild(settingsHeaderCenterElementContent); - settingsHeader.appendCustomChild(settingsHeaderRightElement); - settingsHeaderRightElement.appendCustomChild(settingsCloseButton); - settingsPopover.appendCustomChild(settingsScrollableArea); - settingsScrollableArea.appendCustomChild(settingsContent); - settingsContent.appendCustomChild(exportNotes); - settingsContent.appendCustomChild(importNotes); - settingsContent.appendCustomChild(clearData); - settingsContent.appendCustomChild(createChatSettingsSeparator()); - settingsContent.appendCustomChild(openAllNotes); - - settingsCloseButton.setClickListener(() => toggleSettingsList()); - exportNotes.setClickListener(() => { - NoteStorage.exportNotes(); - toggleSettingsList(); - }); - importNotes.setClickListener(() => { - openImportNotesWindow(); - toggleSettingsList(); - }); - clearData.setClickListener(() => { - openClearDataWindow(); - toggleSettingsList(); - }); - openAllNotes.setClickListener(() => { - openAllNoteListWindow(); - toggleSettingsList(); - }); - - document.body.appendChild(settingsContainer.getElement()); - return; - } else { - settingsWindow.remove(); - } -} - -export function openAllNoteListWindow() { - let activeNote: string = ''; - const blurredBackground = new CustomDivElement('twitch-notes-blurred-background'); - - const noteSaved = new CustomSpanElement('', 'Note saved!'); - noteSaved.setStyle({ - color: 'green', - marginLeft: '2rem', - }); - noteSaved.hide(); - - const container = new CustomDivElement('twitch-notes-center-floating-container'); - container.setStyle({ - padding: '0', - paddingTop: '1rem', - width: '100%', - maxWidth: '800px', - height: 'calc(320px + 5rem)', - maxHeight: '90vh', - }); - - const closeButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); - closeButton.setStyle({ - position: 'absolute', - top: '1rem', - right: '1rem', - }); - - const title = new CustomDivElement('twitch-note-title', 'Notes:'); - title.setStyle({ - borderBottom: '1px solid #FFFFFF19', - paddingBottom: '1rem', - }); - container.appendCustomChild(title); - - const content = new CustomDivElement(); - content.setStyle({ - display: 'flex', - flexDirection: 'row', - flexWrap: 'no-wrap', - width: '100%', - height: '320px' - }); - container.appendCustomChild(content); - closeButton.setClickListener(() => { - blurredBackground.remove(); - }); - container.appendCustomChild(closeButton); - - const userList = new CustomDivElement(); - userList.setStyle({ - borderRight: '1px solid #FFFFFF19', - height: '320px', - minWidth: '240px', - overflowY: 'auto', - overflowX: 'hidden', - }); - - const noteContainer = new CustomDivElement(); - noteContainer.setStyle({ - height: '320px', - padding: '1rem', - width: '100%', - opacity: '0', - }); - - const note = new CustomDivElement(); - note.setStyle({ - marginBottom: '3rem', - border: '1px solid #FFFFFF19', - height: 'calc(320px - 7rem)', - width: '100%', - padding: '6px', - outline: 'none', - overflowY: 'auto', - overflowX: 'hidden', - }); - note.addAttribute('contentEditable', 'true'); - - const saveNote = new CustomButtonElement('twitch-notes-settings-button','Save Note'); - saveNote.setStyle({ - position: 'absolute', - bottom: '1rem', - left: '1rem' - }); - - saveNote.setClickListener(() => { - if(!activeNote) { - return; - } - NoteStorage.saveNote(activeNote, note.getElement().innerHTML); - noteSaved.setStyle({ - display: 'inline-block', - }); - }); - - for(let user of NoteStorage.getSavedUserList()) { - const userLine = new CustomDivElement('twitch-notes-user-list-user', user); - userLine.setClickListener(() => { - const list = document.getElementsByClassName('twitch-notes-user-list-user'); - for(let i = 0; i < list.length; i++) { - list.item(i)?.removeAttribute('data-active'); - } - noteSaved.hide(); - userLine.addAttribute('data-active', 'true'); - noteContainer.setStyle({opacity: '1'}); - note.getElement().innerHTML = NoteStorage.getNote(user); - activeNote = user; - }); - userList.appendCustomChild(userLine); - } - - noteContainer.appendCustomChild(note); - noteContainer.appendCustomChild(saveNote); - noteContainer.appendCustomChild(noteSaved); - - content.appendCustomChild(userList); - content.appendCustomChild(noteContainer); - - blurredBackground.appendCustomChild(container); - document.body.appendChild(blurredBackground.getElement()); -} - -export function openClearDataWindow() { - const blurredBackground = new CustomDivElement( 'twitch-notes-blurred-background'); - const container = new CustomDivElement('twitch-notes-center-floating-container', 'This action will delete all saved notes!

Consider exporting notes before performing this action in case you will need to use notes again'); - const spacer = new CustomDivElement(); - spacer.setStyle({ - height: '24px' - }); - const deleteAction = new CustomButtonElement('twitch-notes-settings-button', 'DELETE ALL NOTES'); - deleteAction.setStyle({ - background: 'red' - }); - deleteAction.setClickListener(() => { - NoteStorage.clearData(); - blurredBackground.remove(); - }); - const cancelAction = new CustomButtonElement('twitch-notes-settings-button', 'Cancel'); - cancelAction.setClickListener(() => { - blurredBackground.remove(); - }); - container.appendCustomChild(spacer); - container.appendCustomChild(deleteAction); - container.appendCustomChild(cancelAction); - blurredBackground.appendCustomChild(container); - document.body.appendChild(blurredBackground.getElement()); -} - -export function openImportNotesWindow() { - const blurredBackground = new CustomDivElement('twitch-notes-blurred-background'); - const container = new CustomDivElement('twitch-notes-center-floating-container', 'WARNING! This action might override existing data!

'); - const inputBlock = new CustomDivElement('','Select exported twitch notes file

'); - const input = new CustomInputElement('twitch-notes-file-input'); - input.addAttribute('type', 'file').addAttribute('accept', 'json'); - const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); - settingsCloseButton.setStyle({ - position: 'absolute', - top: '1rem', - right: '1rem', - }); - - inputBlock.appendCustomChild(input); - - settingsCloseButton.setClickListener(() => { - blurredBackground.remove(); - }) - - input.setChangeListener(() => { - if (!input?.getElement()?.files) return; - // @ts-ignore - let file = input.getElement().files.item(0); - if (!file) { - return; - } - let reader = new FileReader(); - reader.readAsText(file, "UTF-8"); - reader.onload = function (evt) { - if (!evt?.target?.result) return; - - NoteStorage.inputData = JSON.parse(evt.target.result.toString()); - if (NoteStorage.inputData) { - inputBlock.setStyle({display: 'none'}); - importNotesResult(container, blurredBackground); - } - } - reader.onerror = function () { - console.error("error reading file"); - } - }); - - container.appendCustomChild(settingsCloseButton); - container.appendCustomChild(inputBlock); - blurredBackground.appendCustomChild(container); - document.body.appendChild(blurredBackground.getElement()); -} - -export function importNotesResult(container: CustomHTMLElement, blurredBackground: CustomHTMLElement) { - const willBeAdded = []; - let willBeOverwritten = []; - const noChanges = []; - - for (let user of NoteStorage.inputData.users) { - if (NoteStorage.getNote(user)) { - if (NoteStorage.inputData.notes.find(note => note.user === user)?.resolved || NoteStorage.getNote(user) !== NoteStorage.inputData.notes.find(note => note.user === user)?.note) { - willBeOverwritten.push(user); - } else { - noChanges.push(user); - } - } else { - willBeAdded.push(user); - } - } - - const resultsContainer = new CustomDivElement(); - - if (willBeOverwritten.length) { - const overwrittenContainer = new CustomDivElement('', 'Note conflicts found for:
'); - overwrittenContainer.setStyle({marginTop: '1rem'}); - - for (let item of willBeOverwritten) { - const row = new CustomDivElement('', item + ' '); - const isResolved = NoteStorage.inputData.notes.find(note => note.user === item)?.resolved; - row.setStyle({ - color: isResolved ? 'green' : 'red', - }); - const resolve = new CustomAElement('', isResolved ? '[update]' : '[resolve]'); - resolve.setStyle({ - cursor: 'pointer' - }); - - resolve.setClickListener(() => { - importNotesConflictResolve(item, resultsContainer, container, blurredBackground); - }) - row.appendCustomChild(resolve); - overwrittenContainer.appendCustomChild(row); - } - resultsContainer.appendCustomChild(overwrittenContainer); - } - - if (willBeAdded.length) { - const addedContainer = new CustomDivElement('', 'Note will be added for:
' + willBeAdded.join('
')); - addedContainer.setStyle({ - marginTop: '1rem', - color: 'white' - }); - resultsContainer.appendCustomChild(addedContainer); - } - - if (noChanges.length) { - const noChangesContainer = new CustomDivElement('', 'No changes for:
' + noChanges.join('
')); - noChangesContainer.setStyle({ - marginTop: '1rem', - color: 'gray' - }); - resultsContainer.appendCustomChild(noChangesContainer); - } - - const spacer = new CustomDivElement(); - spacer.setStyle({height: '24px'}); - resultsContainer.appendCustomChild(spacer); - - willBeOverwritten = willBeOverwritten.filter(x => !NoteStorage.inputData.notes.find(n => n.user === x)?.resolved); - - const saveButton = new CustomButtonElement('twitch-notes-settings-button', 'Complete import'); - saveButton.setStyle({}) - if (willBeOverwritten.length > 0) saveButton.setStyle({background: 'gray'}); - if (willBeOverwritten.length > 0) saveButton.addAttribute('disabled', 'true'); - - resultsContainer.appendCustomChild(saveButton) - - if (willBeOverwritten.length > 0) { - const note = new CustomDivElement('', 'Import cannot be completed while there are unresolved conflicts'); - note.setStyle({ - color: 'gray', - }); - resultsContainer.appendCustomChild(note); - } else { - saveButton.setClickListener(() => { - for (let note of NoteStorage.inputData.notes) { - NoteStorage.saveNote(note.user, note.note); - } - blurredBackground.remove(); - NoteStorage.inputData = { - users: [], - notes: [], - settings: {}, - }; - }); - } - - container.appendCustomChild(resultsContainer); -} - -export function importNotesConflictResolve( - user: string, - resultsContainer: CustomHTMLElement, - container: CustomHTMLElement, - blurredBackground: CustomHTMLElement -) { - const diffBlurredBackground = new CustomDivElement('twitch-notes-blurred-background'); - const diffContainer = new CustomDivElement('twitch-notes-center-floating-container', 'Select which note to keep for ' + user + '
You can modify notes to merge them and keep updated one

'); - - const inputContainer = new CustomDivElement(); - inputContainer.setStyle({ - display: 'flex', - flexDirection: 'row', - flexWrap: 'no-wrap', - width: '100%', - }); - - const valueStyle = { - minWidth: '320px', - padding: '6px', - border: '1px solid #FFFFFF19', - outline: 'none', - }; - const valueAttr: [string, string] = ['contentEditable', 'true']; - - const localValueContainer = new CustomDivElement(); - const pLocal = new CustomPElement('', 'Locally saved note'); - localValueContainer.appendCustomChild(pLocal); - - const localValue = new CustomDivElement(); - localValue.setStyle(valueStyle); - localValue.addAttribute(...valueAttr); - localValue.setId('local-value-container'); - const localValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this'); - localValueSave.setClickListener(() => { - const val = document.getElementById('local-value-container')?.innerHTML || ''; - NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? { - user: n.user, - note: val, - resolved: true - } : n); - diffBlurredBackground.remove(); - resultsContainer.remove(); - importNotesResult(container, blurredBackground); - }); - localValueContainer.appendCustomChild(localValue); - localValueContainer.appendBody('
'); - localValueContainer.appendCustomChild(localValueSave); - - const importedValueContainer = new CustomDivElement(); - const iLocal = new CustomPElement('', 'Imported note'); - importedValueContainer.appendCustomChild(iLocal); - const importedValue = new CustomDivElement('', NoteStorage.inputData.notes.filter(n => n.user === user)[0].note || ''); - importedValue.setId('import-value-container'); - importedValue.setStyle(valueStyle); - importedValue.addAttribute(...valueAttr); - const importedValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this'); - importedValueSave.setClickListener(() => { - const val = document.getElementById('import-value-container')?.innerHTML || ''; - NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? { - user: n.user, - note: val, - resolved: true - } : n); - diffBlurredBackground.remove(); - resultsContainer.remove(); - importNotesResult(container, blurredBackground); - }); - - importedValueContainer.appendCustomChild(importedValue); - importedValueContainer.appendBody('
'); - importedValueContainer.appendCustomChild(importedValueSave); - - - inputContainer.appendCustomChild(localValueContainer); - inputContainer.appendCustomChild(importedValueContainer); - - diffContainer.appendCustomChild(inputContainer); - diffBlurredBackground.appendCustomChild(diffContainer); - document.body.appendChild(diffBlurredBackground.getElement()); -} - -export function createChatSettingsLine(text: string) { - const element = new CustomDivElement('twitch-notes-settings-option-line'); - const button = new CustomButtonElement('twitch-notes-settings-option-line-button'); - const buttonContainer = new CustomDivElement('twitch-notes-settings-option-line-button-container'); - const buttonContainerContent = new CustomDivElement('twitch-notes-settings-option-line-button-container-content', text); - - buttonContainer.appendCustomChild(buttonContainerContent); - button.appendCustomChild(buttonContainer); - element.appendCustomChild(button); - return element; -} - -export function createChatSettingsSeparator() { - return new CustomDivElement('twitch-note-settings-separator'); -} - -export function openTwitchNote(username: string) { - if (NoteContainers.containers[username]) return; - const container = new CustomDivElement('twitch-note-container'); - container.setStyle({ - left: Mouse.mousePosition.x + 'px', - top: Mouse.mousePosition.y + 10 + 'px' - }); - - const header = new CustomDivElement('twitch-note-header'); - header.setMouseDownListener(() => { - NoteContainers.activeContainer = username; - Mouse.isMouseDown = true; - }); - - const closeButton = new CustomSpanElement('twitch-note-close-button', getCloseButtonSVG()); - closeButton.setClickListener(() => { - NoteContainers.removeContainer(username); - }); - - const title = new CustomSpanElement('twitch-note-title', username); - const content = new CustomDivElement('twitch-note-content', NoteStorage.getNote(username)); - content.addAttribute('contentEditable', 'true'); - - const saveButton = new CustomDivElement('twitch-note-save-button', 'SAVE'); - saveButton.setClickListener(() => { - NoteStorage.saveNote(username, content.getElement().innerHTML); - NoteContainers.removeContainer(username); - }); - - header.appendCustomChild(closeButton); - header.appendCustomChild(title); - container.appendCustomChild(header); - container.appendCustomChild(content); - container.appendCustomChild(saveButton); - document.body.appendChild(container.getElement()); - NoteContainers.addContainer(username, container); -} - export function getCloseButtonSVG() { return ` diff --git a/src/TwitchNote.ts b/src/TwitchNote.ts index 8c3bd22..dbdeaf3 100644 --- a/src/TwitchNote.ts +++ b/src/TwitchNote.ts @@ -5,7 +5,8 @@ import { CustomSpanElement } from "./CustomHTMLElement"; import {ELEMENT_ButtonContainer} from "./const"; -import {openTwitchNote, toggleSettingsList} from "./HTMLTemplates"; +import toggleSettingsList from "./toggleSettingsList"; +import openTwitchNote from "./openTwitchNote"; export default class TwitchNote { diff --git a/src/chatSettingsLine.ts b/src/chatSettingsLine.ts new file mode 100644 index 0000000..f2d1cc2 --- /dev/null +++ b/src/chatSettingsLine.ts @@ -0,0 +1,13 @@ +import {CustomButtonElement, CustomDivElement} from "./CustomHTMLElement"; + +export default function createChatSettingsLine(text: string) { + const element = new CustomDivElement('twitch-notes-settings-option-line'); + const button = new CustomButtonElement('twitch-notes-settings-option-line-button'); + const buttonContainer = new CustomDivElement('twitch-notes-settings-option-line-button-container'); + const buttonContainerContent = new CustomDivElement('twitch-notes-settings-option-line-button-container-content', text); + + buttonContainer.appendCustomChild(buttonContainerContent); + button.appendCustomChild(buttonContainer); + element.appendCustomChild(button); + return element; +} \ No newline at end of file diff --git a/src/chatSettingsSeparator.ts b/src/chatSettingsSeparator.ts new file mode 100644 index 0000000..b3c23a3 --- /dev/null +++ b/src/chatSettingsSeparator.ts @@ -0,0 +1,5 @@ +import {CustomDivElement} from "./CustomHTMLElement"; + +export default function createChatSettingsSeparator() { + return new CustomDivElement('twitch-note-settings-separator'); +} \ No newline at end of file diff --git a/src/importNotesConflictResolve.ts b/src/importNotesConflictResolve.ts new file mode 100644 index 0000000..d3336ab --- /dev/null +++ b/src/importNotesConflictResolve.ts @@ -0,0 +1,85 @@ +import {CustomButtonElement, CustomDivElement, CustomHTMLElement, CustomPElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; +import importNotesResult from "./importNotesResult"; + +export default function importNotesConflictResolve( + user: string, + resultsContainer: CustomHTMLElement, + container: CustomHTMLElement, + blurredBackground: CustomHTMLElement +) { + const diffBlurredBackground = new CustomDivElement('twitch-notes-blurred-background'); + const diffContainer = new CustomDivElement('twitch-notes-center-floating-container', 'Select which note to keep for ' + user + '
You can modify notes to merge them and keep updated one

'); + + const inputContainer = new CustomDivElement(); + inputContainer.setStyle({ + display: 'flex', + flexDirection: 'row', + flexWrap: 'no-wrap', + width: '100%', + }); + + const valueStyle = { + minWidth: '320px', + padding: '6px', + border: '1px solid #FFFFFF19', + outline: 'none', + }; + const valueAttr: [string, string] = ['contentEditable', 'true']; + + const localValueContainer = new CustomDivElement(); + const pLocal = new CustomPElement('', 'Locally saved note'); + localValueContainer.appendCustomChild(pLocal); + + const localValue = new CustomDivElement(); + localValue.setStyle(valueStyle); + localValue.addAttribute(...valueAttr); + localValue.setId('local-value-container'); + const localValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this'); + localValueSave.setClickListener(() => { + const val = document.getElementById('local-value-container')?.innerHTML || ''; + NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? { + user: n.user, + note: val, + resolved: true + } : n); + diffBlurredBackground.remove(); + resultsContainer.remove(); + importNotesResult(container, blurredBackground); + }); + localValueContainer.appendCustomChild(localValue); + localValueContainer.appendBody('
'); + localValueContainer.appendCustomChild(localValueSave); + + const importedValueContainer = new CustomDivElement(); + const iLocal = new CustomPElement('', 'Imported note'); + importedValueContainer.appendCustomChild(iLocal); + const importedValue = new CustomDivElement('', NoteStorage.inputData.notes.filter(n => n.user === user)[0].note || ''); + importedValue.setId('import-value-container'); + importedValue.setStyle(valueStyle); + importedValue.addAttribute(...valueAttr); + const importedValueSave = new CustomButtonElement('twitch-notes-settings-button', 'Save this'); + importedValueSave.setClickListener(() => { + const val = document.getElementById('import-value-container')?.innerHTML || ''; + NoteStorage.inputData.notes = NoteStorage.inputData.notes.map(n => n.user === user ? { + user: n.user, + note: val, + resolved: true + } : n); + diffBlurredBackground.remove(); + resultsContainer.remove(); + importNotesResult(container, blurredBackground); + }); + + importedValueContainer.appendCustomChild(importedValue); + importedValueContainer.appendBody('
'); + importedValueContainer.appendCustomChild(importedValueSave); + + + inputContainer.appendCustomChild(localValueContainer); + inputContainer.appendCustomChild(importedValueContainer); + + diffContainer.appendCustomChild(inputContainer); + diffBlurredBackground.appendCustomChild(diffContainer); + document.body.appendChild(diffBlurredBackground.getElement()); +} \ No newline at end of file diff --git a/src/importNotesResult.ts b/src/importNotesResult.ts new file mode 100644 index 0000000..470c8b8 --- /dev/null +++ b/src/importNotesResult.ts @@ -0,0 +1,100 @@ +import {CustomAElement, CustomButtonElement, CustomDivElement, CustomHTMLElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; +import importNotesConflictResolve from "./importNotesConflictResolve"; + +export default function importNotesResult(container: CustomHTMLElement, blurredBackground: CustomHTMLElement) { + const willBeAdded = []; + let willBeOverwritten = []; + const noChanges = []; + + for (let user of NoteStorage.inputData.users) { + if (NoteStorage.getNote(user)) { + if (NoteStorage.inputData.notes.find(note => note.user === user)?.resolved || NoteStorage.getNote(user) !== NoteStorage.inputData.notes.find(note => note.user === user)?.note) { + willBeOverwritten.push(user); + } else { + noChanges.push(user); + } + } else { + willBeAdded.push(user); + } + } + + const resultsContainer = new CustomDivElement(); + + if (willBeOverwritten.length) { + const overwrittenContainer = new CustomDivElement('', 'Note conflicts found for:
'); + overwrittenContainer.setStyle({marginTop: '1rem'}); + + for (let item of willBeOverwritten) { + const row = new CustomDivElement('', item + ' '); + const isResolved = NoteStorage.inputData.notes.find(note => note.user === item)?.resolved; + row.setStyle({ + color: isResolved ? 'green' : 'red', + }); + const resolve = new CustomAElement('', isResolved ? '[update]' : '[resolve]'); + resolve.setStyle({ + cursor: 'pointer' + }); + + resolve.setClickListener(() => { + importNotesConflictResolve(item, resultsContainer, container, blurredBackground); + }) + row.appendCustomChild(resolve); + overwrittenContainer.appendCustomChild(row); + } + resultsContainer.appendCustomChild(overwrittenContainer); + } + + if (willBeAdded.length) { + const addedContainer = new CustomDivElement('', 'Note will be added for:
' + willBeAdded.join('
')); + addedContainer.setStyle({ + marginTop: '1rem', + color: 'white' + }); + resultsContainer.appendCustomChild(addedContainer); + } + + if (noChanges.length) { + const noChangesContainer = new CustomDivElement('', 'No changes for:
' + noChanges.join('
')); + noChangesContainer.setStyle({ + marginTop: '1rem', + color: 'gray' + }); + resultsContainer.appendCustomChild(noChangesContainer); + } + + const spacer = new CustomDivElement(); + spacer.setStyle({height: '24px'}); + resultsContainer.appendCustomChild(spacer); + + willBeOverwritten = willBeOverwritten.filter(x => !NoteStorage.inputData.notes.find(n => n.user === x)?.resolved); + + const saveButton = new CustomButtonElement('twitch-notes-settings-button', 'Complete import'); + saveButton.setStyle({}) + if (willBeOverwritten.length > 0) saveButton.setStyle({background: 'gray'}); + if (willBeOverwritten.length > 0) saveButton.addAttribute('disabled', 'true'); + + resultsContainer.appendCustomChild(saveButton) + + if (willBeOverwritten.length > 0) { + const note = new CustomDivElement('', 'Import cannot be completed while there are unresolved conflicts'); + note.setStyle({ + color: 'gray', + }); + resultsContainer.appendCustomChild(note); + } else { + saveButton.setClickListener(() => { + for (let note of NoteStorage.inputData.notes) { + NoteStorage.saveNote(note.user, note.note); + } + blurredBackground.remove(); + NoteStorage.inputData = { + users: [], + notes: [], + settings: {}, + }; + }); + } + + container.appendCustomChild(resultsContainer); +} \ No newline at end of file diff --git a/src/openAllNoteListWindow.ts b/src/openAllNoteListWindow.ts new file mode 100644 index 0000000..2e78613 --- /dev/null +++ b/src/openAllNoteListWindow.ts @@ -0,0 +1,126 @@ +import {CustomButtonElement, CustomDivElement, CustomSpanElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; +import {getCloseButtonSVG} from "./HTMLTemplates"; + +export default function openAllNoteListWindow() { + let activeNote: string = ''; + const blurredBackground = new CustomDivElement('twitch-notes-blurred-background'); + + const noteSaved = new CustomSpanElement('', 'Note saved!'); + noteSaved.setStyle({ + color: 'green', + marginLeft: '2rem', + }); + noteSaved.hide(); + + const container = new CustomDivElement('twitch-notes-center-floating-container'); + container.setStyle({ + padding: '0', + paddingTop: '1rem', + width: '100%', + maxWidth: '800px', + height: 'calc(320px + 5rem)', + maxHeight: '90vh', + }); + + const closeButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); + closeButton.setStyle({ + position: 'absolute', + top: '1rem', + right: '1rem', + }); + + const title = new CustomDivElement('twitch-note-title', 'Notes:'); + title.setStyle({ + borderBottom: '1px solid #FFFFFF19', + paddingBottom: '1rem', + }); + container.appendCustomChild(title); + + const content = new CustomDivElement(); + content.setStyle({ + display: 'flex', + flexDirection: 'row', + flexWrap: 'no-wrap', + width: '100%', + height: '320px' + }); + container.appendCustomChild(content); + closeButton.setClickListener(() => { + blurredBackground.remove(); + }); + container.appendCustomChild(closeButton); + + const userList = new CustomDivElement(); + userList.setStyle({ + borderRight: '1px solid #FFFFFF19', + height: '320px', + minWidth: '240px', + overflowY: 'auto', + overflowX: 'hidden', + }); + + const noteContainer = new CustomDivElement(); + noteContainer.setStyle({ + height: '320px', + padding: '1rem', + width: '100%', + opacity: '0', + }); + + const note = new CustomDivElement(); + note.setStyle({ + marginBottom: '3rem', + border: '1px solid #FFFFFF19', + height: 'calc(320px - 7rem)', + width: '100%', + padding: '6px', + outline: 'none', + overflowY: 'auto', + overflowX: 'hidden', + }); + note.addAttribute('contentEditable', 'true'); + + const saveNote = new CustomButtonElement('twitch-notes-settings-button','Save Note'); + saveNote.setStyle({ + position: 'absolute', + bottom: '1rem', + left: '1rem' + }); + + saveNote.setClickListener(() => { + if(!activeNote) { + return; + } + NoteStorage.saveNote(activeNote, note.getElement().innerHTML); + noteSaved.setStyle({ + display: 'inline-block', + }); + }); + + for(let user of NoteStorage.getSavedUserList()) { + const userLine = new CustomDivElement('twitch-notes-user-list-user', user); + userLine.setClickListener(() => { + const list = document.getElementsByClassName('twitch-notes-user-list-user'); + for(let i = 0; i < list.length; i++) { + list.item(i)?.removeAttribute('data-active'); + } + noteSaved.hide(); + userLine.addAttribute('data-active', 'true'); + noteContainer.setStyle({opacity: '1'}); + note.getElement().innerHTML = NoteStorage.getNote(user); + activeNote = user; + }); + userList.appendCustomChild(userLine); + } + + noteContainer.appendCustomChild(note); + noteContainer.appendCustomChild(saveNote); + noteContainer.appendCustomChild(noteSaved); + + content.appendCustomChild(userList); + content.appendCustomChild(noteContainer); + + blurredBackground.appendCustomChild(container); + document.body.appendChild(blurredBackground.getElement()); +} \ No newline at end of file diff --git a/src/openClearDataWindow.ts b/src/openClearDataWindow.ts new file mode 100644 index 0000000..9dfb7a9 --- /dev/null +++ b/src/openClearDataWindow.ts @@ -0,0 +1,28 @@ +import {CustomButtonElement, CustomDivElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; + +export default function openClearDataWindow() { + const blurredBackground = new CustomDivElement( 'twitch-notes-blurred-background'); + const container = new CustomDivElement('twitch-notes-center-floating-container', 'This action will delete all saved notes!

Consider exporting notes before performing this action in case you will need to use notes again'); + const spacer = new CustomDivElement(); + spacer.setStyle({ + height: '24px' + }); + const deleteAction = new CustomButtonElement('twitch-notes-settings-button', 'DELETE ALL NOTES'); + deleteAction.setStyle({ + background: 'red' + }); + deleteAction.setClickListener(() => { + NoteStorage.clearData(); + blurredBackground.remove(); + }); + const cancelAction = new CustomButtonElement('twitch-notes-settings-button', 'Cancel'); + cancelAction.setClickListener(() => { + blurredBackground.remove(); + }); + container.appendCustomChild(spacer); + container.appendCustomChild(deleteAction); + container.appendCustomChild(cancelAction); + blurredBackground.appendCustomChild(container); + document.body.appendChild(blurredBackground.getElement()); +} \ No newline at end of file diff --git a/src/openImprtNotesWindow.ts b/src/openImprtNotesWindow.ts new file mode 100644 index 0000000..f59a1c5 --- /dev/null +++ b/src/openImprtNotesWindow.ts @@ -0,0 +1,52 @@ +import {CustomButtonElement, CustomDivElement, CustomInputElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; +import {getCloseButtonSVG} from "./HTMLTemplates"; +import importNotesResult from "./importNotesResult"; + +export default function openImportNotesWindow() { + const blurredBackground = new CustomDivElement('twitch-notes-blurred-background'); + const container = new CustomDivElement('twitch-notes-center-floating-container', 'WARNING! This action might override existing data!

'); + const inputBlock = new CustomDivElement('','Select exported twitch notes file

'); + const input = new CustomInputElement('twitch-notes-file-input'); + input.addAttribute('type', 'file').addAttribute('accept', 'json'); + const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); + settingsCloseButton.setStyle({ + position: 'absolute', + top: '1rem', + right: '1rem', + }); + + inputBlock.appendCustomChild(input); + + settingsCloseButton.setClickListener(() => { + blurredBackground.remove(); + }) + + input.setChangeListener(() => { + if (!input?.getElement()?.files) return; + // @ts-ignore + let file = input.getElement().files.item(0); + if (!file) { + return; + } + let reader = new FileReader(); + reader.readAsText(file, "UTF-8"); + reader.onload = function (evt) { + if (!evt?.target?.result) return; + + NoteStorage.inputData = JSON.parse(evt.target.result.toString()); + if (NoteStorage.inputData) { + inputBlock.setStyle({display: 'none'}); + importNotesResult(container, blurredBackground); + } + } + reader.onerror = function () { + console.error("error reading file"); + } + }); + + container.appendCustomChild(settingsCloseButton); + container.appendCustomChild(inputBlock); + blurredBackground.appendCustomChild(container); + document.body.appendChild(blurredBackground.getElement()); +} \ No newline at end of file diff --git a/src/openTwitchNote.ts b/src/openTwitchNote.ts new file mode 100644 index 0000000..7690c1e --- /dev/null +++ b/src/openTwitchNote.ts @@ -0,0 +1,43 @@ +import NoteContainers from "./NoteContainers"; +import {CustomDivElement, CustomSpanElement} from "./CustomHTMLElement"; +import Mouse from "./Mouse"; +import NoteStorage from "./NoteStorage"; +import {getCloseButtonSVG} from "./HTMLTemplates"; + +export default function openTwitchNote(username: string) { + if (NoteContainers.containers[username]) return; + const container = new CustomDivElement('twitch-note-container'); + container.setStyle({ + left: Mouse.mousePosition.x + 'px', + top: Mouse.mousePosition.y + 10 + 'px' + }); + + const header = new CustomDivElement('twitch-note-header'); + header.setMouseDownListener(() => { + NoteContainers.activeContainer = username; + Mouse.isMouseDown = true; + }); + + const closeButton = new CustomSpanElement('twitch-note-close-button', getCloseButtonSVG()); + closeButton.setClickListener(() => { + NoteContainers.removeContainer(username); + }); + + const title = new CustomSpanElement('twitch-note-title', username); + const content = new CustomDivElement('twitch-note-content', NoteStorage.getNote(username)); + content.addAttribute('contentEditable', 'true'); + + const saveButton = new CustomDivElement('twitch-note-save-button', 'SAVE'); + saveButton.setClickListener(() => { + NoteStorage.saveNote(username, content.getElement().innerHTML); + NoteContainers.removeContainer(username); + }); + + header.appendCustomChild(closeButton); + header.appendCustomChild(title); + container.appendCustomChild(header); + container.appendCustomChild(content); + container.appendCustomChild(saveButton); + document.body.appendChild(container.getElement()); + NoteContainers.addContainer(username, container); +} \ No newline at end of file diff --git a/src/toggleSettingsList.ts b/src/toggleSettingsList.ts new file mode 100644 index 0000000..7cea69d --- /dev/null +++ b/src/toggleSettingsList.ts @@ -0,0 +1,70 @@ +import {CustomButtonElement, CustomDivElement, CustomPElement} from "./CustomHTMLElement"; +import NoteStorage from "./NoteStorage"; +import { + getCloseButtonSVG +} from "./HTMLTemplates"; +import openAllNoteListWindow from "./openAllNoteListWindow"; +import openClearDataWindow from "./openClearDataWindow"; +import openImportNotesWindow from "./openImprtNotesWindow"; +import createChatSettingsLine from "./chatSettingsLine"; +import createChatSettingsSeparator from "./chatSettingsSeparator"; + +export default function toggleSettingsList() { + const settingsWindow = document.querySelector('.twitch-notes-settings-container') as HTMLElement; + if (!settingsWindow) { + const settingsContainer = new CustomDivElement('twitch-notes-settings-container'); + const settingsBalloon = new CustomDivElement('twitch-notes-settings-balloon'); + const settingsPopover = new CustomDivElement('twitch-notes-settings-popover'); + const settingsHeader = new CustomDivElement('twitch-notes-settings-header'); + const settingsHeaderLeftElement = new CustomDivElement('twitch-notes-settings-header-left-element'); + const settingsHeaderCenterElement = new CustomDivElement('twitch-notes-settings-header-center-element'); + const settingsHeaderCenterElementContent = new CustomPElement('twitch-notes-settings-header-center-element-content','Twitch Notes Settings'); + const settingsHeaderRightElement = new CustomDivElement('twitch-notes-settings-header-right-element'); + const settingsCloseButton = new CustomButtonElement('twitch-notes-settings-close-button', getCloseButtonSVG()); + const settingsScrollableArea = new CustomDivElement('twitch-notes-settings-scrollable-area'); + const settingsContent = new CustomDivElement('twitch-notes-settings-content'); + const exportNotes = createChatSettingsLine('Export Notes'); + const importNotes = createChatSettingsLine('Import Notes'); + const clearData = createChatSettingsLine('Clear Data'); + const openAllNotes = createChatSettingsLine('View All Notes'); + + settingsContainer.appendCustomChild(settingsBalloon); + settingsBalloon.appendCustomChild(settingsPopover); + settingsPopover.appendCustomChild(settingsHeader); + settingsHeader.appendCustomChild(settingsHeaderLeftElement); + settingsHeader.appendCustomChild(settingsHeaderCenterElement); + settingsHeaderCenterElement.appendCustomChild(settingsHeaderCenterElementContent); + settingsHeader.appendCustomChild(settingsHeaderRightElement); + settingsHeaderRightElement.appendCustomChild(settingsCloseButton); + settingsPopover.appendCustomChild(settingsScrollableArea); + settingsScrollableArea.appendCustomChild(settingsContent); + settingsContent.appendCustomChild(exportNotes); + settingsContent.appendCustomChild(importNotes); + settingsContent.appendCustomChild(clearData); + settingsContent.appendCustomChild(createChatSettingsSeparator()); + settingsContent.appendCustomChild(openAllNotes); + + settingsCloseButton.setClickListener(() => toggleSettingsList()); + exportNotes.setClickListener(() => { + NoteStorage.exportNotes(); + toggleSettingsList(); + }); + importNotes.setClickListener(() => { + openImportNotesWindow(); + toggleSettingsList(); + }); + clearData.setClickListener(() => { + openClearDataWindow(); + toggleSettingsList(); + }); + openAllNotes.setClickListener(() => { + openAllNoteListWindow(); + toggleSettingsList(); + }); + + document.body.appendChild(settingsContainer.getElement()); + return; + } else { + settingsWindow.remove(); + } +} \ No newline at end of file