Compare commits

..

3 Commits

Author SHA1 Message Date
f748d0b771 2023-10-26 02:29:00 -07:00
d1de5350e1 2023-10-26 01:14:49 -07:00
826c7ccb73 . 2023-10-25 23:47:19 -07:00
6 changed files with 208 additions and 56 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
react
package.json
package-lock.json

View File

@ -17,7 +17,7 @@
</tr> </tr>
</table> </table>
<h1>Add emoji</h1> <h1>Add emoji</h1>
<input type="file" id="fileinput" accept="image/*" multiple /><input type="submit" id="submit" /> <input type="file" id="fileinput" accept="image/png,image/jpeg" multiple /><input type="submit" id="submit" />
<script src="edit.js" charset="UTF-8"></script> <script src="edit.js" charset="UTF-8"></script>
</body></html> </body></html>

17
edit.js
View File

@ -45,7 +45,7 @@ async function instantiateRow(url, internalId, animationStyle) {
c0.appendChild(img); c0.appendChild(img);
let select = document.createElement("select"); let select = document.createElement("select");
animationStyles.forEach(a => { ANIMATION_STYLES.forEach(a => {
let option = document.createElement("option"); let option = document.createElement("option");
option.innerText = a; option.innerText = a;
option.value = a.toLowerCase(); option.value = a.toLowerCase();
@ -59,20 +59,16 @@ async function instantiateRow(url, internalId, animationStyle) {
let button = document.createElement("button"); let button = document.createElement("button");
button.innerText = "🗑️"; button.innerText = "🗑️";
button.onclick = () => { button.onclick = async () => {
deleteEmoji(internalId); //if (!confirm("Delete emoji from storage. Are you sure?")) return;
var {emoji} = await chrome.storage.local.get("emoji");
emoji = emoji.filter(e => e.internalId != internalId);
await chrome.storage.local.set({emoji});
row.remove(); row.remove();
}; };
c2.appendChild(button); c2.appendChild(button);
} }
async function deleteEmoji(internalId) {
if (!confirm("Delete emoji from storage. Are you sure?")) return;
var {emoji} = await chrome.storage.local.get("emoji");
emoji = emoji.filter(e => e.internalId != internalId);
await chrome.storage.local.set({emoji});
}
async function setAnimationStyle(internalId, animationStyle) { async function setAnimationStyle(internalId, animationStyle) {
var {emoji} = await chrome.storage.local.get("emoji"); var {emoji} = await chrome.storage.local.get("emoji");
var e = emoji.find(e => e.internalId == internalId); var e = emoji.find(e => e.internalId == internalId);
@ -98,6 +94,7 @@ submit.onclick = async () => {
var {emoji} = await chrome.storage.local.get("emoji"); var {emoji} = await chrome.storage.local.get("emoji");
emoji = emoji.concat(newEmoji); emoji = emoji.concat(newEmoji);
await chrome.storage.local.set({emoji}); await chrome.storage.local.set({emoji});
fileinput.value = "";
submit.disabled = false; submit.disabled = false;
}; };

View File

@ -25,8 +25,5 @@
"background": { "background": {
"service_worker": "background.js", "service_worker": "background.js",
"type": "module" "type": "module"
},
"action": {
"default_popup": "toggle.html"
} }
} }

View File

@ -1,39 +1,51 @@
<!DOCTYPE html><html><head> <!DOCTYPE html><html><head>
<meta charset="UTF-8" />
<style> <style>
#emojigrid { #emojigrid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
width: 269px;
} }
.emojisquare { .emojisquare {
width: 50px;
height: 50px;
border: 1px solid gray; border: 1px solid gray;
margin: 1px; margin: 1px;
padding: 4px;
}
.emojisquare .imgcontainer {
width: 64px;
height: 64px;
margin: auto;
display: flex; display: flex;
} }
.emojisquare img { .emojisquare .imgcontainer img {
width: 48px; max-width: 100%;
height: 48px; max-height: 100%;
margin: auto; object-fit: contain;
} }
.enabled {
[data-state=enabled] {
background-color:rgba(0, 255, 0, 0.5); background-color:rgba(0, 255, 0, 0.5);
} }
.disabled { [data-state=disabled] {
background-color:rgba(255, 0, 0, 0.5); background-color:rgba(255, 0, 0, 0.5);
} }
.pending { [data-state=pending] {
background-color:rgba(255, 255, 0, 0.5); background-color:rgba(255, 255, 0, 0.5);
} }
#emojiadd {
margin-top: 8px;
}
#errorDiv { #errorDiv {
color: red; color: red;
} }
</style> </style>
</head><body> </head><body>
<h1>VRChat Emoji Manager</h1>
<div id="emojigrid"></div> <div id="emojigrid"></div>
<div id="errorDiv"></div> <div id="errorDiv"></div>
<div id="emojiadd">Add more!!! <input type="file" id="fileinput" accept="image/png,image/jpeg" multiple /> <select id="default_animation_style_select" value="aura"><option value="aura">Aura</option><option value="bats">Bats</option><option value="bees">Bees</option><option value="bounce">Bounce</option><option value="cloud">Cloud</option><option value="confetti">Confetti</option><option value="crying">Crying</option><option value="dislike">Dislike</option><option value="fire">Fire</option><option value="idea">Idea</option><option value="lasers">Lasers</option><option value="like">Like</option><option value="magnet">Magnet</option><option value="mistletoe">Mistletoe</option><option value="money">Money</option><option value="noise">Noise</option><option value="orbit">Orbit</option><option value="pizza">Pizza</option><option value="rain">Rain</option><option value="rotate">Rotate</option><option value="shake">Shake</option><option value="snow">Snow</option><option value="snowball">Snowball</option><option value="spin">Spin</option><option value="splash">Splash</option><option value="stop">Stop</option><option value="zzz">ZZZ</option></select> <input type="submit" id="submit" value="Add" /></div>
<div><button id="deletemodebtn">Delete Emojis</button></div>
<script src="toggle.js"></script> <script src="toggle.js"></script>
</body></html> </body></html>

200
toggle.js
View File

@ -1,3 +1,92 @@
const ANIMATION_STYLES = `Aura
Bats
Bees
Bounce
Cloud
Confetti
Crying
Dislike
Fire
Idea
Lasers
Like
Magnet
Mistletoe
Money
Noise
Orbit
Pizza
Rain
Rotate
Shake
Snow
Snowball
Spin
Splash
Stop
ZZZ`.split('\n');
function createAnimationStyleSelect() {
let select = document.createElement("select");
ANIMATION_STYLES.forEach(a => {
let option = document.createElement("option");
option.innerText = a;
option.value = a.toLowerCase();
select.appendChild(option);
});
return select;
}
submit.onclick = async () => {
submit.disabled = true;
var newEmoji = [];
for (let file of fileinput.files) {
let e = {
internalId: randomId(),
animationStyle: default_animation_style_select.value,
};
let data = await fileToDataURL(file);
//todo convert/resize
await chrome.storage.local.set({["data-"+e.internalId]: data});
newEmoji.push(e);
instantiateRow(data, e.internalId, e.animationStyle);
}
var {emoji} = await chrome.storage.local.get("emoji");
emoji = emoji.concat(newEmoji);
await chrome.storage.local.set({emoji});
fileinput.value = "";
submit.disabled = false;
};
var deleteMode = false;
deletemodebtn.onclick = () => {
if (deleteMode) {
deleteMode = false;
deletemodebtn.innerText = "Delete Emojis";
return;
}
alert("Delete mode activated, click emojis to delete.");
deleteMode = true;
deletemodebtn.innerText = "CANCEL DELETE MODE";
};
loadEmojis().catch(displayError); loadEmojis().catch(displayError);
async function loadEmojis() { async function loadEmojis() {
@ -6,15 +95,26 @@ async function loadEmojis() {
if (!store.emoji) throw new Error("No emoji."); if (!store.emoji) throw new Error("No emoji.");
for (let emoji of store.emoji) { for (let emoji of store.emoji) {
let div = document.createElement("div"); let div = document.createElement("div");
div.className = "emojisquare";
div.dataset.animationStyle = emoji.animationStyle; div.dataset.animationStyle = emoji.animationStyle;
div.dataset.internalId = emoji.internalId; div.dataset.internalId = emoji.internalId;
div.dataset.currentId = emoji.currentId; div.dataset.currentId = emoji.currentId;
div.classList.add("emojisquare");
div.onclick = toggleEmoji; div.onclick = toggleEmoji;
let imgdiv = document.createElement("div");
imgdiv.className = "imgcontainer";
let img = document.createElement("img"); let img = document.createElement("img");
img.src = store["data-" + emoji.internalId]; img.src = store["data-" + emoji.internalId];
div.appendChild(img); imgdiv.appendChild(img);
div.appendChild(imgdiv);
let select = createAnimationStyleSelect();
select.value = emoji.animationStyle;
select.onclick = event => event.stopPropagation();
select.onchange = event => {
setAnimationStyle(emoji.internalId, event.target.value);
};
div.appendChild(select);
emojigrid.appendChild(div); emojigrid.appendChild(div);
} }
@ -22,14 +122,13 @@ async function loadEmojis() {
} }
async function loadToggleState() { async function loadToggleState() {
console.debug("loadToggleState");
var elements = document.querySelectorAll(".emojisquare"); var elements = document.querySelectorAll(".emojisquare");
if (elements.length === 0) return; if (elements.length === 0) return;
var currentEmojis = await callContentScript("getEmojis"); var currentEmojis = await callContentScript("getEmojis");
var active = currentEmojis?.map(e => e.id); var active = currentEmojis?.map(e => e.id);
elements.forEach(e => { elements.forEach(e => {
var yes = active.includes(e.dataset.currentId); e.dataset.state = active.includes(e.dataset.currentId) ? "enabled" : "disabled";
e.classList.add(yes ? "enabled" : "disabled");
e.classList.remove(yes ? "disabled" : "enabled");
}); });
} }
@ -61,44 +160,87 @@ async function callContentScript(method, ...args) {
}; };
async function toggleEmoji(event) { async function toggleEmoji(event) {
if (this.classList.contains("pending")) return; if (this.dataset.state == "pending") return;
this.classList.add("pending"); var selectedState = this.dataset.state;
this.dataset.state = "pending";
errorDiv.innerText = ""; errorDiv.innerText = "";
if (document.querySelectorAll(".enabled").length + document.querySelectorAll(".disabled").length < document.querySelectorAll(".emojisquare").length) { if (emojigrid.querySelector(".emojisquare:not([data-state])")) {
try { try {
await loadToggleState(); await loadToggleState();
} catch (error) { } catch (error) {
displayError(error); displayError(error);
this.classList.remove("pending"); this.dataset.state = selectedState;
return; return;
} }
} }
if (this.classList.contains("enabled")) { if (selectedState == "enabled") {
// disable // disable
callContentScript("deleteEmoji", this.dataset.currentId).then(() => { callContentScript("deleteEmoji", this.dataset.currentId).then(() => {
this.classList.remove("pending"); this.dataset.state = "disabled";
this.classList.remove("enabled");
this.classList.add("disabled");
}).catch(error => { }).catch(error => {
this.classList.remove("pending"); this.dataset.state = selectedState;
displayError(error);
});
} else {
// enable
callContentScript("createEmoji", this.querySelector("img").src, this.dataset.animationStyle).then(newEmoji => {
chrome.storage.local.get("emoji").then(({emoji}) => {
emoji.find(e => e.internalId == this.dataset.internalId).currentId = newEmoji.id;
chrome.storage.local.set({emoji});
this.dataset.currentId = newEmoji.id;
this.classList.remove("pending");
this.classList.remove("disabled");
this.classList.add("enabled");
});
}).catch(error => {
this.classList.remove("pending");
displayError(error); displayError(error);
}); });
if (!deleteMode) return;
} }
if (deleteMode) {
let {emoji} = await chrome.storage.local.get("emoji");
emoji = emoji.filter(e => e.internalId != this.dataset.internalId);
await chrome.storage.local.set({emoji});
this.remove();
return;
}
// enable
callContentScript("createEmoji", this.querySelector("img").src, this.dataset.animationStyle).then(newEmoji => {
chrome.storage.local.get("emoji").then(({emoji}) => {
emoji.find(e => e.internalId == this.dataset.internalId).currentId = newEmoji.id;
chrome.storage.local.set({emoji});
this.dataset.currentId = newEmoji.id;
this.dataset.state = "enabled";
});
}).catch(error => {
this.dataset.state = selectedState;
displayError(error);
});
}
async function setAnimationStyle(internalId, animationStyle) {
var {emoji} = await chrome.storage.local.get("emoji");
var e = emoji.find(e => e.internalId == internalId);
e.animationStyle = animationStyle;
await chrome.storage.local.set({emoji});
}
function fileToDataURL(file) {
return new Promise(function(resolve, reject){
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
});
}
function randomId() {
let id = "";
const CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 16; i++) {
id += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return id;
} }