Feature: Add import and export settings buttons (fixes #2)
This commit is contained in:
parent
2203cb0f90
commit
b063e1507c
|
@ -74,8 +74,10 @@
|
||||||
"ts"
|
"ts"
|
||||||
],
|
],
|
||||||
"global": [
|
"global": [
|
||||||
|
"Blob",
|
||||||
"confirm",
|
"confirm",
|
||||||
"document",
|
"document",
|
||||||
|
"FileReader",
|
||||||
"performance",
|
"performance",
|
||||||
"window"
|
"window"
|
||||||
],
|
],
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"description": "An updated and reimagined recreation of Tildes Extended to enhance and improve the experience of Tildes.net.",
|
"description": "An updated and reimagined recreation of Tildes Extended to enhance and improve the experience of Tildes.net.",
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
"downloads",
|
||||||
"storage",
|
"storage",
|
||||||
"*://tildes.net/*"
|
"*://tildes.net/*"
|
||||||
],
|
],
|
||||||
|
|
|
@ -157,6 +157,16 @@
|
||||||
please use the "Copy Bug Template" button below and fill out
|
please use the "Copy Bug Template" button below and fill out
|
||||||
the template in your message.
|
the template in your message.
|
||||||
</p>
|
</p>
|
||||||
|
<form id="import-export">
|
||||||
|
<input class="trx-hidden" accept="application/json" type="file"
|
||||||
|
id="import-file">
|
||||||
|
<button id="import-button">Import Settings</button>
|
||||||
|
<button id="export-button">Export Settings</button>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
When importing settings, note that your current settings will be
|
||||||
|
deleted and overwritten with the new ones.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="debug-buttons">
|
<div id="debug-buttons">
|
||||||
<button id="copy-bug-template-button">
|
<button id="copy-bug-template-button">
|
||||||
|
|
|
@ -200,6 +200,28 @@ p > .red-re {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#import-export {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
border: none;
|
||||||
|
color: $foreground;
|
||||||
|
background-color: $cyan;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($cyan, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import {browser} from 'webextension-polyfill-ts';
|
||||||
|
import {
|
||||||
|
querySelector,
|
||||||
|
flashMessage,
|
||||||
|
Settings,
|
||||||
|
log,
|
||||||
|
getSettings,
|
||||||
|
isValidTildesUsername,
|
||||||
|
isValidHexColor,
|
||||||
|
setSettings
|
||||||
|
} from './utilities';
|
||||||
|
import {themeColors} from './theme-colors';
|
||||||
|
|
||||||
|
export async function importSettingsHandler(event: MouseEvent): Promise<void> {
|
||||||
|
event.preventDefault();
|
||||||
|
const fileInput: HTMLInputElement = querySelector('#import-file');
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importFileHandler(event: Event): Promise<void> {
|
||||||
|
const fileList: FileList | null = (event.target as HTMLInputElement).files;
|
||||||
|
if (fileList === null) {
|
||||||
|
flashMessage('No file imported.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader: FileReader = new FileReader();
|
||||||
|
reader.addEventListener(
|
||||||
|
'load',
|
||||||
|
async (): Promise<void> => {
|
||||||
|
let data: Partial<Settings>;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(reader.result!.toString());
|
||||||
|
} catch (error) {
|
||||||
|
log(error, true);
|
||||||
|
flashMessage(error, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings: Settings = await getSettings();
|
||||||
|
const newSettings: Settings = {...settings};
|
||||||
|
if (typeof data.data !== 'undefined') {
|
||||||
|
if (typeof data.data.userLabels !== 'undefined') {
|
||||||
|
newSettings.data.userLabels = [];
|
||||||
|
for (const label of data.data.userLabels) {
|
||||||
|
if (
|
||||||
|
typeof label.username === 'undefined' ||
|
||||||
|
!isValidTildesUsername(label.username)
|
||||||
|
) {
|
||||||
|
log(`Invalid username in imported labels: ${label.username}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newSettings.data.userLabels.push({
|
||||||
|
color: isValidHexColor(label.color)
|
||||||
|
? label.color
|
||||||
|
: themeColors.white.backgroundAlt,
|
||||||
|
id: newSettings.data.userLabels.length + 1,
|
||||||
|
priority: isNaN(label.priority) ? 0 : label.priority,
|
||||||
|
text: typeof label.text === 'undefined' ? 'Label' : label.text,
|
||||||
|
username: label.username
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.features !== 'undefined') {
|
||||||
|
newSettings.features = {...data.features};
|
||||||
|
}
|
||||||
|
|
||||||
|
await setSettings(newSettings);
|
||||||
|
flashMessage(
|
||||||
|
'Successfully imported your settings, reloading the page to apply.'
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 2500);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
reader.addEventListener('error', (): void => {
|
||||||
|
log(reader.error, true);
|
||||||
|
reader.abort();
|
||||||
|
});
|
||||||
|
reader.readAsText(fileList[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exportSettingsHandler(event: MouseEvent): Promise<void> {
|
||||||
|
event.preventDefault();
|
||||||
|
const settings: Settings = await getSettings();
|
||||||
|
const settingsBlob: Blob = new Blob([JSON.stringify(settings, null, 2)], {
|
||||||
|
type: 'text/json'
|
||||||
|
});
|
||||||
|
const blobObjectURL: string = URL.createObjectURL(settingsBlob);
|
||||||
|
try {
|
||||||
|
await browser.downloads.download({
|
||||||
|
filename: 'tildes_reextended-settings.json',
|
||||||
|
url: blobObjectURL,
|
||||||
|
saveAs: true
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log(error);
|
||||||
|
} finally {
|
||||||
|
// According to MDN, when creating an object URL we should also revoke it
|
||||||
|
// when "it's safe to do so" to prevent excess memory/storage use. 60
|
||||||
|
// seconds should be enough time to download the settings.
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobObjectURL), 60000);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
import platform from 'platform';
|
import platform from 'platform';
|
||||||
import {browser} from 'webextension-polyfill-ts';
|
import {browser} from 'webextension-polyfill-ts';
|
||||||
|
import {
|
||||||
|
importSettingsHandler,
|
||||||
|
importFileHandler,
|
||||||
|
exportSettingsHandler
|
||||||
|
} from './import-export';
|
||||||
import {
|
import {
|
||||||
getSettings,
|
getSettings,
|
||||||
Settings,
|
Settings,
|
||||||
|
@ -33,6 +38,19 @@ window.addEventListener(
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const importSettingsButton: HTMLButtonElement = querySelector(
|
||||||
|
'#import-button'
|
||||||
|
);
|
||||||
|
importSettingsButton.addEventListener('click', importSettingsHandler);
|
||||||
|
|
||||||
|
const importFileInput: HTMLInputElement = querySelector('#import-file');
|
||||||
|
importFileInput.addEventListener('change', importFileHandler);
|
||||||
|
|
||||||
|
const exportSettingsButton: HTMLButtonElement = querySelector(
|
||||||
|
'#export-button'
|
||||||
|
);
|
||||||
|
exportSettingsButton.addEventListener('click', exportSettingsHandler);
|
||||||
|
|
||||||
const copyBugTemplateButton: HTMLButtonElement = querySelector(
|
const copyBugTemplateButton: HTMLButtonElement = querySelector(
|
||||||
'#copy-bug-template-button'
|
'#copy-bug-template-button'
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue