diff --git a/projects/chrome/custom-webpack.config.ts b/projects/chrome/custom-webpack.config.ts index 39da619..d5586e0 100644 --- a/projects/chrome/custom-webpack.config.ts +++ b/projects/chrome/custom-webpack.config.ts @@ -18,5 +18,9 @@ module.exports = { import: 'src/prompt.ts', runtime: false, }, + options: { + import: 'src/options.ts', + runtime: false, + }, }, } as Configuration; diff --git a/projects/chrome/public/manifest.json b/projects/chrome/public/manifest.json index d78a1cd..bfdaa5b 100644 --- a/projects/chrome/public/manifest.json +++ b/projects/chrome/public/manifest.json @@ -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" diff --git a/projects/chrome/public/options.html b/projects/chrome/public/options.html new file mode 100644 index 0000000..587c212 --- /dev/null +++ b/projects/chrome/public/options.html @@ -0,0 +1,173 @@ + + + + + Gooti - Options + + + + + +
+
+ + Gooti + OPTIONS +
+ +
+ Nostr Identity Manager & Signer + + Manage and switch between + multiple identities + while interacting with Nostr apps + +
+ +
+
+ + Vault Snapshots + + Importing a previously exported vault snapshot is not + directly + 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. + + + To circumvent this limitation, you need to upload your snapshot here + and make it available for the extension to import in the popup. + + + + Uploading a snapshot here does NOT automatically start an import! + + + + + The data remains inside this browser and is NOT uploaded to + any server! + + + +
+ + + +
+ +
    + +
+
+
+
+ + + + + diff --git a/projects/chrome/src/options.ts b/projects/chrome/src/options.ts new file mode 100644 index 0000000..02bf5df --- /dev/null +++ b/projects/chrome/src/options.ts @@ -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 { + 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); +}); diff --git a/projects/chrome/tsconfig.app.json b/projects/chrome/tsconfig.app.json index ada90a5..5885059 100644 --- a/projects/chrome/tsconfig.app.json +++ b/projects/chrome/tsconfig.app.json @@ -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"] } diff --git a/projects/common/src/lib/services/storage/gooti-meta-handler.ts b/projects/common/src/lib/services/storage/gooti-meta-handler.ts index 86bc23d..aabeb73 100644 --- a/projects/common/src/lib/services/storage/gooti-meta-handler.ts +++ b/projects/common/src/lib/services/storage/gooti-meta-handler.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), diff --git a/projects/common/src/lib/services/storage/types.ts b/projects/common/src/lib/services/storage/types.ts index 2c6d519..b73d4e8 100644 --- a/projects/common/src/lib/services/storage/types.ts +++ b/projects/common/src/lib/services/storage/types.ts @@ -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[]; } diff --git a/projects/common/src/lib/styles/_spacing.scss b/projects/common/src/lib/styles/_spacing.scss index 3ff3ae9..f9a1132 100644 --- a/projects/common/src/lib/styles/_spacing.scss +++ b/projects/common/src/lib/styles/_spacing.scss @@ -10,6 +10,10 @@ margin-top: var(--size-h); } +.sam-mt-hh { + margin-top: var(--size-hh); +} + .sam-mb { margin-bottom: var(--size); } diff --git a/projects/common/src/lib/styles/styles.scss b/projects/common/src/lib/styles/styles.scss index e374e45..06ce184 100644 --- a/projects/common/src/lib/styles/styles.scss +++ b/projects/common/src/lib/styles/styles.scss @@ -8,6 +8,7 @@ --size-2: 32px; --size: 16px; --size-h: 8px; + --size-hh: 4px; --background: #161c26; --background-light: #202733;