710 lines
22 KiB
HTML
710 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<meta charset="UTF-8"/>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<!--
|
||
<script src="../../../SpaccDotWeb/SpaccDotWeb.js" module="SpaccDotWeb"></script>
|
||
<script src="//SpaccInc.gitlab.io/SpaccDotWeb/SpaccDotWeb.js" module="SpaccDotWeb"></script>
|
||
-->
|
||
<script src="../../../SpaccDotWeb/SpaccDotWeb.Alt.js" module="SpaccDotWeb"></script>
|
||
<script src="//SpaccInc.gitlab.io/SpaccDotWeb/SpaccDotWeb.Alt.js" module="SpaccDotWeb"></script>
|
||
<script src="https://googlechrome.github.io/dialog-polyfill/dist/dialog-polyfill.js"></script>
|
||
<link rel="stylesheet" href="./paper.min.css"/>
|
||
|
||
<script module="Meta">({
|
||
Name: "🃏️ [Matrix] Sticker Helper",
|
||
})</script>
|
||
|
||
<script module="Main" type="module">
|
||
//import { html, render } from 'https://esm.sh/htm/preact/standalone';
|
||
//import axios from 'https://cdn.skypack.dev/axios';
|
||
|
||
const Spacc = SpaccDotWeb.AppInit();
|
||
|
||
const State = {};
|
||
const Defaults = {
|
||
stickerSelectorUrl: "https://maunium.net/stickers-demo/",
|
||
appIdentity: "org.eu.octt.MatrixStickerHelper",
|
||
appInterface: "v1",
|
||
Strings: {
|
||
mustInitMessage: `Your account must first be initialized to handle stickers.`,
|
||
notManagedMessage: `
|
||
Your account is set-up with a sticker picker, but it's not marked as being managed by this app.
|
||
This could mean that you are currently using an incompatible sticker picker.
|
||
You can try to continue anyway if you think it should work, otherwise you should reinitialize sticker data.
|
||
`,
|
||
}
|
||
};
|
||
|
||
let Config = localStorage.getItem('SpaccInc-Matrix-Config');
|
||
if (Config) Config = JSON.parse(Config);
|
||
else Config = {
|
||
accounts: [],
|
||
};
|
||
Config.Save = () => localStorage.setItem('SpaccInc-Matrix-Config', JSON.stringify(Config));
|
||
|
||
/*
|
||
function ActionForm (props) {
|
||
let formElems = [];
|
||
|
||
for (const action of props.actionEntries) {
|
||
action.options ||= [];
|
||
|
||
formElems.push(html`
|
||
<button
|
||
style=${{ textAlign: 'left', width: `calc(100% - ${action.options.length * 3}em)`, display: 'inline-block' }}
|
||
dataIndex=${action.dataIndex}
|
||
onclick=${action.onclick}
|
||
disabled=${action.disabled}
|
||
name=${action.name}
|
||
>
|
||
${action.label}
|
||
</button>
|
||
`)
|
||
|
||
for (const option of action.options) {
|
||
formElems.push(html`
|
||
<button
|
||
style=${{ width: '3em', display: 'inline-block' }}
|
||
dataIndex=${option.dataIndex}
|
||
onclick=${option.onclick}
|
||
>
|
||
${option.label}
|
||
</button>
|
||
`)
|
||
}
|
||
}
|
||
|
||
return html`<div class="ActionForm">
|
||
${formElems}
|
||
</div>`
|
||
}
|
||
|
||
function TableForm (props) {
|
||
let tableRows = [];
|
||
|
||
for (const entry of props.formEntries) {
|
||
tableRows.push(html`<tr>
|
||
<td>
|
||
${entry.type !== 'button' ? entry.label : ''}
|
||
</td>
|
||
<td style=${{ width: '100%' }}>
|
||
<input
|
||
style=${{ width: '100%' }}
|
||
name=${entry.name || entry.label.toLowerCase()}
|
||
type=${entry.type}
|
||
placeholder=${entry.placeholder || entry.value}
|
||
value=${entry.type !== 'button' ? entry.value : (entry.value || entry.label)}
|
||
onclick=${entry.onclick || entry.onInteract}
|
||
onselect=${entry.onInteract}
|
||
onchange=${entry.onInteract}
|
||
oninput=${entry.onInteract}
|
||
onpaste=${entry.onInteract}
|
||
/>
|
||
</td>
|
||
</tr>`)
|
||
}
|
||
}
|
||
|
||
function ClickImg (props) {
|
||
return html`<div class="ClickImg">
|
||
<img
|
||
src=${props.src}
|
||
onclick=${() => {
|
||
for (const openImg of document.querySelectorAll('.ClickImg[data-opened="1"]')) {
|
||
if (openImg !== this.base ) {
|
||
openImg.dataset.opened = '0';
|
||
}
|
||
}
|
||
const isOpened = this.base.dataset.opened;
|
||
if (isOpened === '1') {
|
||
this.base.dataset.opened = '0';
|
||
} else {
|
||
this.base.dataset.opened = '1';
|
||
}
|
||
}}
|
||
/>
|
||
<${SecureButton}
|
||
name="delete"
|
||
modalText='❌️ Confirm delete?'
|
||
onclick=${() => {
|
||
alert(1)
|
||
}}
|
||
>
|
||
❌️
|
||
</${SecureButton}>
|
||
</div>`
|
||
}
|
||
|
||
async function TryMatrixLoginAndSaveAndUse (loginData) {
|
||
try {
|
||
const response = await axios.post(`${loginData.homeserver}/_matrix/client/v3/login`, {
|
||
type: "m.login.password",
|
||
identifier: {
|
||
type: "m.id.user",
|
||
user: loginData.username,
|
||
},
|
||
password: loginData.password,
|
||
});
|
||
State.account = {
|
||
homeserver: loginData.homeserver,
|
||
username: loginData.username,
|
||
token: response.data.access_token,
|
||
};
|
||
Config.accounts.push(State.account);
|
||
Config.Save();
|
||
Render(CollectionScreen);
|
||
} catch(err) {
|
||
alert(`Error trying to get a session token: ${err.response.request.response}`);
|
||
document.querySelector('#AccountLoginForm input[name="loginSave"]').disabled = false;
|
||
}
|
||
}
|
||
|
||
async function DataWidgetSaveRequest (configUrl) {
|
||
document.querySelector('input[name="reinit"]').disabled = true;
|
||
document.querySelector('button[name="commit"]').disabled = true;
|
||
State.dataWidgets.stickerpicker = {
|
||
content: {
|
||
type: "m.stickerpicker",
|
||
url: `${document.querySelector('input[name="sticker selector url"]').value}?&config=${configUrl || ''}&theme=$theme`,
|
||
name: "Stickerpicker",
|
||
creatorUserId: `${GetMatrixUserTag(State.account)}`,
|
||
managedBy: [
|
||
`${Defaults.appIdentity}/${Defaults.appInterface}`,
|
||
],
|
||
},
|
||
sender: `${GetMatrixUserTag(State.account)}`,
|
||
state_key: "stickerpicker",
|
||
type: "m.widget",
|
||
id: "stickerpicker",
|
||
};
|
||
try {
|
||
const response = await axios.put(`${State.account.homeserver}/_matrix/client/v3/user/${GetMatrixUserTag(State.account)}/account_data/m.widgets`, State.dataWidgets, {
|
||
headers: { "Authorization": `Bearer ${State.account.token}` },
|
||
});
|
||
console.log(response.data);
|
||
Render(CollectionScreen);
|
||
} catch(err) {
|
||
alert(`Error updating account widget data: ${err.response.request.response}`);
|
||
document.querySelector('button[name="commit"]').disabled = false;
|
||
}
|
||
document.querySelector('input[name="reinit"]').disabled = false;
|
||
}
|
||
|
||
function AccountChoice (props) {
|
||
let accountsActions = [];
|
||
|
||
for (let i=0; i<Config.accounts.length; i++) {
|
||
let account = Config.accounts[i];
|
||
|
||
accountsActions.push({
|
||
label: `🐱️ ${GetMatrixUserTag(account)}`,
|
||
onclick: () => {
|
||
State.account = account;
|
||
Render(CollectionScreen);
|
||
},
|
||
options: [ {
|
||
dataIndex: i,
|
||
label: '❌️',
|
||
onclick: () => {
|
||
Config.accounts.pop(this.dataIndex);
|
||
Config.Save();
|
||
Render();
|
||
},
|
||
} ]
|
||
})
|
||
}
|
||
|
||
return html`
|
||
<${ActionForm} actionEntries=${[...accountsActions,
|
||
{ label: '🆕️ Add new account', onclick: () => Render(AccountLogin) },
|
||
]}/>
|
||
`
|
||
}
|
||
|
||
function AccountLogin (props) {
|
||
return html`<Fragment>
|
||
<p>
|
||
Please login with your [Matrix] account.
|
||
(Your login details are processed locally and only sent to the homeserver you specified.)
|
||
</p>
|
||
<${TableForm} id="AccountLoginForm" formEntries=${[
|
||
{ label: 'Homeserver', type: 'text', value: 'https://matrix.org' },
|
||
{ label: 'Username', type: 'text', placeholder: 'AzureDiamond' },
|
||
{ label: 'Password', type: 'password', placeholder: '*******' },
|
||
{ label: 'Import via session token', type: 'checkbox' },
|
||
{ label: '🆕️ Login and Save', type: 'button', onclick: () => {
|
||
this.base.querySelector('input[name="loginSave"]').disabled = true;
|
||
const password = this.base.querySelector('input[name="password"]').value;
|
||
let loginData = {
|
||
homeserver: this.base.querySelector('input[name="homeserver"]').value,
|
||
username: this.base.querySelector('input[name="username"]').value,
|
||
};
|
||
if (this.base.querySelector('input[name="import via session token"]').checked) {
|
||
loginData.token = password;
|
||
Config.accounts.push(loginData);
|
||
Config.Save();
|
||
Render(CollectionScreen);
|
||
} else {
|
||
loginData.password = password;
|
||
TryMatrixLoginAndSaveAndUse(loginData);
|
||
}
|
||
}, name: 'loginSave' },
|
||
...(Config.accounts.length > 0
|
||
? [{ label: '✖️ Cancel', type: 'button', onclick: () => Render() }]
|
||
: [])
|
||
]}/>
|
||
</Fragment>`
|
||
}
|
||
|
||
function CollectionList (props) {
|
||
let packElems = [];
|
||
for (const pack of (props.extras?.dataPacks?.packs || [])) {
|
||
packElems.push(html`
|
||
<img
|
||
src="${props.extras?.dataPacks?.homeserver_url}/_matrix/media/r0/thumbnail/${pack.stickers[0].info.thumbnail_url.split('mxc://')[1]}?&height=128&width=128&method=scale"
|
||
onclick=${null}
|
||
/>
|
||
`);
|
||
}
|
||
|
||
return html`<${CollectionViewMenu} optionsOpen=${false}>
|
||
<div id="CollectionList"><div>
|
||
<button style=${{ verticalAlign: 'top', height: '64px' }}>
|
||
➕️ Create new pack
|
||
</button>
|
||
${packElems}
|
||
</div></div>
|
||
<${CollectionGrid}/>
|
||
</${CollectionViewMenu}>`
|
||
}
|
||
|
||
function CollectionGrid (props) {
|
||
return html`<div id="CollectionGrid">
|
||
<${ClickImg} src="https://http.cat/100"/>
|
||
<${ClickImg} src="https://http.cat/101"/>
|
||
<${ClickImg} src="https://http.cat/102"/>
|
||
<${ClickImg} src="https://http.cat/103"/>
|
||
<${ClickImg} src="https://http.cat/200"/>
|
||
<${ClickImg} src="https://http.cat/201"/>
|
||
<${ClickImg} src="https://http.cat/202"/>
|
||
<${ClickImg} src="https://http.cat/203"/>
|
||
<${ClickImg} src="https://http.cat/204"/>
|
||
<button
|
||
style=${{ verticalAlign: 'top', width: '128px', height: '128px' }}
|
||
onclick=${() => this.base.querySelector('button > input[type="file"]').click()}
|
||
>
|
||
➕️ Upload new
|
||
<br/><br/>
|
||
<small>(Only image files supported for now)</small>
|
||
<input
|
||
style=${{ display: 'none' }}
|
||
type="file"
|
||
accept="image/jpeg,image/gif,image/png,image/webp"
|
||
multiple
|
||
onchange=${(data) => {
|
||
console.log(data.target.files);
|
||
document.querySelector('button[name="commit"]').disabled = false;
|
||
}}
|
||
/>
|
||
</button>
|
||
</div>`
|
||
}
|
||
|
||
function CollectionViewMenu (props) {
|
||
return html`<Fragment class="CollectionViewMenu">
|
||
<${ActionForm} actionEntries=${[
|
||
{ label: '🔙️ Go Back', onclick: () => Render() },
|
||
{ name: 'commit', label: '📝️ Commit Changes', disabled: true, onclick: () => DataWidgetSaveRequest(packsUrl) },
|
||
]}/>
|
||
${props.children}
|
||
<details open=${props.optionsOpen}>
|
||
<summary>
|
||
<h3 style=${{ display: 'inline' }}>
|
||
Options
|
||
</h3>
|
||
</summary>
|
||
<p>
|
||
Reinitializing sticker data for your account will simply override
|
||
the <code>stickerpicker</code> subfield in its <code>m.widgets</code> field.
|
||
</p>
|
||
<${TableForm} formEntries=${[
|
||
{ name: 'reinit', label: '💡️ Reinitialize sticker data as new', type: 'button', onclick: () => DataWidgetSaveRequest(this.base.querySelector('input[name="import json from url"]').value) },
|
||
{ label: 'or', type: 'hidden' },
|
||
{ label: 'Import JSON from URL', placeholder: 'https://example.com', type: 'text', onInteract: () => {
|
||
const thisElem = this.base.querySelector('input[name="import json from url"]');
|
||
const button = this.base.querySelector('input[name="reinit"]');
|
||
if (thisElem.value === '') {
|
||
button.value = '💡️ Reinitialize sticker data as new';
|
||
} else {
|
||
button.value = '💡️ Reinitialize sticker packs from URL';
|
||
}
|
||
} },
|
||
]}/>
|
||
<details open=${false}>
|
||
<summary>
|
||
<h4 style=${{ display: 'inline' }}>
|
||
Advanced
|
||
</h4>
|
||
</summary>
|
||
<${TableForm} formEntries=${[
|
||
{ label: 'Sticker selector URL', value: Defaults.stickerSelectorUrl, type: 'text' },
|
||
]}/>
|
||
</details>
|
||
</details>
|
||
</Fragment>`
|
||
}
|
||
*/
|
||
|
||
const $ = (query, ...params) => (query
|
||
? document.querySelector(Array.isArray(query)
|
||
? (params.length > 0
|
||
? params.map((a, i) => `${query[i]}${a}`).join('')
|
||
: query.join(''))
|
||
: query)
|
||
: document);
|
||
|
||
const GetMatrixUserTag = (account) => `@${account.username}:${account.homeserver.split('://')[1]}`;
|
||
|
||
const GetMatrixMediaUrl = (mxcId, homeserverUrl) => `${homeserverUrl}/_matrix/media/r0/thumbnail/${mxcId?.split('mxc://')[1]}`;
|
||
|
||
function ResetLayouts () {
|
||
$`#Main`.hidden = false;
|
||
for (const id of ['LayoutInfo', 'LayoutAccountSelect', 'LayoutPacksList', 'LayoutPackGrid']) {
|
||
$`#${id}`.innerHTML = '';
|
||
}
|
||
for (const id of ['LayoutAccountLogin', 'LayoutCollectionActions', 'LayoutCollectionOptions']) {
|
||
$`#${id}`.hidden = true;
|
||
}
|
||
for (const id of ['LayoutCollectionOptions']) {
|
||
$`#${id}`.open = false;
|
||
}
|
||
}
|
||
|
||
function RegisterLayouts () {
|
||
$`#LayoutCollectionActions button[name="back"]`.onclick = () => DisplayAccountSelectLogin();
|
||
$`#LayoutCollectionOptions input[name="pickerUrl"]`.value = Defaults.stickerSelectorUrl;
|
||
}
|
||
|
||
async function RequestAccountWidgetsData (account, postData) {
|
||
const request = await fetch(`${account.homeserver}/_matrix/client/v3/user/${GetMatrixUserTag(account)}/account_data/m.widgets`, {
|
||
method: (postData ? "PUT" : "GET"),
|
||
headers: { Authorization: `Bearer ${account.token}` },
|
||
body: JSON.stringify(postData),
|
||
});
|
||
const result = await request.json();
|
||
if (request.status === 200 || result.errcode === 'M_NOT_FOUND') {
|
||
return result;
|
||
} else {
|
||
Spacc.ShowModal(`Error: ${JSON.stringify(result)}`);
|
||
}
|
||
}
|
||
|
||
async function RequestUploadFile (account, fileData, fileMime) {
|
||
const request = await fetch(`${account.homeserver}/_matrix/client/v3/upload`, {
|
||
method: "POST",
|
||
headers: { Authorization: `Bearer ${account.token}`, "Content-Type": (fileData.type || fileMime) },
|
||
body: fileData,
|
||
});
|
||
const result = await request.json();
|
||
if (request.status === 200) {
|
||
return result;
|
||
} else {
|
||
Spacc.ShowModal(`Error: ${JSON.stringify(result)}`);
|
||
}
|
||
}
|
||
|
||
async function PreparePacksEditor (account) {
|
||
ResetLayouts();
|
||
$`#LayoutCollectionActions`.hidden = false;
|
||
$`#LayoutCollectionOptions`.hidden = false;
|
||
$`#LayoutCollectionActions button[name="commit"]`.onclick = () => Spacc.ShowModal({ label: 'Confirm committing account data?', action: async () => {
|
||
// ... we must read updated widget data that is stored somewhere accessible
|
||
//if (await RequestAccountWidgetsData(account, {})) {
|
||
// `#LayoutCollectionActions button[name="commit"]`.disabled = true;
|
||
//}
|
||
} });
|
||
$`#LayoutCollectionOptions button[name="reinit"]`.onclick = () => Spacc.ShowModal({ label: 'Confirm committing account data?', action: async () => {
|
||
const userTag = GetMatrixUserTag(account);
|
||
await RequestAccountWidgetsData(account, {
|
||
stickerpicker: {
|
||
content: {
|
||
type: "m.stickerpicker",
|
||
url: `${$`#LayoutCollectionOptions input[name="pickerUrl"]`.value}?&config=&theme=$theme`,
|
||
name: "Stickerpicker",
|
||
creatorUserId: userTag,
|
||
managedBy: [
|
||
`${Defaults.appIdentity}`,
|
||
`${Defaults.appIdentity}/${Defaults.appInterface}`,
|
||
],
|
||
},
|
||
sender: userTag,
|
||
state_key: "stickerpicker",
|
||
type: "m.widget",
|
||
id: "stickerpicker",
|
||
},
|
||
});
|
||
DisplayPacksEditor(account);
|
||
} });
|
||
$`#LayoutInfo`.innerHTML = `<p>
|
||
Fetching account data...
|
||
</p>`;
|
||
const widgetsData = await RequestAccountWidgetsData(account);
|
||
if (widgetsData) {
|
||
const isManaged = widgetsData?.stickerpicker?.content?.managedBy?.includes(`${Defaults.appIdentity}/${Defaults.appInterface}`);
|
||
const packsUrl = (new URLSearchParams(widgetsData?.stickerpicker?.content?.url?.split('?')[1])).get('config');
|
||
if (!isManaged || !widgetsData?.stickerpicker) {
|
||
$`#LayoutCollectionOptions`.open = true;
|
||
if (!widgetsData?.stickerpicker) {
|
||
$`#LayoutInfo`.innerHTML = `<p>${Defaults.Strings.mustInitMessage}</p>`;
|
||
} else
|
||
if (!isManaged) {
|
||
$`#LayoutInfo`.innerHTML = `
|
||
<p>${Defaults.Strings.notManagedMessage}</p>
|
||
<button name="continue">⏭️ Continue</button>
|
||
`;
|
||
$`#LayoutInfo > button[name="continue"]`.onclick = () => {
|
||
$`#LayoutCollectionOptions`.open = false;
|
||
DisplayPacksEditor(account, packsUrl);
|
||
};
|
||
}
|
||
} else {
|
||
DisplayPacksEditor(account, packsUrl);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function DisplayPacksEditor (account, packsUrl) {
|
||
ResetLayouts();
|
||
$`#LayoutCollectionActions`.hidden = false;
|
||
$`#LayoutCollectionOptions`.hidden = false;
|
||
let packsData;
|
||
if (packsUrl) {
|
||
const request = await fetch(packsUrl);
|
||
packsData = await request.json();
|
||
}
|
||
const addButton = $().createElement('button');
|
||
addButton.name = 'add';
|
||
addButton.innerHTML = '➕️ Create New Pack';
|
||
addButton.onclick = (ev) => {
|
||
for (const elem of ev.srcElement.parentElement.querySelectorAll('button')) {
|
||
elem.disabled = false;
|
||
}
|
||
const packButton = MakeStickerPackButton(account, packsData);
|
||
$`#LayoutPacksList`.insertBefore(packButton, $`#LayoutPacksList > button[name="add"]`.nextElementSibling);
|
||
$`#LayoutCollectionActions button[name="commit"]`.disabled = false;
|
||
packButton.click();
|
||
};
|
||
$`#LayoutPacksList`.appendChild(addButton);
|
||
for (const pack of (packsData?.packs || [])) {
|
||
const request = await fetch(pack);
|
||
const packData = await request.json();
|
||
$`#LayoutPacksList`.appendChild(MakeStickerPackButton(account, packsData, packData));
|
||
}
|
||
}
|
||
|
||
function MakeStickerPackButton (account, packsData, packData) {
|
||
const packButton = $().createElement('button');
|
||
packButton.innerHTML = `<img src="${GetMatrixMediaUrl(packData?.stickers[0]?.info?.thumbnail_url, packsData?.homeserver_url)}?&height=64&width=64&method=scale"/>`;
|
||
packButton.onclick = (ev) => {
|
||
const thisElem = (ev.srcElement.tagName.toLowerCase() === 'button' ? ev.srcElement : ev.srcElement.parentElement);
|
||
for (const elem of thisElem.parentElement.querySelectorAll('button')) {
|
||
elem.disabled = false;
|
||
}
|
||
thisElem.disabled = true;
|
||
$`#LayoutPackGrid`.innerHTML = '';
|
||
for (const sticker of (packData?.stickers || [])) {
|
||
const stickerElem = $().createElement('button');
|
||
stickerElem.innerHTML = `<img src="${GetMatrixMediaUrl(sticker.info.thumbnail_url, packsData?.homeserver_url)}?&height=128&width=128&method=scale"/>`;
|
||
$`#LayoutPackGrid`.appendChild(stickerElem);
|
||
}
|
||
const addButton = $().createElement('button');
|
||
addButton.innerHTML = `
|
||
➕️ Upload New Sticker(s)
|
||
<input type=file hidden="true" multiple="true" accept="image/jpeg,image/gif,image/png,image/webp"/>
|
||
`;
|
||
addButton.querySelector('input[type="file"]').onchange = async (ev) => {
|
||
for (const file in ev.target.files) {
|
||
const result = await RequestUploadFile(account, file);
|
||
// ... we must use the result.content_uri to add the new sticker to the screen, add it somewhere keeping the current pack state, and the packs state, ready for committing
|
||
if (!result) {
|
||
switch (Spacc.ShowModal({ label: 'File upload failed. What to do?', action: () => 'continue', actionCancel: () => 'cancel', labelSecondary: '🔄️ Retry', actionSecondary: () => 'retry' })) {
|
||
case 'cancel':
|
||
break;
|
||
break;
|
||
case 'continue':
|
||
continue;
|
||
break;
|
||
case 'retry':
|
||
// ... find out how to handle this
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
addButton.onclick = (ev) => ev.srcElement.querySelector('input[type="file"]')?.click();
|
||
$`#LayoutPackGrid`.appendChild(addButton);
|
||
};
|
||
return packButton;
|
||
}
|
||
|
||
function DisplayAccountSelect () {
|
||
ResetLayouts();
|
||
for (const account of Config.accounts) {
|
||
const accountButton = $().createElement('button');
|
||
accountButton.style.width = 'calc(100% - 3em)';
|
||
accountButton.innerHTML += `🐱️ ${GetMatrixUserTag(account)}`;
|
||
accountButton.onclick = () => PreparePacksEditor(account);
|
||
$`#LayoutAccountSelect`.appendChild(accountButton);
|
||
const deleteButton = $().createElement('button');
|
||
deleteButton.innerHTML += `❌️`;
|
||
deleteButton.onclick = () => Spacc.ShowModal({ label: '❌️ Confirm remove account?', action: () => {
|
||
Config.accounts.pop(account);
|
||
Config.Save();
|
||
DisplayAccountSelectLogin();
|
||
} });
|
||
$`#LayoutAccountSelect`.appendChild(deleteButton);
|
||
}
|
||
const addButton = $().createElement('button');
|
||
addButton.innerHTML += '🆕️ Add new account';
|
||
addButton.onclick = () => DisplayAccountLogin();
|
||
$`#LayoutAccountSelect`.appendChild(addButton);
|
||
}
|
||
|
||
function DisplayAccountLogin() {
|
||
ResetLayouts();
|
||
$`#LayoutAccountLogin`.hidden = false;
|
||
}
|
||
|
||
function DisplayAccountSelectLogin () {
|
||
(Config.accounts.length > 0 ? DisplayAccountSelect : DisplayAccountLogin)();
|
||
}
|
||
|
||
RegisterLayouts();
|
||
DisplayAccountSelectLogin();
|
||
</script>
|
||
|
||
<div id="Main" hidden="true">
|
||
<div id="LayoutAccountSelect"></div>
|
||
|
||
<div id="LayoutAccountLogin">
|
||
<label>
|
||
Homeserver
|
||
<input type="text"/>
|
||
</label>
|
||
<label>
|
||
Username
|
||
<input type="text"/>
|
||
</label>
|
||
<label>
|
||
Password
|
||
<input type="password"/>
|
||
</label>
|
||
<label>
|
||
Add account via session token instead
|
||
<input type="checkbox"/>
|
||
</label>
|
||
<label>
|
||
<input type="button" value="Add"/>
|
||
</label>
|
||
</div>
|
||
|
||
<div id="LayoutCollectionActions">
|
||
<button name="back">🔙️ Go Back</button>
|
||
<button name="commit" disabled="true">📝️ Commit Changes</button>
|
||
</div>
|
||
|
||
<div id="LayoutPacksList"></div>
|
||
<div id="LayoutPackGrid"></div>
|
||
<div id="LayoutInfo"></div>
|
||
|
||
<details class="col border margin" id="LayoutCollectionOptions">
|
||
<summary>
|
||
<p>Options</p>
|
||
</summary>
|
||
<p>
|
||
Reinitializing sticker data for your account will simply override
|
||
<!-- the <code>stickerpicker</code> subfield in --> its <code>m.widgets</code> field.
|
||
</p>
|
||
<button name="reinit">💡️ Reinitialize sticker data as new</button>
|
||
<details class="col border margin">
|
||
<summary>
|
||
<p>Advanced</p>
|
||
</summary>
|
||
<label>
|
||
Sticker selector app URL
|
||
<input name="pickerUrl" type="text"/>
|
||
</label>
|
||
</details>
|
||
</details>
|
||
</div>
|
||
|
||
<p>
|
||
🃏️ [Matrix] Sticker Helper <a name="version" href="javascript:;">WIP</a>,
|
||
created with ☕️ by <a href="https://hub.octt.eu.org">OctoSpacc</a>.
|
||
<br/>
|
||
Made possible by <a href="https://github.com/maunium/stickerpicker">Maunium sticker picker</a>,
|
||
brought to paper thanks to <a href="https://www.getpapercss.com">PaperCSS</a>.
|
||
</p>
|
||
|
||
<style>
|
||
:root {
|
||
--margin: 8px;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body, .ActionForm, .TableForm {
|
||
margin: var(--margin);
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
details > summary > * {
|
||
display: inline-block;
|
||
}
|
||
|
||
/*
|
||
#CollectionList {
|
||
max-width: 100%;
|
||
overflow: auto;
|
||
}
|
||
|
||
#CollectionList > div {
|
||
width: max-content;
|
||
}
|
||
|
||
#CollectionList > div > * {
|
||
width: 64px;
|
||
margin: var(--margin);
|
||
}
|
||
|
||
.ActionButton,
|
||
.ActionForm > *,
|
||
.TableForm td > * {
|
||
display: block;
|
||
height: 2em;
|
||
width: 100%;
|
||
}
|
||
|
||
.ActionForm > * > * {
|
||
height: 2em;
|
||
}
|
||
|
||
#CollectionGrid > *,
|
||
.ClickImg {
|
||
display: inline-block;
|
||
margin: 8px;
|
||
}
|
||
|
||
.ClickImg,
|
||
.ClickImg img {
|
||
width: 128px;
|
||
}
|
||
|
||
.ClickImg[data-opened="1"], .ClickImg[data-opened="1"] > img {
|
||
width: calc(100% - var(--margin));
|
||
}
|
||
*/
|
||
</style>
|