Compare commits
10 Commits
7e667fd9e4
...
f2972cfa06
| Author | SHA1 | Date | |
|---|---|---|---|
| f2972cfa06 | |||
| 26bcc0cbfb | |||
| 0f3b3d527d | |||
| 12fc946c9b | |||
| b33bf7fece | |||
| 94978f8f54 | |||
| 128e10db07 | |||
| 64bbc81460 | |||
| d13e318fe6 | |||
| 36d249f92b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
twitchNotes*.zip
|
||||||
@@ -1,691 +0,0 @@
|
|||||||
(function runTwitchNotes() {
|
|
||||||
function save(filename, data) {
|
|
||||||
const blob = new Blob([data], {type: 'text/json'});
|
|
||||||
const elem = window.document.createElement('a');
|
|
||||||
elem.href = window.URL.createObjectURL(blob);
|
|
||||||
elem.download = filename;
|
|
||||||
document.body.appendChild(elem);
|
|
||||||
elem.click();
|
|
||||||
document.body.removeChild(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Notes = {
|
|
||||||
inputData: {},
|
|
||||||
getSavedUserList: () => {
|
|
||||||
return JSON.parse(localStorage.getItem(LS_UserList) || "[]");
|
|
||||||
},
|
|
||||||
addUserToSavedList: (username) => {
|
|
||||||
let userList = Notes.getSavedUserList();
|
|
||||||
|
|
||||||
if (!userList.includes(username)) {
|
|
||||||
userList.push(username);
|
|
||||||
localStorage.setItem(LS_UserList, JSON.stringify(userList));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeUserFromSavedList: (username) => {
|
|
||||||
let userList = Notes.getSavedUserList();
|
|
||||||
userList = userList.filter(u => u !== username);
|
|
||||||
if (userList.length) {
|
|
||||||
localStorage.setItem(LS_UserList, JSON.stringify(userList));
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(LS_UserList);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveNote: (username, note) => {
|
|
||||||
Notes.addUserToSavedList(username);
|
|
||||||
localStorage.setItem(LS_Prefix + username, note);
|
|
||||||
},
|
|
||||||
deleteNote: (username) => {
|
|
||||||
Notes.removeUserFromSavedList(username);
|
|
||||||
localStorage.removeItem(LS_Prefix + username);
|
|
||||||
},
|
|
||||||
getNote: (username) => {
|
|
||||||
return localStorage.getItem(LS_Prefix + username) || "";
|
|
||||||
},
|
|
||||||
exportNotes: () => {
|
|
||||||
save('twitch-notes-' + (Date.now()) + '.json', JSON.stringify({
|
|
||||||
users: Notes.getSavedUserList(),
|
|
||||||
notes: Notes.getSavedUserList().map(user => {
|
|
||||||
return {user: user, note: Notes.getNote(user)};
|
|
||||||
}),
|
|
||||||
settings: {},
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
showAllNotes: () => {
|
|
||||||
let activeNote = null;
|
|
||||||
const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
|
|
||||||
const container = createElement('div', null, 'twitch-notes-center-floating-container', {
|
|
||||||
padding: '0',
|
|
||||||
paddingTop: '1rem',
|
|
||||||
width: '100%',
|
|
||||||
height: 'calc(320px + 5rem)'
|
|
||||||
});
|
|
||||||
const closeButton = createElement('button', xButton, 'twitch-notes-settings-close-button', {
|
|
||||||
position: 'absolute',
|
|
||||||
top: '1rem',
|
|
||||||
right: '1rem',
|
|
||||||
});
|
|
||||||
|
|
||||||
const title = createElement('div', 'Notes:', 'twitch-note-title', {
|
|
||||||
borderBottom: '1px solid #FFFFFF19',
|
|
||||||
paddingBottom: '1rem',
|
|
||||||
});
|
|
||||||
container.appendChild(title);
|
|
||||||
|
|
||||||
const content = createElement('div', null, null, {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'no-wrap',
|
|
||||||
width: '100%',
|
|
||||||
height: '320px'
|
|
||||||
});
|
|
||||||
container.appendChild(content);
|
|
||||||
closeButton.addEventListener('click', () => {
|
|
||||||
blurredBackground.remove();
|
|
||||||
});
|
|
||||||
container.appendChild(closeButton);
|
|
||||||
|
|
||||||
const userList = createElement('div', null, null, {
|
|
||||||
borderRight: '1px solid #FFFFFF19',
|
|
||||||
height: '320px',
|
|
||||||
minWidth: '240px',
|
|
||||||
overflowY: 'auto',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
});
|
|
||||||
|
|
||||||
const noteContainer = createElement('div', null, null, {
|
|
||||||
height: '320px',
|
|
||||||
padding: '1rem',
|
|
||||||
width: '100%',
|
|
||||||
opacity: '0',
|
|
||||||
});
|
|
||||||
|
|
||||||
const note = createElement('div', null, null, {
|
|
||||||
marginBottom: '3rem',
|
|
||||||
border: '1px solid #FFFFFF19',
|
|
||||||
height: 'calc(320px - 7rem)',
|
|
||||||
width: '100%',
|
|
||||||
padding: '6px',
|
|
||||||
outline: 'none',
|
|
||||||
overflowY: 'auto',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
}, {
|
|
||||||
contentEditable: 'true',
|
|
||||||
});
|
|
||||||
const saveNote = createElement('button', 'Save Note', 'twitch-notes-settings-button', {
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: '1rem',
|
|
||||||
left: '1rem'
|
|
||||||
});
|
|
||||||
|
|
||||||
saveNote.addEventListener('click', () => {
|
|
||||||
if(!activeNote) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Notes.saveNote(activeNote, note.innerHTML);
|
|
||||||
})
|
|
||||||
|
|
||||||
for(let user of Notes.getSavedUserList()) {
|
|
||||||
const userLine = createElement('div', user, 'twitch-notes-user-list-user');
|
|
||||||
userLine.addEventListener('click', () => {
|
|
||||||
const list = document.getElementsByClassName('twitch-notes-user-list-user');
|
|
||||||
for(let i = 0; i < list.length; i++) {
|
|
||||||
list.item(i).removeAttribute('data-active');
|
|
||||||
}
|
|
||||||
userLine.setAttribute('data-active', 'true');
|
|
||||||
noteContainer.style.opacity = '1';
|
|
||||||
note.innerHTML = Notes.getNote(user);
|
|
||||||
activeNote = user;
|
|
||||||
});
|
|
||||||
userList.appendChild(userLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
noteContainer.appendChild(note);
|
|
||||||
noteContainer.appendChild(saveNote);
|
|
||||||
|
|
||||||
content.appendChild(userList);
|
|
||||||
content.appendChild(noteContainer);
|
|
||||||
|
|
||||||
blurredBackground.appendChild(container);
|
|
||||||
document.body.appendChild(blurredBackground);
|
|
||||||
},
|
|
||||||
importNotesConflictResolve: (user, resultsContainer, container, blurredBackground) => {
|
|
||||||
const diffBlurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
|
|
||||||
const diffContainer = createElement('div', '<strong>Select which note to keep for <em>' + user + '</em></strong><br /><em>You can modify notes to merge them and keep updated one</em><br /><br />', 'twitch-notes-center-floating-container');
|
|
||||||
|
|
||||||
const inputContainer = createElement('div', null, null, {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'no-wrap',
|
|
||||||
width: '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
const valueStyle = {
|
|
||||||
minWidth: '320px',
|
|
||||||
padding: '6px',
|
|
||||||
border: '1px solid #FFFFFF19',
|
|
||||||
outline: 'none',
|
|
||||||
};
|
|
||||||
const valueAttr = {contentEditable: 'true'};
|
|
||||||
|
|
||||||
const localValueContainer = createElement('div');
|
|
||||||
const pLocal = createElement('p', '<strong>Locally saved note</strong>');
|
|
||||||
localValueContainer.appendChild(pLocal);
|
|
||||||
const localValue = createElement('div', Notes.getNote(user), null, valueStyle, valueAttr, 'local-value-container');
|
|
||||||
const localValueSave = createElement('button', 'Save this', 'twitch-notes-settings-button');
|
|
||||||
localValueSave.addEventListener('click', () => {
|
|
||||||
const val = document.getElementById('local-value-container').innerHTML;
|
|
||||||
Notes.inputData.notes = Notes.inputData.notes.map(n => n.user === user ? {
|
|
||||||
user: n.user,
|
|
||||||
note: val,
|
|
||||||
resolved: true
|
|
||||||
} : n);
|
|
||||||
diffBlurredBackground.remove();
|
|
||||||
resultsContainer.remove();
|
|
||||||
Notes.importNotesResult(container, blurredBackground);
|
|
||||||
});
|
|
||||||
localValueContainer.appendChild(localValue);
|
|
||||||
localValueContainer.innerHTML += '<br />';
|
|
||||||
localValueContainer.appendChild(localValueSave);
|
|
||||||
|
|
||||||
const importedValueContainer = createElement('div');
|
|
||||||
const iLocal = createElement('p', '<strong>Imported note</strong>');
|
|
||||||
importedValueContainer.appendChild(iLocal);
|
|
||||||
const importedValue = createElement('div', Notes.inputData.notes.filter(n => n.user === user)[0].note || '', null, valueStyle, valueAttr, 'import-value-container');
|
|
||||||
const importedValueSave = createElement('button', 'Save this', 'twitch-notes-settings-button');
|
|
||||||
importedValueSave.addEventListener('click', () => {
|
|
||||||
const val = document.getElementById('import-value-container').innerHTML;
|
|
||||||
Notes.inputData.notes = Notes.inputData.notes.map(n => n.user === user ? {
|
|
||||||
user: n.user,
|
|
||||||
note: val,
|
|
||||||
resolved: true
|
|
||||||
} : n);
|
|
||||||
diffBlurredBackground.remove();
|
|
||||||
resultsContainer.remove();
|
|
||||||
Notes.importNotesResult(container, blurredBackground);
|
|
||||||
});
|
|
||||||
|
|
||||||
importedValueContainer.appendChild(importedValue);
|
|
||||||
importedValueContainer.innerHTML += '<br />';
|
|
||||||
importedValueContainer.appendChild(importedValueSave);
|
|
||||||
|
|
||||||
|
|
||||||
inputContainer.appendChild(localValueContainer);
|
|
||||||
inputContainer.appendChild(importedValueContainer);
|
|
||||||
|
|
||||||
diffContainer.appendChild(inputContainer);
|
|
||||||
diffBlurredBackground.appendChild(diffContainer);
|
|
||||||
document.body.appendChild(diffBlurredBackground);
|
|
||||||
},
|
|
||||||
importNotesResult: (container, blurredBackground) => {
|
|
||||||
|
|
||||||
const willBeAdded = [];
|
|
||||||
let willBeOverwritten = [];
|
|
||||||
const noChanges = [];
|
|
||||||
|
|
||||||
for (let user of Notes.inputData.users) {
|
|
||||||
if (Notes.getNote(user)) {
|
|
||||||
if (Notes.inputData.notes.find(note => note.user === user).resolved || Notes.getNote(user) !== Notes.inputData.notes.find(note => note.user === user).note) {
|
|
||||||
willBeOverwritten.push(user);
|
|
||||||
} else {
|
|
||||||
noChanges.push(user);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
willBeAdded.push(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultsContainer = createElement('div', null);
|
|
||||||
|
|
||||||
if (willBeOverwritten.length) {
|
|
||||||
const overwrittenContainer = createElement('div', '<strong>Note conflicts found for:</strong><br />', null, {
|
|
||||||
marginTop: '1rem',
|
|
||||||
});
|
|
||||||
for (let item of willBeOverwritten) {
|
|
||||||
const row = createElement('div', item + ' ', null, {
|
|
||||||
color: Notes.inputData.notes.find(note => note.user === item).resolved ? 'green' : 'red',
|
|
||||||
});
|
|
||||||
const resolve = createElement('a', Notes.inputData.notes.find(note => note.user === item).resolved ? '[update]' : '[resolve]', null, {
|
|
||||||
cursor: 'pointer',
|
|
||||||
});
|
|
||||||
resolve.addEventListener('click', () => {
|
|
||||||
Notes.importNotesConflictResolve(item, resultsContainer, container, blurredBackground);
|
|
||||||
})
|
|
||||||
row.appendChild(resolve);
|
|
||||||
overwrittenContainer.appendChild(row);
|
|
||||||
}
|
|
||||||
resultsContainer.appendChild(overwrittenContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (willBeAdded.length) {
|
|
||||||
const addedContainer = createElement('div', '<strong>Note will be added for:</strong><br />' + willBeAdded.join('<br />'), null, {
|
|
||||||
marginTop: '1rem',
|
|
||||||
color: 'white'
|
|
||||||
})
|
|
||||||
resultsContainer.appendChild(addedContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (noChanges.length) {
|
|
||||||
const noChangesContainer = createElement('div', '<strong>No changes for:</strong><br />' + noChanges.join('<br />'), null, {
|
|
||||||
marginTop: '1rem',
|
|
||||||
color: 'gray'
|
|
||||||
})
|
|
||||||
resultsContainer.appendChild(noChangesContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
const spacer = createElement('div', null, null, {height: '24px'});
|
|
||||||
resultsContainer.appendChild(spacer);
|
|
||||||
|
|
||||||
willBeOverwritten = willBeOverwritten.filter(x => !Notes.inputData.notes.find(n => n.user === x).resolved);
|
|
||||||
|
|
||||||
const saveButton = createElement('button', 'Complete import', 'twitch-notes-settings-button',
|
|
||||||
willBeOverwritten.length > 0 ? {background: 'gray'} : null,
|
|
||||||
willBeOverwritten.length > 0 ? {disabled: 'true'} : null)
|
|
||||||
|
|
||||||
resultsContainer.appendChild(saveButton)
|
|
||||||
|
|
||||||
if (willBeOverwritten.length > 0) {
|
|
||||||
const note = createElement('div', '<em>Import cannot be completed while there are unresolved conflicts</em>', null, {
|
|
||||||
color: 'gray',
|
|
||||||
});
|
|
||||||
resultsContainer.appendChild(note);
|
|
||||||
} else {
|
|
||||||
saveButton.addEventListener('click', () => {
|
|
||||||
for (let note of Notes.inputData.notes) {
|
|
||||||
Notes.saveNote(note.user, note.note);
|
|
||||||
}
|
|
||||||
blurredBackground.remove();
|
|
||||||
Notes.inputData = {};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
container.appendChild(resultsContainer);
|
|
||||||
},
|
|
||||||
importNotes: () => {
|
|
||||||
const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
|
|
||||||
const container = createElement('div', '<strong>WARNING!</strong> <em>This action might override existing data!</em><br /><br />', 'twitch-notes-center-floating-container');
|
|
||||||
const inputBlock = createElement('div', 'Select exported twitch notes file<br /><br />');
|
|
||||||
const input = createElement('input', null, 'twitch-notes-file-input', null, {
|
|
||||||
type: 'file',
|
|
||||||
accept: '.json'
|
|
||||||
});
|
|
||||||
const settingsCloseButton = createElement('button', xButton, 'twitch-notes-settings-close-button', {
|
|
||||||
position: 'absolute',
|
|
||||||
top: '1rem',
|
|
||||||
right: '1rem',
|
|
||||||
});
|
|
||||||
inputBlock.appendChild(input);
|
|
||||||
settingsCloseButton.addEventListener('click', () => {
|
|
||||||
blurredBackground.remove();
|
|
||||||
});
|
|
||||||
input.addEventListener('change', () => {
|
|
||||||
let file = input.files[0];
|
|
||||||
if (!file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.readAsText(file, "UTF-8");
|
|
||||||
reader.onload = function (evt) {
|
|
||||||
Notes.inputData = JSON.parse(evt.target.result.toString());
|
|
||||||
if (Notes.inputData) {
|
|
||||||
inputBlock.style.display = 'none';
|
|
||||||
Notes.importNotesResult(container, blurredBackground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.onerror = function () {
|
|
||||||
console.error("error reading file");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
container.appendChild(settingsCloseButton);
|
|
||||||
container.appendChild(inputBlock);
|
|
||||||
blurredBackground.appendChild(container);
|
|
||||||
document.body.appendChild(blurredBackground);
|
|
||||||
},
|
|
||||||
clearAllData: () => {
|
|
||||||
const blurredBackground = createElement('div', null, 'twitch-notes-blurred-background');
|
|
||||||
const container = createElement('div', '<strong>This action will delete all saved notes!</strong><br /><br /><em>Consider exporting notes before performing this action in case you will need to use notes again</em>', 'twitch-notes-center-floating-container');
|
|
||||||
|
|
||||||
const spacer = createElement('div', null, null, {
|
|
||||||
height: '24px'
|
|
||||||
});
|
|
||||||
const deleteAction = createElement('button', 'DELETE ALL NOTES', 'twitch-notes-settings-button', {
|
|
||||||
background: 'red'
|
|
||||||
});
|
|
||||||
deleteAction.addEventListener('click', () => {
|
|
||||||
const users = Notes.getSavedUserList();
|
|
||||||
for(let user of users) {
|
|
||||||
Notes.deleteNote(user);
|
|
||||||
}
|
|
||||||
blurredBackground.remove();
|
|
||||||
})
|
|
||||||
const cancelAction = createElement('button', 'Cancel', 'twitch-notes-settings-button');
|
|
||||||
cancelAction.addEventListener('click', () => {
|
|
||||||
blurredBackground.remove();
|
|
||||||
})
|
|
||||||
|
|
||||||
container.appendChild(spacer);
|
|
||||||
container.appendChild(deleteAction);
|
|
||||||
container.appendChild(cancelAction);
|
|
||||||
|
|
||||||
blurredBackground.appendChild(container);
|
|
||||||
document.body.appendChild(blurredBackground);
|
|
||||||
},
|
|
||||||
openAllNotes: () => {
|
|
||||||
Notes.showAllNotes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mouse = {
|
|
||||||
mousePosition: {x: 0, y: 0},
|
|
||||||
lastMousePosition: {x: 0, y: 0},
|
|
||||||
handleMouseMove: (event) => {
|
|
||||||
if (isMouseDown) {
|
|
||||||
if (activeContainer) {
|
|
||||||
const dx = Mouse.mousePosition.x - Mouse.lastMousePosition.x;
|
|
||||||
const dy = Mouse.mousePosition.y - Mouse.lastMousePosition.y;
|
|
||||||
openContainers[activeContainer].style.top =
|
|
||||||
parseInt(openContainers[activeContainer].style.top) + dy + "px";
|
|
||||||
openContainers[activeContainer].style.left =
|
|
||||||
parseInt(openContainers[activeContainer].style.left) + dx + "px";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Mouse.updateMousePosition(event.clientX, event.clientY);
|
|
||||||
},
|
|
||||||
handleMouseUp: () => {
|
|
||||||
activeContainer = null;
|
|
||||||
isMouseDown = false;
|
|
||||||
},
|
|
||||||
updateMousePosition: (x, y) => {
|
|
||||||
Mouse.lastMousePosition = {...Mouse.mousePosition};
|
|
||||||
Mouse.mousePosition = {x, y};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createElement(tag, body, classes, styles, attributes, id) {
|
|
||||||
const element = document.createElement(tag);
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
element.innerHTML = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classes) {
|
|
||||||
if (typeof classes === 'object') {
|
|
||||||
for (let className of classes) {
|
|
||||||
element.classList.add((className));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
element.classList.add(classes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styles) {
|
|
||||||
if (typeof styles === 'object') {
|
|
||||||
for (let style in styles) {
|
|
||||||
element.style[style] = styles[style];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes) {
|
|
||||||
if (typeof attributes === 'object') {
|
|
||||||
for (let attribute in attributes) {
|
|
||||||
element.setAttribute(attribute, attributes[attribute]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
element.setAttribute('id', id ? id : 'element-' + Math.floor((Math.random() * 10000000)))
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSettingsButton(buttonContainer) {
|
|
||||||
|
|
||||||
const c1 = createElement('div', null, null, {marginLeft: "0.5rem !important"});
|
|
||||||
const c2 = createElement('div', null, null, {display: "inline-flex !important"});
|
|
||||||
const settingsButton = createElement('button', 'Notes', 'twitch-notes-settings-button');
|
|
||||||
|
|
||||||
settingsButton.addEventListener("click", toggleSettings);
|
|
||||||
|
|
||||||
c2.appendChild(settingsButton);
|
|
||||||
c1.appendChild(c2);
|
|
||||||
|
|
||||||
buttonContainer.lastChild.insertBefore(
|
|
||||||
c1,
|
|
||||||
buttonContainer.lastChild.lastChild
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
const container = createElement('div', null, 'twitch-note-container', {
|
|
||||||
left: Mouse.mousePosition.x + 'px',
|
|
||||||
top: Mouse.mousePosition.y + 10 + 'px'
|
|
||||||
});
|
|
||||||
|
|
||||||
const header = createElement('div', null, 'twitch-note-header');
|
|
||||||
header.addEventListener("mousedown", function close() {
|
|
||||||
activeContainer = username;
|
|
||||||
isMouseDown = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const closeButton = createElement('span', xButton, 'twitch-note-close-button')
|
|
||||||
closeButton.addEventListener("click", function close() {
|
|
||||||
removeContainer(username);
|
|
||||||
});
|
|
||||||
|
|
||||||
const title = createElement('span', username, 'twitch-note-title');
|
|
||||||
const content = createElement('div', Notes.getNote(username), 'twitch-note-content', [], {
|
|
||||||
contentEditable: 'true'
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveButton = createElement('div', 'SAVE', 'twitch-note-save-button')
|
|
||||||
saveButton.addEventListener("click", function () {
|
|
||||||
Notes.saveNote(username, content.innerHTML);
|
|
||||||
removeContainer(username);
|
|
||||||
});
|
|
||||||
|
|
||||||
header.appendChild(closeButton);
|
|
||||||
header.appendChild(title);
|
|
||||||
container.appendChild(header);
|
|
||||||
container.appendChild(content);
|
|
||||||
container.appendChild(saveButton);
|
|
||||||
|
|
||||||
addContainer(username, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNoteBadges() {
|
|
||||||
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(".twitch-note");
|
|
||||||
if (!noteButton) {
|
|
||||||
|
|
||||||
const twitchNote = createElement('span', null, 'twitch-note', {
|
|
||||||
cursor: 'pointer',
|
|
||||||
})
|
|
||||||
twitchNote.addEventListener("click", () => {
|
|
||||||
openTwitchNote(username);
|
|
||||||
});
|
|
||||||
|
|
||||||
const img = createElement('img', null, null, {
|
|
||||||
height: '18px',
|
|
||||||
paddingRight: '4px'
|
|
||||||
}, {
|
|
||||||
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]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createChatSettingsLine(text) {
|
|
||||||
const element = createElement('div', null, 'twitch-notes-settings-option-line');
|
|
||||||
const button = createElement('button', null, 'twitch-notes-settings-option-line-button');
|
|
||||||
const buttonContainer = createElement('div', null, 'twitch-notes-settings-option-line-button-container');
|
|
||||||
const buttonContainerContent = createElement('div', text, 'twitch-notes-settings-option-line-button-container-content');
|
|
||||||
buttonContainer.appendChild(buttonContainerContent);
|
|
||||||
button.appendChild(buttonContainer);
|
|
||||||
element.appendChild(button);
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createChatSettingsSeparator() {
|
|
||||||
return createElement('div', null, 'twitch-note-settings-separator');
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSettings() {
|
|
||||||
if (!settingsWindow) {
|
|
||||||
const settingsContainer = createElement('div', null, 'twitch-notes-settings-container');
|
|
||||||
const settingsBalloon = createElement('div', null, 'twitch-notes-settings-balloon');
|
|
||||||
const settingsPopover = createElement('div', null, 'twitch-notes-settings-popover');
|
|
||||||
|
|
||||||
// startOf: settings header
|
|
||||||
const settingsHeader = createElement('div', null, 'twitch-notes-settings-header');
|
|
||||||
const settingsHeaderLeftElement = createElement('div', null, 'twitch-notes-settings-header-left-element')
|
|
||||||
settingsHeader.appendChild(settingsHeaderLeftElement);
|
|
||||||
const settingsHeaderCenterElement = createElement('div', null, 'twitch-notes-settings-header-center-element')
|
|
||||||
settingsHeader.appendChild(settingsHeaderCenterElement);
|
|
||||||
const settingsHeaderCenterElementContent = createElement('p', 'Twitch Notes Settings', 'twitch-notes-settings-header-center-element-content')
|
|
||||||
settingsHeaderCenterElement.appendChild(settingsHeaderCenterElementContent);
|
|
||||||
const settingsHeaderRightElement = createElement('div', null, 'twitch-notes-settings-header-right-element')
|
|
||||||
settingsHeader.appendChild(settingsHeaderRightElement);
|
|
||||||
const settingsCloseButton = createElement('button', xButton, 'twitch-notes-settings-close-button');
|
|
||||||
settingsHeaderRightElement.appendChild(settingsCloseButton);
|
|
||||||
settingsPopover.appendChild(settingsHeader);
|
|
||||||
// endOf: settings header
|
|
||||||
|
|
||||||
settingsCloseButton.addEventListener('click', toggleSettings);
|
|
||||||
|
|
||||||
|
|
||||||
// startOf: scrollable area
|
|
||||||
const settingsScrollableArea = createElement('div', null, 'twitch-notes-settings-scrollable-area');
|
|
||||||
const settingsContent = createElement('div', null, 'twitch-notes-settings-content');
|
|
||||||
settingsScrollableArea.appendChild(settingsContent);
|
|
||||||
|
|
||||||
const exportNotes = createChatSettingsLine('Export Notes');
|
|
||||||
settingsContent.appendChild(exportNotes);
|
|
||||||
const importNotes = createChatSettingsLine('Import Notes');
|
|
||||||
settingsContent.appendChild(importNotes);
|
|
||||||
const clearData = createChatSettingsLine('Clear Data');
|
|
||||||
settingsContent.appendChild(clearData);
|
|
||||||
settingsContent.appendChild(createChatSettingsSeparator());
|
|
||||||
const openAllNotes = createChatSettingsLine('View All Notes');
|
|
||||||
settingsContent.appendChild(openAllNotes);
|
|
||||||
|
|
||||||
exportNotes.addEventListener('click', () => {Notes.exportNotes(); toggleSettings()});
|
|
||||||
importNotes.addEventListener('click', () => {Notes.importNotes(); toggleSettings()});
|
|
||||||
clearData.addEventListener('click', () => {Notes.clearAllData(); toggleSettings()});
|
|
||||||
openAllNotes.addEventListener('click', () => {Notes.openAllNotes(); toggleSettings()});
|
|
||||||
|
|
||||||
|
|
||||||
settingsPopover.appendChild(settingsScrollableArea);
|
|
||||||
// endOf: scrollable area
|
|
||||||
|
|
||||||
|
|
||||||
settingsBalloon.appendChild(settingsPopover);
|
|
||||||
settingsContainer.appendChild(settingsBalloon);
|
|
||||||
settingsWindow = settingsContainer;
|
|
||||||
document.body.appendChild(settingsWindow);
|
|
||||||
settingsWindowOpen = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsWindowOpen) {
|
|
||||||
settingsWindow.style.display = 'none';
|
|
||||||
settingsWindowOpen = false;
|
|
||||||
} else {
|
|
||||||
settingsWindow.style.display = 'block';
|
|
||||||
settingsWindowOpen = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const xButton = `<svg width="100%" height="100%" viewBox="0 0 20 20" x="0px" y="0px" class="twitch-notes-x-button"><g><path d="M8.5 10L4 5.5 5.5 4 10 8.5 14.5 4 16 5.5 11.5 10l4.5 4.5-1.5 1.5-4.5-4.5L5.5 16 4 14.5 8.5 10z"></path></g></svg>`;
|
|
||||||
let openContainers = {};
|
|
||||||
let isMouseDown = false;
|
|
||||||
let activeContainer = null;
|
|
||||||
let settingsWindowOpen = false;
|
|
||||||
let settingsWindow = null;
|
|
||||||
|
|
||||||
let LS_UserList = "twitch-note-all-users-list";
|
|
||||||
let LS_Prefix = "twitch-note-";
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", Mouse.handleMouseMove);
|
|
||||||
document.addEventListener("mouseup", Mouse.handleMouseUp);
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
if (!changeInProgress) {
|
|
||||||
for (const mutation of mutationList) {
|
|
||||||
if (mutation.type === "childList") {
|
|
||||||
changeInProgress = true;
|
|
||||||
addNoteBadges();
|
|
||||||
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);
|
|
||||||
})();
|
|
||||||
File diff suppressed because one or more lines are too long
1
dist/twitch-notes.js
vendored
1
dist/twitch-notes.js
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Twitch Notes",
|
"name": "Twitch Notes",
|
||||||
"version": "0.0.1",
|
"version": "0.2.1",
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
{
|
{
|
||||||
"resources": [
|
"resources": [
|
||||||
@@ -39,6 +39,5 @@
|
|||||||
"*://*.twitch.tv/*"
|
"*://*.twitch.tv/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"permissions": ["webNavigation", "webRequest"]
|
|
||||||
}
|
}
|
||||||
@@ -13,14 +13,4 @@
|
|||||||
script.id = "twitchNotesScript";
|
script.id = "twitchNotesScript";
|
||||||
head.appendChild(script);
|
head.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!document.querySelector("style#twitchNotesStyle")) {
|
|
||||||
// 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);
|
|
||||||
// }
|
|
||||||
})();
|
})();
|
||||||
2
extension/twitch-notes.js
Normal file
2
extension/twitch-notes.js
Normal file
File diff suppressed because one or more lines are too long
1
extension/twitch-notes.js.map
Normal file
1
extension/twitch-notes.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "twitchnotes",
|
"name": "twitchnotes",
|
||||||
"version": "0.0.1",
|
"version": "0.2.1",
|
||||||
"description": "Add notes to twitch users (only visible to you)",
|
"description": "Add notes to twitch users (only visible to you)",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack && cp ./dist/twitch-notes.js './Twitch Notes/twitch-notes.js'"
|
"build": "webpack",
|
||||||
|
"zip": "cd extension && zip ../twitchNotes.zip ./*"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
112
src/HTMLBuilder.ts
Normal file
112
src/HTMLBuilder.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import {HTMLBuilderBlock} from "./types";
|
||||||
|
|
||||||
|
export default class HTMLBuilder {
|
||||||
|
|
||||||
|
element: HTMLElement;
|
||||||
|
|
||||||
|
constructor(data: HTMLBuilderBlock) {
|
||||||
|
this.element = document.createElement(data.element);
|
||||||
|
this.setId(data.id ?? data.element + '-custom-element-' + Math.floor(Math.random() * 1000000));
|
||||||
|
if (data.class) this.setClasses(data.class);
|
||||||
|
if (data.attributes) this.setAttributes(data.attributes);
|
||||||
|
if (data.style) this.setStyles(data.style);
|
||||||
|
if (data.mouseClickEvent) this.setMouseClickListener(data.mouseClickEvent);
|
||||||
|
if (data.mouseMoveEvent) this.setMouseMoveListener(data.mouseMoveEvent);
|
||||||
|
if (data.mouseDownEvent) this.setMouseDownListener(data.mouseDownEvent);
|
||||||
|
if (data.mouseUpEvent) this.setMouseUpListener(data.mouseUpEvent);
|
||||||
|
if (data.keyUpEvent) this.setKeyUpListener(data.keyUpEvent);
|
||||||
|
if (data.changeListener) this.setChangeListener(data.changeListener);
|
||||||
|
if (data.content) this.setContent(data.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
setId(id: string) {
|
||||||
|
this.element.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setClasses(classes: string | string[]) {
|
||||||
|
if (typeof classes === 'string') {
|
||||||
|
this.addClass(classes);
|
||||||
|
} else {
|
||||||
|
for(let className of classes) {
|
||||||
|
this.addClass(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addClass(className: string) {
|
||||||
|
this.element.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttributes(attributes: {[key: string]: string}) {
|
||||||
|
for(let key in attributes) {
|
||||||
|
if (!attributes[key]) continue;
|
||||||
|
this.addAttribute(key, attributes[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addAttribute(key: string, value: string) {
|
||||||
|
this.element.setAttribute(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyles(style: Partial<CSSStyleDeclaration>) {
|
||||||
|
for (const key in style) {
|
||||||
|
this.addStyle(key, style[key] || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addStyle(key: string, value: string) {
|
||||||
|
this.element.style.setProperty(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseClickListener(cb: Function) {
|
||||||
|
this.element.addEventListener('click', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent(content: (HTMLBuilderBlock|string|HTMLElement)[]) {
|
||||||
|
for (let block of content) {
|
||||||
|
this.addContent(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addContent(content: HTMLBuilderBlock|string|HTMLElement) {
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
this.element.innerHTML += content;
|
||||||
|
} else {
|
||||||
|
if (content instanceof HTMLElement) {
|
||||||
|
this.element.appendChild(content);
|
||||||
|
} else {
|
||||||
|
const el = new HTMLBuilder(content);
|
||||||
|
this.element.appendChild(el.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseMoveListener(cb: Function) {
|
||||||
|
this.element.addEventListener('mousemove', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseDownListener(cb: Function) {
|
||||||
|
this.element.addEventListener('mousedown', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseUpListener(cb: Function) {
|
||||||
|
this.element.addEventListener('mouseup', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeyUpListener(cb: Function) {
|
||||||
|
this.element.addEventListener('keyup', () => cb(this.element.innerHTML));
|
||||||
|
}
|
||||||
|
|
||||||
|
setChangeListener(cb: Function) {
|
||||||
|
this.element.addEventListener('change', (e) => cb(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
getElement() {
|
||||||
|
return this.element;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
src/HTMLElements/TwitchButton.ts
Normal file
30
src/HTMLElements/TwitchButton.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export default class TwitchButton {
|
||||||
|
|
||||||
|
element: HTMLButtonElement;
|
||||||
|
|
||||||
|
constructor(content: string, type: 'default' | 'outline' = 'default', classes: string|string[] = '') {
|
||||||
|
this.element = document.createElement('button');
|
||||||
|
this.element.textContent = content;
|
||||||
|
|
||||||
|
this.element.classList.add('twitch-notes_button');
|
||||||
|
if(type === 'outline') {
|
||||||
|
this.element.classList.add('twitch-notes_button__outline');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classes) {
|
||||||
|
if (typeof classes === 'string') {
|
||||||
|
this.element.classList.add(classes);
|
||||||
|
} else {
|
||||||
|
for (let className of classes) {
|
||||||
|
this.element.classList.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(cb: Function) {
|
||||||
|
this.element.addEventListener('click', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
31
src/HTMLElements/TwitchCloseButton.ts
Normal file
31
src/HTMLElements/TwitchCloseButton.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export default class TwitchCloseButton {
|
||||||
|
|
||||||
|
element: HTMLButtonElement;
|
||||||
|
|
||||||
|
constructor(classes: string|string[] = '') {
|
||||||
|
this.element = document.createElement('button');
|
||||||
|
this.element.innerHTML = `<svg width="100%" height="100%" viewBox="0 0 20 20" x="0px" y="0px" class="twitch-notes_x-button">
|
||||||
|
<g>
|
||||||
|
<path d="M8.5 10L4 5.5 5.5 4 10 8.5 14.5 4 16 5.5 11.5 10l4.5 4.5-1.5 1.5-4.5-4.5L5.5 16 4 14.5 8.5 10z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
this.element.classList.add('twitch-notes_close-button');
|
||||||
|
|
||||||
|
if (classes) {
|
||||||
|
if (typeof classes === 'string') {
|
||||||
|
this.element.classList.add(classes);
|
||||||
|
} else {
|
||||||
|
for (let className of classes) {
|
||||||
|
this.element.classList.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(cb: Function) {
|
||||||
|
this.element.addEventListener('click', () => cb());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
113
src/HTMLElements/TwitchNotesImport.ts
Normal file
113
src/HTMLElements/TwitchNotesImport.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import TwitchCloseButton from "./TwitchCloseButton";
|
||||||
|
import NoteStorage from "../NoteStorage";
|
||||||
|
// import openImportNotesWindow from "../openImprtNotesWindow";
|
||||||
|
import openClearDataWindow from "../openClearDataWindow";
|
||||||
|
import TwitchButton from "./TwitchButton";
|
||||||
|
import TwitchNotesWindow from "./TwitchNotesWindow";
|
||||||
|
import importNotesResult from "../importNotesResult";
|
||||||
|
import TwitchNotesImportResolve from "./TwitchNotesImportResolve";
|
||||||
|
|
||||||
|
export default class TwitchNotesImport {
|
||||||
|
|
||||||
|
private static instance?: TwitchNotesImport;
|
||||||
|
private element?: HTMLDivElement;
|
||||||
|
private userList?: HTMLDivElement;
|
||||||
|
private static resolveCallback?: Function;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
private create() {
|
||||||
|
if (!this.element) {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.innerHTML = '';
|
||||||
|
|
||||||
|
this.element.classList.add('twitch-notes-blurred-background');
|
||||||
|
|
||||||
|
const closeButton = new TwitchCloseButton();
|
||||||
|
closeButton.onClick(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.classList.add('twitch-notes-center-floating-container');
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.classList.add('twitch-notes-container-header');
|
||||||
|
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.classList.add('twitch-notes-container-header-title');
|
||||||
|
title.textContent = 'Import Notes';
|
||||||
|
header.appendChild(title);
|
||||||
|
container.appendChild(header);
|
||||||
|
header.insertAdjacentElement("beforeend", closeButton.element);
|
||||||
|
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.classList.add('twitch-notes-container-content');
|
||||||
|
content.style.display = 'block';
|
||||||
|
content.style.padding = '1rem';
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
content.innerHTML = `
|
||||||
|
<strong>WARNING!</strong> <em>This action might override existing data!</em><br />
|
||||||
|
<br />
|
||||||
|
Select exported twitch notes file<br />
|
||||||
|
`;
|
||||||
|
|
||||||
|
const blurredBackground = this.element;
|
||||||
|
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.classList.add('twitch-notes-file-input');
|
||||||
|
input.setAttribute('type', 'file');
|
||||||
|
input.setAttribute('accept', 'json');
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
if (!input.files) return;
|
||||||
|
// @ts-ignore
|
||||||
|
let file = input.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) {
|
||||||
|
input.style.display = 'none';
|
||||||
|
importNotesResult(container, blurredBackground, TwitchNotesImport.resolveCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.onerror = function () {
|
||||||
|
console.error("error reading file");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
content.appendChild(input);
|
||||||
|
|
||||||
|
this.element.appendChild(container);
|
||||||
|
document.body.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private close() {
|
||||||
|
if (this.element) this.element.remove();
|
||||||
|
this.element = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static update() {
|
||||||
|
TwitchNotesImport.instance?.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static open(cb: Function) {
|
||||||
|
TwitchNotesImport.resolveCallback = cb;
|
||||||
|
TwitchNotesImport.instance = new TwitchNotesImport();
|
||||||
|
TwitchNotesImport.instance.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static close() {
|
||||||
|
TwitchNotesImport.instance?.element?.remove();
|
||||||
|
TwitchNotesImport.instance = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
76
src/HTMLElements/TwitchNotesImportResolve.ts
Normal file
76
src/HTMLElements/TwitchNotesImportResolve.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import TwitchCloseButton from "./TwitchCloseButton";
|
||||||
|
import NoteStorage from "../NoteStorage";
|
||||||
|
// import openImportNotesWindow from "../openImprtNotesWindow";
|
||||||
|
import openClearDataWindow from "../openClearDataWindow";
|
||||||
|
import TwitchButton from "./TwitchButton";
|
||||||
|
import TwitchNotesWindow from "./TwitchNotesWindow";
|
||||||
|
import importNotesResult from "../importNotesResult";
|
||||||
|
import importNotesConflictResolve from "../importNotesConflictResolve";
|
||||||
|
import HTMLBuilder from "../HTMLBuilder";
|
||||||
|
|
||||||
|
export default class TwitchNotesImportResolve {
|
||||||
|
|
||||||
|
private static instance?: TwitchNotesImportResolve;
|
||||||
|
private element?: HTMLDivElement;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
private create(
|
||||||
|
user: string,
|
||||||
|
parentContainer: HTMLElement,
|
||||||
|
blurredBackground: HTMLElement
|
||||||
|
) {
|
||||||
|
if (!this.element) {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.innerHTML = '';
|
||||||
|
|
||||||
|
this.element.classList.add('twitch-notes-blurred-background');
|
||||||
|
|
||||||
|
const closeButton = new TwitchCloseButton();
|
||||||
|
closeButton.onClick(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.classList.add('twitch-notes-center-floating-container');
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.classList.add('twitch-notes-container-header');
|
||||||
|
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.classList.add('twitch-notes-container-header-title');
|
||||||
|
title.textContent = 'Resolve note ('+user+') conflict';
|
||||||
|
header.appendChild(title);
|
||||||
|
container.appendChild(header);
|
||||||
|
header.insertAdjacentElement("beforeend", closeButton.element);
|
||||||
|
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.classList.add('twitch-notes-container-content');
|
||||||
|
content.style.display = 'block';
|
||||||
|
content.style.padding = '1rem';
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
content.appendChild(importNotesConflictResolve(user, this.element, parentContainer, blurredBackground));
|
||||||
|
|
||||||
|
this.element.appendChild(container);
|
||||||
|
document.body.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private close() {
|
||||||
|
if (this.element) this.element.remove();
|
||||||
|
this.element = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static open(user: string,
|
||||||
|
container: HTMLElement,
|
||||||
|
blurredBackground: HTMLElement) {
|
||||||
|
TwitchNotesImportResolve.instance = new TwitchNotesImportResolve();
|
||||||
|
TwitchNotesImportResolve.instance.create(user, container, blurredBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
259
src/HTMLElements/TwitchNotesWindow.ts
Normal file
259
src/HTMLElements/TwitchNotesWindow.ts
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
import TwitchCloseButton from "./TwitchCloseButton";
|
||||||
|
import NoteStorage from "../NoteStorage";
|
||||||
|
// import openImportNotesWindow from "../openImprtNotesWindow";
|
||||||
|
import openClearDataWindow from "../openClearDataWindow";
|
||||||
|
import TwitchButton from "./TwitchButton";
|
||||||
|
import TwitchNotesImport from "./TwitchNotesImport";
|
||||||
|
|
||||||
|
export default class TwitchNotesWindow {
|
||||||
|
|
||||||
|
private static instance?: TwitchNotesWindow;
|
||||||
|
private element?: HTMLDivElement;
|
||||||
|
private userList?: HTMLDivElement;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildContent(content: HTMLDivElement) {
|
||||||
|
content.innerHTML = '';
|
||||||
|
|
||||||
|
const userButtons: HTMLDivElement[] = [];
|
||||||
|
|
||||||
|
this.userList = document.createElement('div');
|
||||||
|
this.userList.classList.add('twitch-notes-container-content-user-list');
|
||||||
|
|
||||||
|
const addNoteButton = document.createElement('div');
|
||||||
|
addNoteButton.classList.add('twitch-notes-container-content-user-list-item');
|
||||||
|
addNoteButton.textContent = '+ Add New Note';
|
||||||
|
addNoteButton.addEventListener('click', () => {
|
||||||
|
this.newNote(noteContainer, userButtons);
|
||||||
|
});
|
||||||
|
this.userList.appendChild(addNoteButton);
|
||||||
|
|
||||||
|
const searchField = document.createElement('input');
|
||||||
|
searchField.classList.add('twitch-notes-container-content-user-list-item');
|
||||||
|
searchField.classList.add('twitch-notes-container-content-user-list-item-search');
|
||||||
|
searchField.placeholder = 'Search...'
|
||||||
|
searchField.addEventListener('keyup', () => {
|
||||||
|
for(let row of userButtons) {
|
||||||
|
if (searchField.value === '') {
|
||||||
|
row.style.display = 'block';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(row.textContent?.includes(searchField.value)) {
|
||||||
|
row.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
row.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.userList.appendChild(searchField);
|
||||||
|
|
||||||
|
for (const user of NoteStorage.getSavedUserList()) {
|
||||||
|
const userButton = document.createElement('div');
|
||||||
|
userButton.classList.add('twitch-notes-container-content-user-list-item');
|
||||||
|
userButton.textContent = user;
|
||||||
|
userButtons.push(userButton);
|
||||||
|
this.userList.appendChild(userButton);
|
||||||
|
userButton.addEventListener('click', () => {
|
||||||
|
this.openNote(noteContainer, user, userButtons, userButton);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteContainer = document.createElement('div');
|
||||||
|
noteContainer.classList.add('twitch-notes-container-content-note-container');
|
||||||
|
|
||||||
|
if (NoteStorage.getSavedUserList().length) {
|
||||||
|
userButtons[0].click();
|
||||||
|
} else {
|
||||||
|
noteContainer.textContent = '';
|
||||||
|
const noNotes = document.createElement('div');
|
||||||
|
noNotes.classList.add('twitch-notes-container-content-note-container-note');
|
||||||
|
noNotes.innerHTML = 'You have not notes yet...';
|
||||||
|
noteContainer.appendChild(noNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
content.appendChild(this.userList);
|
||||||
|
content.appendChild(noteContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private newNote(noteContainer: HTMLDivElement, userButtons: HTMLDivElement[]) {
|
||||||
|
noteContainer.innerHTML = '';
|
||||||
|
for (let btn of userButtons) {
|
||||||
|
btn.style.background = 'transparent';
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteTitle = document.createElement('input');
|
||||||
|
noteTitle.classList.add('twitch-notes-container-header-title');
|
||||||
|
noteTitle.classList.add('twitch-notes-container-content-user-list-item-search');
|
||||||
|
noteTitle.style.marginTop = '1rem';
|
||||||
|
noteTitle.placeholder = 'Note title or username';
|
||||||
|
noteContainer.appendChild(noteTitle);
|
||||||
|
|
||||||
|
const note = document.createElement('div');
|
||||||
|
note.classList.add('twitch-notes-container-content-note-container-note');
|
||||||
|
note.setAttribute('contentEditable', 'true');
|
||||||
|
note.innerHTML = '';
|
||||||
|
|
||||||
|
noteContainer.appendChild(note);
|
||||||
|
|
||||||
|
const controls = document.createElement('div');
|
||||||
|
controls.classList.add('twitch-notes-container-content-note-container-controls');
|
||||||
|
noteContainer.appendChild(controls);
|
||||||
|
|
||||||
|
const save = new TwitchButton('Save');
|
||||||
|
controls.appendChild(save.element);
|
||||||
|
|
||||||
|
const savedNote = document.createElement('span');
|
||||||
|
savedNote.classList.add('twitch-notes-container-content-note-container-controls-saved')
|
||||||
|
savedNote.textContent = 'Note Saved!';
|
||||||
|
savedNote.style.opacity = '0';
|
||||||
|
controls.appendChild(savedNote);
|
||||||
|
|
||||||
|
save.onClick(() => {
|
||||||
|
const user = noteTitle.value.trim().toLowerCase();
|
||||||
|
const userButton = document.createElement('div');
|
||||||
|
userButton.classList.add('twitch-notes-container-content-user-list-item');
|
||||||
|
userButton.textContent = user;
|
||||||
|
userButtons.push(userButton);
|
||||||
|
this.userList?.appendChild(userButton);
|
||||||
|
userButton.addEventListener('click', () => {
|
||||||
|
this.openNote(noteContainer, user, userButtons, userButton);
|
||||||
|
});
|
||||||
|
userButtons.push(userButton);
|
||||||
|
NoteStorage.saveNote(user, note.innerHTML);
|
||||||
|
this.openNote(noteContainer, user, userButtons, userButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private openNote(noteContainer: HTMLDivElement, user: string, userButtons: HTMLDivElement[], userButton: HTMLDivElement) {
|
||||||
|
noteContainer.innerHTML = '';
|
||||||
|
for (let btn of userButtons) {
|
||||||
|
btn.style.background = 'transparent';
|
||||||
|
}
|
||||||
|
userButton.style.background = 'var(--color-twitch-purple-5)'
|
||||||
|
const noteTitle = document.createElement('div');
|
||||||
|
noteTitle.classList.add('twitch-notes-container-header-title');
|
||||||
|
noteTitle.textContent = user;
|
||||||
|
noteTitle.style.marginTop = '1rem';
|
||||||
|
noteContainer.appendChild(noteTitle);
|
||||||
|
|
||||||
|
const note = document.createElement('div');
|
||||||
|
note.classList.add('twitch-notes-container-content-note-container-note');
|
||||||
|
note.setAttribute('contentEditable', 'true');
|
||||||
|
note.innerHTML = NoteStorage.getNote(user);
|
||||||
|
|
||||||
|
noteContainer.appendChild(note);
|
||||||
|
|
||||||
|
const controls = document.createElement('div');
|
||||||
|
controls.classList.add('twitch-notes-container-content-note-container-controls');
|
||||||
|
noteContainer.appendChild(controls);
|
||||||
|
|
||||||
|
const save = new TwitchButton('Save');
|
||||||
|
controls.appendChild(save.element);
|
||||||
|
|
||||||
|
const savedNote = document.createElement('span');
|
||||||
|
savedNote.classList.add('twitch-notes-container-content-note-container-controls-saved')
|
||||||
|
savedNote.textContent = 'Note Saved!';
|
||||||
|
savedNote.style.opacity = '0';
|
||||||
|
controls.appendChild(savedNote);
|
||||||
|
|
||||||
|
save.onClick(() => {
|
||||||
|
NoteStorage.saveNote(user, note.innerHTML);
|
||||||
|
savedNote.style.opacity = '1';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private create() {
|
||||||
|
if (!this.element) {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.innerHTML = '';
|
||||||
|
|
||||||
|
this.element.classList.add('twitch-notes-blurred-background');
|
||||||
|
|
||||||
|
const closeButton = new TwitchCloseButton();
|
||||||
|
closeButton.onClick(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.classList.add('twitch-notes-center-floating-container');
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.classList.add('twitch-notes-container-header');
|
||||||
|
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.classList.add('twitch-notes-container-header-title');
|
||||||
|
title.textContent = 'Twitch Notes';
|
||||||
|
header.appendChild(title);
|
||||||
|
container.appendChild(header);
|
||||||
|
header.insertAdjacentElement("beforeend", closeButton.element);
|
||||||
|
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.classList.add('twitch-notes-container-content')
|
||||||
|
container.appendChild(content);
|
||||||
|
|
||||||
|
this.buildContent(content);
|
||||||
|
|
||||||
|
const footer = document.createElement('div');
|
||||||
|
footer.classList.add('twitch-notes-container-footer');
|
||||||
|
container.appendChild(footer);
|
||||||
|
|
||||||
|
const exportNotes = document.createElement('span');
|
||||||
|
exportNotes.classList.add('twitch-notes-link');
|
||||||
|
exportNotes.textContent = 'Export Notes';
|
||||||
|
exportNotes.addEventListener('click', () => {
|
||||||
|
NoteStorage.exportNotes();
|
||||||
|
})
|
||||||
|
footer.appendChild(exportNotes);
|
||||||
|
|
||||||
|
const importNotes = document.createElement('span');
|
||||||
|
importNotes.classList.add('twitch-notes-link');
|
||||||
|
importNotes.textContent = 'Import Notes';
|
||||||
|
importNotes.addEventListener('click', () => {
|
||||||
|
TwitchNotesImport.open(() => {
|
||||||
|
TwitchNotesWindow.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
footer.appendChild(importNotes);
|
||||||
|
|
||||||
|
const clearNotes = document.createElement('span');
|
||||||
|
clearNotes.classList.add('twitch-notes-link');
|
||||||
|
clearNotes.textContent = 'Clear Notes';
|
||||||
|
clearNotes.addEventListener('click', () => {
|
||||||
|
openClearDataWindow();
|
||||||
|
});
|
||||||
|
footer.appendChild(clearNotes);
|
||||||
|
|
||||||
|
this.element.appendChild(container);
|
||||||
|
document.body.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private close() {
|
||||||
|
if (this.element) this.element.remove();
|
||||||
|
this.element = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static update() {
|
||||||
|
TwitchNotesWindow.instance?.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static toggle() {
|
||||||
|
if (!TwitchNotesWindow.instance) {
|
||||||
|
TwitchNotesWindow.instance = new TwitchNotesWindow();
|
||||||
|
TwitchNotesWindow.instance.create();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TwitchNotesWindow.instance.element) {
|
||||||
|
TwitchNotesWindow.instance.close();
|
||||||
|
} else {
|
||||||
|
TwitchNotesWindow.instance.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
src/HTMLTemplates.ts
Normal file
7
src/HTMLTemplates.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function getCloseButtonSVG() {
|
||||||
|
return `<svg width="100%" height="100%" viewBox="0 0 20 20" x="0px" y="0px" class="twitch-notes-x-button">
|
||||||
|
<g>
|
||||||
|
<path d="M8.5 10L4 5.5 5.5 4 10 8.5 14.5 4 16 5.5 11.5 10l4.5 4.5-1.5 1.5-4.5-4.5L5.5 16 4 14.5 8.5 10z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
}
|
||||||
43
src/Mouse.ts
Normal file
43
src/Mouse.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import NoteContainers from "./NoteContainers";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default class Mouse {
|
||||||
|
|
||||||
|
static isMouseDown = false;
|
||||||
|
static mousePosition = {x: 0, y: 0};
|
||||||
|
static lastMousePosition = {x: 0, y: 0};
|
||||||
|
static listenersSetUp = false;
|
||||||
|
|
||||||
|
static setupListeners() {
|
||||||
|
if (!Mouse.listenersSetUp) {
|
||||||
|
document.addEventListener("mousemove", Mouse.handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", Mouse.handleMouseUp);
|
||||||
|
Mouse.listenersSetUp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static handleMouseUp() {
|
||||||
|
NoteContainers.activeContainer = null;
|
||||||
|
Mouse.isMouseDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateMousePosition(x: number, y: number) {
|
||||||
|
Mouse.lastMousePosition = {...this.mousePosition};
|
||||||
|
Mouse.mousePosition = {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
static handleMouseMove(event: MouseEvent) {
|
||||||
|
if (Mouse.isMouseDown) {
|
||||||
|
if (NoteContainers.activeContainer) {
|
||||||
|
const dx = Mouse.mousePosition.x - Mouse.lastMousePosition.x;
|
||||||
|
const dy = Mouse.mousePosition.y - Mouse.lastMousePosition.y;
|
||||||
|
NoteContainers.containers[NoteContainers.activeContainer].setStyles({
|
||||||
|
top: parseInt(NoteContainers.containers[NoteContainers.activeContainer].getElement().style.top) + dy + "px",
|
||||||
|
left: parseInt(NoteContainers.containers[NoteContainers.activeContainer].getElement().style.left) + dx + "px",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mouse.updateMousePosition(event.clientX, event.clientY);
|
||||||
|
}
|
||||||
|
};
|
||||||
17
src/NoteContainers.ts
Normal file
17
src/NoteContainers.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
|
||||||
|
export default class NoteContainers {
|
||||||
|
static activeContainer: string | null = null;
|
||||||
|
static containers: { [key: string]: HTMLBuilder } = {};
|
||||||
|
|
||||||
|
static addContainer(username: string, container: HTMLBuilder) {
|
||||||
|
if (NoteContainers.containers[username]) return;
|
||||||
|
NoteContainers.containers[username] = container;
|
||||||
|
};
|
||||||
|
|
||||||
|
static removeContainer(username: string) {
|
||||||
|
if (!NoteContainers.containers[username]) return;
|
||||||
|
NoteContainers.containers[username].remove();
|
||||||
|
delete NoteContainers.containers[username];
|
||||||
|
};
|
||||||
|
}
|
||||||
81
src/NoteStorage.ts
Normal file
81
src/NoteStorage.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import {LS_Prefix, LS_UserList} from './const';
|
||||||
|
|
||||||
|
export default class NoteStorage {
|
||||||
|
|
||||||
|
static inputData: {
|
||||||
|
users: string[],
|
||||||
|
notes: {
|
||||||
|
user: string,
|
||||||
|
note: string,
|
||||||
|
resolved?: boolean,
|
||||||
|
}[],
|
||||||
|
settings: any,
|
||||||
|
} = {
|
||||||
|
users: [],
|
||||||
|
notes: [],
|
||||||
|
settings: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
static getSavedUserList(): string[] {
|
||||||
|
return JSON.parse(localStorage.getItem(LS_UserList) || "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
static getNote(username: string): string {
|
||||||
|
return localStorage.getItem(LS_Prefix + username) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static addUserToSavedList(username: string) {
|
||||||
|
let userList = this.getSavedUserList();
|
||||||
|
|
||||||
|
if (!userList.includes(username)) {
|
||||||
|
userList.push(username);
|
||||||
|
localStorage.setItem(LS_UserList, JSON.stringify(userList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeUserFromSavedList(username: string) {
|
||||||
|
let userList = this.getSavedUserList();
|
||||||
|
userList = userList.filter(u => u !== username);
|
||||||
|
if (userList.length) {
|
||||||
|
localStorage.setItem(LS_UserList, JSON.stringify(userList));
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(LS_UserList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static saveNote(username: string, note: string) {
|
||||||
|
this.addUserToSavedList(username);
|
||||||
|
localStorage.setItem(LS_Prefix + username, note);
|
||||||
|
}
|
||||||
|
|
||||||
|
static deleteNote(username: string) {
|
||||||
|
this.removeUserFromSavedList(username);
|
||||||
|
localStorage.removeItem(LS_Prefix + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
static exportNotes() {
|
||||||
|
const dataObject = {
|
||||||
|
users: this.getSavedUserList(),
|
||||||
|
notes: this.getSavedUserList().map(user => {
|
||||||
|
return {user: user, note: this.getNote(user)};
|
||||||
|
}),
|
||||||
|
settings: {},
|
||||||
|
};
|
||||||
|
const data = JSON.stringify(dataObject)
|
||||||
|
|
||||||
|
const blob = new Blob([data], {type: 'text/json'});
|
||||||
|
const elem = window.document.createElement('a');
|
||||||
|
elem.href = window.URL.createObjectURL(blob);
|
||||||
|
elem.download = 'twitch-notes-' + (Date.now()) + '.json';
|
||||||
|
document.body.appendChild(elem);
|
||||||
|
elem.click();
|
||||||
|
document.body.removeChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static clearData() {
|
||||||
|
const users = this.getSavedUserList();
|
||||||
|
for(let user of users) {
|
||||||
|
this.deleteNote(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
|
import { ELEMENT_ButtonContainer } from "./const";
|
||||||
|
import toggleSettingsList from "./toggleSettingsList";
|
||||||
|
import openTwitchNote from "./openTwitchNote";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
import TwitchButton from "./HTMLElements/TwitchButton";
|
||||||
|
import TwitchNotesWindow from "./HTMLElements/TwitchNotesWindow";
|
||||||
|
|
||||||
export default class TwitchNote {
|
export default class TwitchNote {
|
||||||
|
|
||||||
observer: MutationObserver | null = null;
|
observer?: MutationObserver;
|
||||||
chatContainerNode?: Element;
|
chatContainerNode?: Element;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
init(chatContainerNode: Element) {
|
init(chatContainerNode: Element) {
|
||||||
this.chatContainerNode = chatContainerNode;
|
this.chatContainerNode = chatContainerNode;
|
||||||
console.log('CHAT CONTAINER INITIATED');
|
|
||||||
this.startObserver(chatContainerNode);
|
this.startObserver(chatContainerNode);
|
||||||
// add Notes management button
|
// add Notes management button
|
||||||
this.addNotesButton();
|
this.addNotesButton();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.addUserNoteBadges();
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
startObserver(targetNode: Element) {
|
startObserver(targetNode: Element) {
|
||||||
@@ -42,8 +48,65 @@ export default class TwitchNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addNotesButton() {
|
addNotesButton() {
|
||||||
|
const buttonContainer = document.querySelector(`.${ELEMENT_ButtonContainer}`);
|
||||||
|
if (!buttonContainer) {
|
||||||
|
setTimeout(() => this.addNotesButton(), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = new TwitchButton('Notes', 'outline');
|
||||||
|
button.onClick(TwitchNotesWindow.toggle);
|
||||||
|
|
||||||
|
if (buttonContainer.lastChild) {
|
||||||
|
buttonContainer.lastChild.insertBefore(
|
||||||
|
button.element,
|
||||||
|
buttonContainer.lastChild.lastChild
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
buttonContainer.appendChild(button.element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addUserNoteBadges() {
|
addUserNoteBadges() {
|
||||||
|
let nodes = document.getElementsByClassName("chat-line__message");
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const node = nodes.item(i);
|
||||||
|
if (!node) continue;
|
||||||
|
const n = node.querySelector(".chat-author__display-name");
|
||||||
|
if (!n) continue;
|
||||||
|
const usernameContainer = node.querySelector(".chat-line__username-container");
|
||||||
|
if (!usernameContainer) continue;
|
||||||
|
const username = n.getAttribute('data-a-user');
|
||||||
|
if (username) {
|
||||||
|
let noteButton = usernameContainer.querySelector(".twitch-note");
|
||||||
|
if (!noteButton) {
|
||||||
|
const twitchNote = new HTMLBuilder({
|
||||||
|
element: 'span',
|
||||||
|
class: 'twitch-note',
|
||||||
|
style: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
mouseClickEvent: () => openTwitchNote(username),
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'img',
|
||||||
|
style: {
|
||||||
|
height: '18px',
|
||||||
|
paddingRight: '4px'
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
src: 'https://cdn.rdarius.lt/icons/32-id-card.png'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
usernameContainer.insertBefore(
|
||||||
|
twitchNote.getElement(),
|
||||||
|
usernameContainer.firstChild
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
31
src/chatSettingsLine.ts
Normal file
31
src/chatSettingsLine.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import {HTMLBuilderBlock} from "./types";
|
||||||
|
|
||||||
|
export default function createChatSettingsLine(text: string, cb?: Function): HTMLBuilderBlock {
|
||||||
|
|
||||||
|
return {
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-settings-option-line',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes-settings-option-line-button',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-settings-option-line-button-container',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-settings-option-line-button-container-content',
|
||||||
|
content: [
|
||||||
|
text
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
mouseClickEvent: () => cb ? cb() : () => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
3
src/const.ts
Normal file
3
src/const.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const LS_UserList = "twitch-note-all-users-list";
|
||||||
|
export const LS_Prefix = "twitch-note-";
|
||||||
|
export const ELEMENT_ButtonContainer = "chat-input__buttons-container";
|
||||||
101
src/importNotesConflictResolve.ts
Normal file
101
src/importNotesConflictResolve.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import importNotesResult from "./importNotesResult";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
|
||||||
|
export default function importNotesConflictResolve(
|
||||||
|
user: string,
|
||||||
|
resultsContainer: HTMLElement,
|
||||||
|
container: HTMLElement,
|
||||||
|
blurredBackground: HTMLElement
|
||||||
|
) {
|
||||||
|
const valueStyle = {
|
||||||
|
minWidth: '320px',
|
||||||
|
padding: '6px',
|
||||||
|
border: '1px solid #FFFFFF19',
|
||||||
|
outline: 'none',
|
||||||
|
};
|
||||||
|
const valueAttr = {contentEditable: 'true'};
|
||||||
|
|
||||||
|
const diffBlurredBackground = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
content: [
|
||||||
|
'<strong>Select which note to keep for <em>' + user + '</em></strong><br /><em>You can modify notes to merge them and keep updated one</em><br /><br />',
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-grid-col-2',
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
content: [{
|
||||||
|
element: 'p',
|
||||||
|
content: [
|
||||||
|
'<strong>Locally saved note</strong>',
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
style: valueStyle,
|
||||||
|
attributes: valueAttr,
|
||||||
|
id: 'local-value-container',
|
||||||
|
content: [NoteStorage.getNote(user)]
|
||||||
|
},
|
||||||
|
'<br />',
|
||||||
|
{
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes_button',
|
||||||
|
content: ['Save this'],
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
content: [{
|
||||||
|
element: 'p',
|
||||||
|
content: [
|
||||||
|
'<strong>Locally saved note</strong>',
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
style: valueStyle,
|
||||||
|
attributes: valueAttr,
|
||||||
|
id: 'import-value-container',
|
||||||
|
content: [NoteStorage.inputData.notes.filter(n => n.user === user)[0].note || '']
|
||||||
|
},
|
||||||
|
'<br />',
|
||||||
|
{
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes_button',
|
||||||
|
content: ['Save this'],
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return diffBlurredBackground.getElement();
|
||||||
|
}
|
||||||
118
src/importNotesResult.ts
Normal file
118
src/importNotesResult.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import importNotesConflictResolve from "./importNotesConflictResolve";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
import TwitchButton from "./HTMLElements/TwitchButton";
|
||||||
|
import TwitchNotesImportResolve from "./HTMLElements/TwitchNotesImportResolve";
|
||||||
|
|
||||||
|
export default function importNotesResult(container: HTMLElement, blurredBackground: HTMLElement, cb?: Function) {
|
||||||
|
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 HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
style: {
|
||||||
|
padding: '1rem'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (willBeOverwritten.length) {
|
||||||
|
const overwrittenContainer = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
content: ['<strong>Note conflicts found for:</strong><br />'],
|
||||||
|
style: {
|
||||||
|
marginTop: '1rem',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let item of willBeOverwritten) {
|
||||||
|
const isResolved = NoteStorage.inputData.notes.find(note => note.user === item)?.resolved;
|
||||||
|
overwrittenContainer.addContent({
|
||||||
|
element: 'div',
|
||||||
|
content: [item + ' ', {
|
||||||
|
element: 'a',
|
||||||
|
content: [isResolved ? '[update]' : '[resolve]'],
|
||||||
|
style: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
TwitchNotesImportResolve.open(item, container, blurredBackground);
|
||||||
|
// importNotesConflictResolve(item, resultsContainer, container, blurredBackground);
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
style: {
|
||||||
|
color: isResolved ? 'green' : 'red',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resultsContainer.addContent(overwrittenContainer.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (willBeAdded.length) {
|
||||||
|
resultsContainer.addContent({
|
||||||
|
element: 'div',
|
||||||
|
content: ['<strong>Note will be added for:</strong><br />' + willBeAdded.join('<br />')],
|
||||||
|
style: {
|
||||||
|
marginTop: '1rem',
|
||||||
|
color: 'white'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noChanges.length) {
|
||||||
|
resultsContainer.addContent({
|
||||||
|
element: 'div',
|
||||||
|
content: ['<strong>No changes for:</strong><br />' + noChanges.join('<br />')],
|
||||||
|
style: {
|
||||||
|
marginTop: '1rem',
|
||||||
|
color: 'gray'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsContainer.addContent({element: 'div', style: {height: '24px'}});
|
||||||
|
|
||||||
|
willBeOverwritten = willBeOverwritten.filter(x => !NoteStorage.inputData.notes.find(n => n.user === x)?.resolved);
|
||||||
|
|
||||||
|
const saveButton = new TwitchButton('Complete import');
|
||||||
|
if (willBeOverwritten.length > 0) saveButton.element.style.background = 'gray';
|
||||||
|
if (willBeOverwritten.length > 0) saveButton.element.setAttribute('disabled', 'true');
|
||||||
|
|
||||||
|
resultsContainer.addContent(saveButton.element)
|
||||||
|
|
||||||
|
if (willBeOverwritten.length > 0) {
|
||||||
|
resultsContainer.addContent({
|
||||||
|
element: 'div',
|
||||||
|
content: ['<em>Import cannot be completed while there are unresolved conflicts</em>'],
|
||||||
|
style: {color: 'gray'}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
saveButton.element.addEventListener('click', () => {
|
||||||
|
for (let note of NoteStorage.inputData.notes) {
|
||||||
|
NoteStorage.saveNote(note.user, note.note);
|
||||||
|
}
|
||||||
|
blurredBackground.remove();
|
||||||
|
NoteStorage.inputData = {
|
||||||
|
users: [],
|
||||||
|
notes: [],
|
||||||
|
settings: {},
|
||||||
|
};
|
||||||
|
cb ? cb() : () => {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(resultsContainer.getElement());
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import './styles/main.scss';
|
import './styles/main.scss';
|
||||||
import TwitchNote from "./TwitchNote";
|
import TwitchNote from "./TwitchNote";
|
||||||
|
import Mouse from "./Mouse";
|
||||||
|
|
||||||
const ChatContainerClass = 'chat-scrollable-area__message-container';
|
const ChatContainerClass = 'chat-scrollable-area__message-container';
|
||||||
const twitchNote = new TwitchNote();
|
const twitchNote = new TwitchNote();
|
||||||
@@ -24,6 +25,7 @@ function init() {
|
|||||||
if (!chatExists) {
|
if (!chatExists) {
|
||||||
// chat container appeared
|
// chat container appeared
|
||||||
twitchNote.init(targetNode);
|
twitchNote.init(targetNode);
|
||||||
|
Mouse.setupListeners();
|
||||||
}
|
}
|
||||||
chatExists = true;
|
chatExists = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
149
src/openAllNoteListWindow.ts
Normal file
149
src/openAllNoteListWindow.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import {getCloseButtonSVG} from "./HTMLTemplates";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
|
||||||
|
export default function openAllNoteListWindow() {
|
||||||
|
let activeNote: string = '';
|
||||||
|
const blurredBackground = new HTMLBuilder({element: "div", class: 'twitch-notes-blurred-background'});
|
||||||
|
|
||||||
|
const noteSaved = new HTMLBuilder({
|
||||||
|
element: 'span',
|
||||||
|
content: ['Note saved!'],
|
||||||
|
style: {
|
||||||
|
color: 'green',
|
||||||
|
marginLeft: '2rem',
|
||||||
|
display: 'none'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'no-wrap',
|
||||||
|
width: '100%',
|
||||||
|
height: '320px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeButton = new HTMLBuilder({
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes-settings-close-button',
|
||||||
|
content: [getCloseButtonSVG()],
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '1rem',
|
||||||
|
right: '1rem',
|
||||||
|
},
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
blurredBackground.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const container = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-center-floating-container',
|
||||||
|
style: {
|
||||||
|
padding: '0',
|
||||||
|
paddingTop: '1rem',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '800px',
|
||||||
|
height: 'calc(320px + 5rem)',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-title',
|
||||||
|
content: ['Notes:'],
|
||||||
|
style: {
|
||||||
|
borderBottom: '1px solid #FFFFFF19',
|
||||||
|
paddingBottom: '1rem',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content.getElement(),
|
||||||
|
closeButton.getElement()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const userList = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-user-list'
|
||||||
|
});
|
||||||
|
|
||||||
|
const noteContainer = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
style: {
|
||||||
|
height: '320px',
|
||||||
|
padding: '1rem',
|
||||||
|
width: '100%',
|
||||||
|
opacity: '0',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const note = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
style: {
|
||||||
|
marginBottom: '3rem',
|
||||||
|
border: '1px solid #FFFFFF19',
|
||||||
|
height: 'calc(320px - 7rem)',
|
||||||
|
width: '100%',
|
||||||
|
padding: '6px',
|
||||||
|
outline: 'none',
|
||||||
|
overflowY: 'auto',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
contentEditable: 'true'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveNote = new HTMLBuilder({
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes-settings-button',
|
||||||
|
content: ['Save Note'],
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '1rem',
|
||||||
|
left: '1rem'
|
||||||
|
},
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
if (!activeNote) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NoteStorage.saveNote(activeNote, note.getElement().innerHTML);
|
||||||
|
noteSaved.getElement().style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let user of NoteStorage.getSavedUserList()) {
|
||||||
|
const userLine = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-user-list-user',
|
||||||
|
content: [user],
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
const list = document.getElementsByClassName('twitch-notes-user-list-user');
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
list.item(i)?.removeAttribute('data-active');
|
||||||
|
}
|
||||||
|
noteSaved.getElement().style.display = 'none';
|
||||||
|
userLine.addAttribute('data-active', 'true');
|
||||||
|
noteContainer.getElement().style.opacity = '1';
|
||||||
|
note.getElement().innerHTML = NoteStorage.getNote(user);
|
||||||
|
activeNote = user;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
userList.addContent(userLine.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
noteContainer.addContent(note.getElement());
|
||||||
|
noteContainer.addContent(saveNote.getElement());
|
||||||
|
noteContainer.addContent(noteSaved.getElement());
|
||||||
|
|
||||||
|
content.addContent(userList.getElement());
|
||||||
|
content.addContent(noteContainer.getElement());
|
||||||
|
|
||||||
|
blurredBackground.addContent(container.getElement());
|
||||||
|
document.body.appendChild(blurredBackground.getElement());
|
||||||
|
}
|
||||||
48
src/openClearDataWindow.ts
Normal file
48
src/openClearDataWindow.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
import TwitchNotesWindow from "./HTMLElements/TwitchNotesWindow";
|
||||||
|
|
||||||
|
export default function openClearDataWindow() {
|
||||||
|
const blurredBackground = new HTMLBuilder({
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-blurred-background',
|
||||||
|
content: [{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-center-floating-container',
|
||||||
|
style: {
|
||||||
|
padding: '1rem',
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
'<strong>This action will delete all saved notes!</strong><br /><br /><em>Consider exporting notes before performing this action in case you will need to use notes again</em>',
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
style: {
|
||||||
|
height: '24px',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes_button',
|
||||||
|
content: ['DELETE ALL NOTES'],
|
||||||
|
style: {
|
||||||
|
background: 'red'
|
||||||
|
},
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
NoteStorage.clearData();
|
||||||
|
blurredBackground.remove();
|
||||||
|
TwitchNotesWindow.update();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'button',
|
||||||
|
class: 'twitch-notes_button',
|
||||||
|
content: ['Cancel'],
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
blurredBackground.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
document.body.appendChild(blurredBackground.getElement());
|
||||||
|
}
|
||||||
73
src/openImprtNotesWindow.ts
Normal file
73
src/openImprtNotesWindow.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import {getCloseButtonSVG} from "./HTMLTemplates";
|
||||||
|
import importNotesResult from "./importNotesResult";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
|
||||||
|
// export default function openImportNotesWindow() {
|
||||||
|
// const blurredBackground = new HTMLBuilder({
|
||||||
|
// element: 'div',
|
||||||
|
// class: 'twitch-notes-blurred-background'
|
||||||
|
// });
|
||||||
|
// const container = new HTMLBuilder({
|
||||||
|
// element: 'div',
|
||||||
|
// class: 'twitch-notes-center-floating-container',
|
||||||
|
// style: {
|
||||||
|
// padding: '1rem',
|
||||||
|
// },
|
||||||
|
// content: [
|
||||||
|
// '<strong>WARNING!</strong> <em>This action might override existing data!</em><br /><br />',
|
||||||
|
// {
|
||||||
|
// element: 'button',
|
||||||
|
// class: 'twitch-notes-settings-close-button',
|
||||||
|
// content: [getCloseButtonSVG()],
|
||||||
|
// style: {
|
||||||
|
// position: 'absolute',
|
||||||
|
// top: '1rem',
|
||||||
|
// right: '1rem',
|
||||||
|
// },
|
||||||
|
// mouseClickEvent:() => {
|
||||||
|
// blurredBackground.remove();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// });
|
||||||
|
// const input = new HTMLBuilder({
|
||||||
|
// element: 'input',
|
||||||
|
// class: 'twitch-notes-file-input',
|
||||||
|
// attributes: {
|
||||||
|
// type: 'file',
|
||||||
|
// accept: 'json'
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// const inputBlock = new HTMLBuilder({
|
||||||
|
// element: 'div',
|
||||||
|
// content: ['Select exported twitch notes file<br /><br />', input.getElement()]
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// input.setChangeListener(() => {
|
||||||
|
// if (!(input?.getElement() as HTMLInputElement)?.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.setStyles({display: 'none'});
|
||||||
|
// importNotesResult(container, blurredBackground);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// reader.onerror = function () {
|
||||||
|
// console.error("error reading file");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// container.addContent(inputBlock.getElement());
|
||||||
|
// blurredBackground.addContent(container.getElement());
|
||||||
|
// document.body.appendChild(blurredBackground.getElement());
|
||||||
|
// }
|
||||||
67
src/openTwitchNote.ts
Normal file
67
src/openTwitchNote.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import NoteContainers from "./NoteContainers";
|
||||||
|
import Mouse from "./Mouse";
|
||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import { getCloseButtonSVG } from "./HTMLTemplates";
|
||||||
|
import {HTMLBuilderBlock} from "./types";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
|
||||||
|
export default function openTwitchNote(username: string) {
|
||||||
|
if (NoteContainers.containers[username]) return;
|
||||||
|
let noteContent = NoteStorage.getNote(username);
|
||||||
|
const container: HTMLBuilderBlock = {
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-floating-container',
|
||||||
|
style: {
|
||||||
|
left: Mouse.mousePosition.x + 'px',
|
||||||
|
top: Mouse.mousePosition.y + 10 + 'px'
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-header',
|
||||||
|
mouseDownEvent: () => {
|
||||||
|
NoteContainers.activeContainer = username;
|
||||||
|
Mouse.isMouseDown = true;
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
element: 'span',
|
||||||
|
class: 'twitch-notes-close-button',
|
||||||
|
content: [getCloseButtonSVG()],
|
||||||
|
mouseClickEvent: () => NoteContainers.removeContainer(username),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'span',
|
||||||
|
class: 'twitch-notes-title',
|
||||||
|
content: [username],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-content',
|
||||||
|
content: [NoteStorage.getNote(username)],
|
||||||
|
attributes: {
|
||||||
|
contentEditable: 'true'
|
||||||
|
},
|
||||||
|
keyUpEvent: (newContent: string) => {
|
||||||
|
noteContent = newContent;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: 'div',
|
||||||
|
class: 'twitch-notes-save-button',
|
||||||
|
content: ['SAVE'],
|
||||||
|
mouseClickEvent: () => {
|
||||||
|
NoteStorage.saveNote(username, noteContent);
|
||||||
|
NoteContainers.removeContainer(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerElement = new HTMLBuilder(container);
|
||||||
|
|
||||||
|
document.body.appendChild(containerElement.getElement());
|
||||||
|
NoteContainers.addContainer(username, containerElement);
|
||||||
|
}
|
||||||
@@ -1,79 +1,6 @@
|
|||||||
.twitch-note-container {
|
.twitch-notes {
|
||||||
position: fixed;
|
|
||||||
z-index: 1000000;
|
|
||||||
min-width: 420px;
|
|
||||||
max-width: 95vw;
|
|
||||||
min-height: 240px;
|
|
||||||
max-height: 50vh;
|
|
||||||
background: var(--color-background-base);
|
|
||||||
border: var(--border-width-default) solid var(--color-border-base) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-note-header {
|
&_button {
|
||||||
width: 100%;
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
|
||||||
position: relative;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-note-title {
|
|
||||||
color: var(--color-text-alt) !important;
|
|
||||||
font-size: var(--font-size-6) !important;
|
|
||||||
font-weight: var(--font-weight-semibold) !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
padding-left: calc((32px - var(--font-size-6)) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-note-close-button {
|
|
||||||
position: absolute;
|
|
||||||
right: 5px;
|
|
||||||
top: 5px;
|
|
||||||
width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
line-height: 22px;
|
|
||||||
text-align: center;
|
|
||||||
aspect-ratio: 1;
|
|
||||||
color: var(--color-text-alt) !important;
|
|
||||||
font-size: var(--font-size-6) !important;
|
|
||||||
font-weight: var(--font-weight-semibold) !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-note-content {
|
|
||||||
position: absolute;
|
|
||||||
top: 32px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 32px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--color-text-base) !important;
|
|
||||||
font-family: var(--font-base);
|
|
||||||
vertical-align: baseline;
|
|
||||||
outline: none;
|
|
||||||
overflow: auto;
|
|
||||||
padding: calc((32px - var(--font-size-6)) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-note-save-button {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 32px;
|
|
||||||
border-top: var(--border-width-default) solid var(--color-border-base) !important;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--color-text-alt) !important;
|
|
||||||
font-size: var(--font-size-6) !important;
|
|
||||||
font-weight: var(--font-weight-semibold) !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-button {
|
|
||||||
z-index: var(--z-index-default) !important;
|
z-index: var(--z-index-default) !important;
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
background-color: var(--color-background-button-primary-default);
|
background-color: var(--color-background-button-primary-default);
|
||||||
@@ -94,77 +21,15 @@
|
|||||||
font-size: var(--button-text-default);
|
font-size: var(--button-text-default);
|
||||||
height: var(--button-size-default);
|
height: var(--button-size-default);
|
||||||
padding: 0 var(--button-padding-x);
|
padding: 0 var(--button-padding-x);
|
||||||
|
|
||||||
|
&__outline {
|
||||||
|
background: transparent;
|
||||||
|
border: 2px solid var(--color-text-button-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-container {
|
|
||||||
border: 0;
|
|
||||||
font: inherit;
|
|
||||||
padding: 0;
|
|
||||||
vertical-align: baseline;
|
|
||||||
position: fixed;
|
|
||||||
z-index: var(--z-index-balloon);
|
|
||||||
inset: auto 0 0 auto;
|
|
||||||
margin: 0 69px 48px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-balloon {
|
&_close-button {
|
||||||
display: inline-block;
|
|
||||||
max-width: 90vw;
|
|
||||||
min-width: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-radius: 0.6rem !important;
|
|
||||||
background-color: var(--color-background-base) !important;
|
|
||||||
box-shadow: var(--shadow-elevation-2) !important;
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-popover {
|
|
||||||
white-space: normal;
|
|
||||||
width: 32rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-header {
|
|
||||||
display: flex !important;
|
|
||||||
-webkit-box-align: center !important;
|
|
||||||
align-items: center !important;
|
|
||||||
position: relative !important;
|
|
||||||
padding-left: 1rem !important;
|
|
||||||
padding-right: 1rem !important;
|
|
||||||
background-color: var(--color-background-base) !important;
|
|
||||||
min-height: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-header-left-element {
|
|
||||||
left: 0 !important;
|
|
||||||
margin-right: 0.5rem !important;
|
|
||||||
width: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-header-center-element {
|
|
||||||
-webkit-box-align: center !important;
|
|
||||||
align-items: center !important;
|
|
||||||
display: flex !important;
|
|
||||||
text-align: center !important;
|
|
||||||
-webkit-box-flex: 1 !important;
|
|
||||||
flex-grow: 1 !important;
|
|
||||||
-webkit-box-pack: center !important;
|
|
||||||
justify-content: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-header-center-element-content {
|
|
||||||
line-height: 1.5;
|
|
||||||
color: var(--color-text-alt) !important;
|
|
||||||
font-size: var(--font-size-5) !important;
|
|
||||||
font-weight: var(--font-weight-semibold) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.twitch-notes-settings-header-right-element {
|
|
||||||
right: 0 !important;
|
|
||||||
margin-left: 0.5rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-close-button {
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
text-indent: 0;
|
text-indent: 0;
|
||||||
@@ -183,7 +48,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: relative;
|
position: absolute;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
-webkit-box-align: center;
|
-webkit-box-align: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -195,68 +60,195 @@
|
|||||||
width: calc(var(--button-size-default) - 1rem);
|
width: calc(var(--button-size-default) - 1rem);
|
||||||
background-color: var(--color-background-button-text-default);
|
background-color: var(--color-background-button-text-default);
|
||||||
color: var(--color-fill-button-icon);
|
color: var(--color-fill-button-icon);
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
|
||||||
|
.twitch-notes_x-button {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: calc(var(--button-size-default) - 1rem);
|
||||||
|
height: calc(var(--button-size-default) - 1rem);
|
||||||
|
top: 0;
|
||||||
|
fill: currentcolor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-scrollable-area {
|
&-floating-container {
|
||||||
display: flex;
|
position: fixed;
|
||||||
overflow: hidden;
|
z-index: 1000000;
|
||||||
z-index: 0;
|
width: 320px;
|
||||||
height: 100%;
|
height: 240px;
|
||||||
position: relative;
|
background: var(--color-background-base);
|
||||||
max-height: 478px;
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-content {
|
&-container {
|
||||||
box-sizing: content-box;
|
position: fixed;
|
||||||
min-width: 100%;
|
z-index: 1000000;
|
||||||
overflow-x: hidden;
|
min-width: 500px;
|
||||||
overflow-y: auto;
|
max-width: 95vw;
|
||||||
max-height: inherit !important;
|
min-height: 240px;
|
||||||
margin: 1rem;
|
max-height: 80vh;
|
||||||
padding-bottom: 0 !important;
|
background: var(--color-background-base);
|
||||||
}
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
|
||||||
.twitch-notes-settings-option-line {
|
&-header {
|
||||||
position: relative !important;
|
|
||||||
width: calc(100% - 2rem) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.twitch-notes-settings-option-line-button {
|
|
||||||
border-radius: var(--border-radius-medium);
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: inherit;
|
height: calc(var(--button-size-default) + 1rem);
|
||||||
|
line-height: calc(var(--button-size-default) + 1rem);
|
||||||
|
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
margin-left: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-option-line-button:hover {
|
&-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 240px 1fr;
|
||||||
|
|
||||||
|
|
||||||
|
&-user-list {
|
||||||
|
border-right: 1px solid #FFFFFF19;
|
||||||
|
height: 100%;
|
||||||
|
max-height: calc(80vh - 2 * var(--button-size-default) + 1rem);
|
||||||
|
min-width: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
width: 100%;
|
||||||
color: inherit;
|
color: var(--color-text-alt) !important;
|
||||||
background-color: var(--color-background-interactable-hover);
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
|
||||||
|
&-search {
|
||||||
|
background: transparent;
|
||||||
|
outline: none;
|
||||||
|
color: var(--color-fill-button-icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-option-line-button-container {
|
&-note-container {
|
||||||
display: flex !important;
|
|
||||||
-webkit-box-align: center !important;
|
&-note {
|
||||||
align-items: center !important;
|
min-width: 420px;
|
||||||
position: relative !important;
|
min-height: 320px;
|
||||||
padding: 0.5rem !important;
|
display: block;
|
||||||
|
margin: 1rem;
|
||||||
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-settings-option-line-button-container-content {
|
&-controls {
|
||||||
-webkit-box-flex: 1 !important;
|
padding: 1rem;
|
||||||
flex-grow: 1 !important;
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
|
&-saved {
|
||||||
|
color: green;
|
||||||
|
padding-left: 1rem;
|
||||||
|
line-height: var(--button-size-default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-note-settings-separator {
|
&-footer {
|
||||||
border-top: 1px solid var(--color-border-base);
|
display: grid;
|
||||||
margin-top: 1rem !important;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
margin-left: 0.5rem !important;
|
width: 100%;
|
||||||
margin-right: 0.5rem !important;
|
border-top: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
padding-bottom: 1rem !important;
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-x-button {
|
&-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
position: relative;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--container-header-title {
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
padding-left: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
padding-left: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-close-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 5px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 32px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 32px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-base) !important;
|
||||||
|
font-family: var(--font-base);
|
||||||
|
vertical-align: baseline;
|
||||||
|
outline: none;
|
||||||
|
overflow: auto;
|
||||||
|
padding: calc((32px - var(--font-size-6)) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-save-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 32px;
|
||||||
|
border-top: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-text-alt) !important;
|
||||||
|
font-size: var(--font-size-6) !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-x-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -265,7 +257,7 @@
|
|||||||
fill: currentcolor;
|
fill: currentcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-blurred-background {
|
&-blurred-background {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0 0 0 0;
|
inset: 0 0 0 0;
|
||||||
z-index: 2000000;
|
z-index: 2000000;
|
||||||
@@ -273,25 +265,40 @@
|
|||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-center-floating-container {
|
&-center-floating-container {
|
||||||
background: var(--color-background-base);
|
background: var(--color-background-base);
|
||||||
border: var(--border-width-default) solid var(--color-border-base) !important;
|
border: var(--border-width-default) solid var(--color-border-base) !important;
|
||||||
padding: 4rem 2rem 2rem;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50vh;
|
top: 50vh;
|
||||||
left: 50vw;
|
left: 50vw;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
min-height: 320px;
|
||||||
|
min-width: 300px;
|
||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
max-width: 90vw;
|
max-width: 90vw;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitch-notes-user-list-user {
|
&-link {
|
||||||
padding: 1rem;
|
display: inline-block;
|
||||||
border-bottom: var(--border-width-default) solid var(--color-border-base) !important;
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
color: var(--color-text-link) !important;
|
||||||
|
line-height: var(--line-height-heading) !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
font-weight: var(--font-weight-semibold) !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.twitch-notes-user-list-user[data-active] {
|
|
||||||
background: #772ce833;
|
&-grid-col-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/toggleSettingsList.ts
Normal file
18
src/toggleSettingsList.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import NoteStorage from "./NoteStorage";
|
||||||
|
import openAllNoteListWindow from "./openAllNoteListWindow";
|
||||||
|
import openClearDataWindow from "./openClearDataWindow";
|
||||||
|
// import openImportNotesWindow from "./openImprtNotesWindow";
|
||||||
|
import createChatSettingsLine from "./chatSettingsLine";
|
||||||
|
import {HTMLBuilderBlock} from "./types";
|
||||||
|
import HTMLBuilder from "./HTMLBuilder";
|
||||||
|
import TwitchCloseButton from "./HTMLElements/TwitchCloseButton";
|
||||||
|
|
||||||
|
export default function toggleSettingsList() {
|
||||||
|
const settingsWindow = document.querySelector('.twitch-notes-settings-container') as HTMLElement;
|
||||||
|
if (settingsWindow) {
|
||||||
|
settingsWindow.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
14
src/types.ts
Normal file
14
src/types.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export type HTMLBuilderBlock = {
|
||||||
|
element: 'div' | 'span' | 'button' | 'a' | 'p' | 'strong' | 'em' | 'input' | 'img',
|
||||||
|
id?: string,
|
||||||
|
class?: string | string[],
|
||||||
|
style?: Partial<CSSStyleDeclaration>,
|
||||||
|
attributes?: {[key: string]: string},
|
||||||
|
content?: (HTMLBuilderBlock | string | HTMLElement)[],
|
||||||
|
mouseClickEvent?: Function,
|
||||||
|
mouseMoveEvent?: Function,
|
||||||
|
mouseDownEvent?: Function,
|
||||||
|
mouseUpEvent?: Function,
|
||||||
|
keyUpEvent?: Function,
|
||||||
|
changeListener?: Function,
|
||||||
|
}
|
||||||
@@ -4,13 +4,13 @@ const path = require('path');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
|
devtool: 'source-map',
|
||||||
entry: {
|
entry: {
|
||||||
twitchNotes: path.resolve(__dirname, 'src/index.ts'),
|
twitchNotes: path.resolve(__dirname, 'src/index.ts'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'extension'),
|
||||||
filename: "twitch-notes.js",
|
filename: "twitch-notes.js",
|
||||||
clean: true,
|
|
||||||
assetModuleFilename: "[name][ext]"
|
assetModuleFilename: "[name][ext]"
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
@@ -42,5 +42,5 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.js'],
|
extensions: ['.ts', '.js'],
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user