1
Fork 0

Feature: Add import and export settings buttons (fixes #2)

This commit is contained in:
Bauke 2019-11-11 15:40:30 +01:00
parent 2203cb0f90
commit b063e1507c
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
6 changed files with 161 additions and 0 deletions

View File

@ -74,8 +74,10 @@
"ts"
],
"global": [
"Blob",
"confirm",
"document",
"FileReader",
"performance",
"window"
],

View File

@ -5,6 +5,7 @@
"description": "An updated and reimagined recreation of Tildes Extended to enhance and improve the experience of Tildes.net.",
"version": "0.1.1",
"permissions": [
"downloads",
"storage",
"*://tildes.net/*"
],

View File

@ -157,6 +157,16 @@
please use the "Copy Bug Template" button below and fill out
the template in your message.
</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 id="debug-buttons">
<button id="copy-bug-template-button">

View File

@ -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 {
align-items: center;
display: flex;

108
source/ts/import-export.ts Normal file
View File

@ -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);
}
}

View File

@ -1,5 +1,10 @@
import platform from 'platform';
import {browser} from 'webextension-polyfill-ts';
import {
importSettingsHandler,
importFileHandler,
exportSettingsHandler
} from './import-export';
import {
getSettings,
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(
'#copy-bug-template-button'
);