add options page (to upload vault snapshots)
This commit is contained in:
@@ -18,5 +18,9 @@ module.exports = {
|
||||
import: 'src/prompt.ts',
|
||||
runtime: false,
|
||||
},
|
||||
options: {
|
||||
import: 'src/options.ts',
|
||||
runtime: false,
|
||||
},
|
||||
},
|
||||
} as Configuration;
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Gooti",
|
||||
"description": "Nostr Identity Manager & Signer",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"homepage_url": "https://getgooti.com",
|
||||
"options_page": "options.html",
|
||||
"permissions": [
|
||||
"windows",
|
||||
"storage"
|
||||
|
||||
173
projects/chrome/public/options.html
Normal file
173
projects/chrome/public/options.html
Normal file
@@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html data-bs-theme="dark">
|
||||
<head>
|
||||
<title>Gooti - Options</title>
|
||||
<link rel="stylesheet" type="text/css" href="styles.css" />
|
||||
<script src="scripts.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background: var(--background);
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
border-radius: 100%;
|
||||
border: 2px solid var(--primary);
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 4px;
|
||||
color: #b9d6ff;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
padding-top: 50px;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
padding-top: 28px;
|
||||
font-size: 20px;
|
||||
max-width: 460px;
|
||||
text-align: right;
|
||||
line-height: 1.4;
|
||||
|
||||
.accent {
|
||||
color: #d63384;
|
||||
border: 1px solid #d63384;
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.middle {
|
||||
margin-top: 68px;
|
||||
width: 100%;
|
||||
background: var(--background-light);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.option-label {
|
||||
font-size: 20px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.option-text {
|
||||
color: gray;
|
||||
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.snapshots-list {
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="container sam-flex-row gap" style="margin-top: 16px">
|
||||
<div class="logo">
|
||||
<img src="gooti.svg" alt="" />
|
||||
</div>
|
||||
<span class="brand-name">Gooti</span>
|
||||
<span>OPTIONS</span>
|
||||
</div>
|
||||
|
||||
<div class="container sam-flex-column center">
|
||||
<span class="main-header"> Nostr Identity Manager & Signer </span>
|
||||
<span class="sub-header">
|
||||
Manage and switch between
|
||||
<span class="accent">multiple identities</span>
|
||||
while interacting with Nostr apps
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="middle">
|
||||
<div class="container sam-flex-column">
|
||||
<!-- VAULT SNAPSHOTS -->
|
||||
<span class="option-label">Vault Snapshots</span>
|
||||
<span class="option-text">
|
||||
Importing a previously exported vault snapshot is not
|
||||
<b>directly</b>
|
||||
possible in the extension's popup window. This is due to the
|
||||
browser's limitation of automatically closing the popup when it
|
||||
looses focus, making it impossible to drop or select a file there.
|
||||
</span>
|
||||
<span class="option-text">
|
||||
To circumvent this limitation, you need to upload your snapshot here
|
||||
and make it available for the extension to import in the popup.
|
||||
</span>
|
||||
<span class="option-text">
|
||||
<b>
|
||||
Uploading a snapshot here does NOT automatically start an import!
|
||||
</b>
|
||||
</span>
|
||||
<span class="option-text">
|
||||
<b>
|
||||
The data remains inside this browser and is NOT uploaded to
|
||||
any server!
|
||||
</b>
|
||||
</span>
|
||||
|
||||
<div class="sam-mt-h sam-flex-row gap">
|
||||
<button id="uploadSnapshotsButton" class="btn btn-primary">
|
||||
Upload Snapshots
|
||||
</button>
|
||||
|
||||
<button id="deleteSnapshotsButton" class="btn btn-danger">
|
||||
Delete Snapshots
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul id="snapshotsList" class="snapshots-list">
|
||||
<!-- will be filled by JS -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
id="uploadSnapshotInput"
|
||||
type="file"
|
||||
multiple
|
||||
style="position: absolute; top: 0; display: none"
|
||||
accept=".json"
|
||||
/>
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
130
projects/chrome/src/options.ts
Normal file
130
projects/chrome/src/options.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import {
|
||||
BrowserSyncData,
|
||||
GOOTI_META_DATA_KEY,
|
||||
GootiMetaData_VaultSnapshot,
|
||||
} from '@common';
|
||||
import './app/common/extensions/array';
|
||||
import browser from 'webextension-polyfill';
|
||||
|
||||
//
|
||||
// Functions
|
||||
//
|
||||
|
||||
async function getGootiMetaDataVaultSnapshots(): Promise<
|
||||
GootiMetaData_VaultSnapshot[]
|
||||
> {
|
||||
const data = (await browser.storage.local.get(
|
||||
GOOTI_META_DATA_KEY.vaultSnapshots
|
||||
)) as {
|
||||
vaultSnapshots?: GootiMetaData_VaultSnapshot[];
|
||||
};
|
||||
|
||||
return typeof data.vaultSnapshots === 'undefined'
|
||||
? []
|
||||
: data.vaultSnapshots.sortBy((x) => x.fileName, 'desc');
|
||||
}
|
||||
|
||||
async function setGootiMetaDataVaultSnapshots(
|
||||
vaultSnapshots: GootiMetaData_VaultSnapshot[]
|
||||
): Promise<void> {
|
||||
await browser.storage.local.set({
|
||||
vaultSnapshots,
|
||||
});
|
||||
}
|
||||
|
||||
function rebuildSnapshotsList(snapshots: GootiMetaData_VaultSnapshot[]) {
|
||||
const ul = document.getElementById('snapshotsList');
|
||||
if (!ul) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the list
|
||||
ul.innerHTML = '';
|
||||
|
||||
for (const snapshot of snapshots) {
|
||||
const li = document.createElement('li');
|
||||
|
||||
const test =
|
||||
'"' +
|
||||
snapshot.fileName +
|
||||
'"' +
|
||||
' -> vault version: ' +
|
||||
snapshot.data.version +
|
||||
' -> identities: ' +
|
||||
snapshot.data.identities.length +
|
||||
' -> relays: ' +
|
||||
snapshot.data.relays.length +
|
||||
'';
|
||||
|
||||
li.innerText = test;
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main
|
||||
//
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const uploadSnapshotsButton = document.getElementById(
|
||||
'uploadSnapshotsButton'
|
||||
);
|
||||
const deleteSnapshotsButton = document.getElementById(
|
||||
'deleteSnapshotsButton'
|
||||
);
|
||||
const uploadSnapshotInput = document.getElementById(
|
||||
'uploadSnapshotInput'
|
||||
) as HTMLInputElement;
|
||||
|
||||
deleteSnapshotsButton?.addEventListener('click', async () => {
|
||||
await setGootiMetaDataVaultSnapshots([]);
|
||||
rebuildSnapshotsList([]);
|
||||
});
|
||||
|
||||
uploadSnapshotsButton?.addEventListener('click', async () => {
|
||||
uploadSnapshotInput?.click();
|
||||
});
|
||||
|
||||
uploadSnapshotInput?.addEventListener('change', async (event) => {
|
||||
const files = (event.target as HTMLInputElement).files;
|
||||
if (!files) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const existingSnapshots = await getGootiMetaDataVaultSnapshots();
|
||||
|
||||
const newSnapshots: GootiMetaData_VaultSnapshot[] = [];
|
||||
for (const file of files) {
|
||||
const text = await file.text();
|
||||
const vault = JSON.parse(text) as BrowserSyncData;
|
||||
|
||||
// Check, if the "new" file is already in the list (via fileName comparison)
|
||||
if (existingSnapshots.some((x) => x.fileName === file.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newSnapshots.push({
|
||||
fileName: file.name,
|
||||
data: vault,
|
||||
});
|
||||
}
|
||||
|
||||
const snapshots = [...existingSnapshots, ...newSnapshots].sortBy(
|
||||
(x) => x.fileName,
|
||||
'desc'
|
||||
);
|
||||
|
||||
// Persist the new snapshots to the local storage
|
||||
await setGootiMetaDataVaultSnapshots(snapshots);
|
||||
|
||||
//
|
||||
rebuildSnapshotsList(snapshots);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
const snapshots = await getGootiMetaDataVaultSnapshots();
|
||||
rebuildSnapshotsList(snapshots);
|
||||
});
|
||||
@@ -11,7 +11,8 @@
|
||||
"src/background.ts",
|
||||
"src/gooti-extension.ts",
|
||||
"src/gooti-content-script.ts",
|
||||
"src/prompt.ts"
|
||||
"src/prompt.ts",
|
||||
"src/options.ts"
|
||||
],
|
||||
"include": ["src/**/*.d.ts"]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export abstract class GootiMetaHandler {
|
||||
|
||||
#gootiMetaData?: GootiMetaData;
|
||||
|
||||
readonly metaProperties = ['syncFlow'];
|
||||
readonly metaProperties = ['syncFlow', 'vaultSnapshots'];
|
||||
/**
|
||||
* Load the full data from the storage. If the storage is used for storing
|
||||
* other data (e.g. browser sync data when the user decided to NOT sync),
|
||||
|
||||
@@ -79,6 +79,17 @@ export interface BrowserSessionData {
|
||||
relays: Relay_DECRYPTED[];
|
||||
}
|
||||
|
||||
export interface GootiMetaData_VaultSnapshot {
|
||||
fileName: string;
|
||||
data: BrowserSyncData;
|
||||
}
|
||||
|
||||
export const GOOTI_META_DATA_KEY = {
|
||||
vaultSnapshots: 'vaultSnapshots',
|
||||
};
|
||||
|
||||
export interface GootiMetaData {
|
||||
syncFlow?: number; // 0 = no sync, 1 = browser sync, (future: 2 = Gooti sync, 3 = Custom sync (bring your own sync))
|
||||
|
||||
vaultSnapshots?: GootiMetaData_VaultSnapshot[];
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
margin-top: var(--size-h);
|
||||
}
|
||||
|
||||
.sam-mt-hh {
|
||||
margin-top: var(--size-hh);
|
||||
}
|
||||
|
||||
.sam-mb {
|
||||
margin-bottom: var(--size);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
--size-2: 32px;
|
||||
--size: 16px;
|
||||
--size-h: 8px;
|
||||
--size-hh: 4px;
|
||||
|
||||
--background: #161c26;
|
||||
--background-light: #202733;
|
||||
|
||||
Reference in New Issue
Block a user