diff --git a/README.md b/README.md index 7aabd5f..15f08f9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # jumble -Yet another Nostr desktop client - -> NOTE: Currently, only browsing is supported. Posting, liking, and reposting will be available soon. +Yet another Nostr client ## Features @@ -10,6 +8,7 @@ Yet another Nostr desktop client - **Relay-Friendly Design:** Minimized and simplified requests ensure efficient communication with relays - **Relay Groups:** Easily manage and switch between relay groups - **Clean Interface:** Enjoy a minimalist design and intuitive interactions +- **Cross-Platform:** Available on macOS, Windows, Linux, and web browsers ## Download diff --git a/package-lock.json b/package-lock.json index fec15c8..e094f8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "lru-cache": "^11.0.1", "lucide-react": "^0.453.0", "nostr-tools": "^2.9.1", + "path-to-regexp": "^8.2.0", "qrcode.react": "^4.1.0", "react-resizable-panels": "^2.1.5", "react-string-replace": "^1.1.1", @@ -7561,6 +7562,14 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", diff --git a/package.json b/package.json index b4d9c49..99752ea 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,14 @@ "typecheck": "npm run typecheck:node && npm run typecheck:web", "start": "electron-vite preview", "dev": "electron-vite dev", + "dev:web": "vite --config web.vite.config.ts", "build": "npm run typecheck && electron-vite build", "postinstall": "electron-builder install-app-deps", "build:unpack": "npm run build && electron-builder --dir", "build:win": "npm run build && electron-builder --win -p never", "build:mac": "electron-vite build && electron-builder --mac -p never", - "build:linux": "electron-vite build && electron-builder --linux -p never" + "build:linux": "electron-vite build && electron-builder --linux -p never", + "build:web": "vite build --config web.vite.config.ts" }, "dependencies": { "@electron-toolkit/preload": "^3.0.1", @@ -47,6 +49,7 @@ "lru-cache": "^11.0.1", "lucide-react": "^0.453.0", "nostr-tools": "^2.9.1", + "path-to-regexp": "^8.2.0", "qrcode.react": "^4.1.0", "react-resizable-panels": "^2.1.5", "react-string-replace": "^1.1.1", diff --git a/src/common/types.ts b/src/common/types.ts index e91e368..ba27490 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,3 +1,4 @@ +import { ElectronAPI } from '@electron-toolkit/preload' import { Event } from 'nostr-tools' export type TRelayGroup = { @@ -15,3 +16,33 @@ export type TThemeSetting = 'light' | 'dark' | 'system' export type TTheme = 'light' | 'dark' export type TDraftEvent = Pick + +export type TElectronWindow = { + electron: ElectronAPI + api: { + system: { + isEncryptionAvailable: () => Promise + } + theme: { + onChange: (cb: (theme: TTheme) => void) => void + current: () => Promise + themeSetting: () => Promise + set: (themeSetting: TThemeSetting) => Promise + } + storage: { + getRelayGroups: () => Promise + setRelayGroups: (relayGroups: TRelayGroup[]) => Promise + } + nostr: { + login: (nsec: string) => Promise<{ + pubkey?: string + reason?: string + }> + logout: () => Promise + } + } + nostr: { + getPublicKey: () => Promise + signEvent: (draftEvent: TDraftEvent) => Promise + } +} diff --git a/src/main/services/storage.service.ts b/src/main/services/storage.service.ts index 6695233..cf8b105 100644 --- a/src/main/services/storage.service.ts +++ b/src/main/services/storage.service.ts @@ -17,21 +17,8 @@ export class StorageService { ) } - getRelayGroups(): TRelayGroup[] { - return ( - this.storage.get('relayGroups') ?? [ - { - groupName: 'Global', - relayUrls: [ - 'wss://relay.damus.io/', - 'wss://nos.lol/', - 'wss://nostr.mom/', - 'wss://relay.primal.net/' - ], - isActive: true - } - ] - ) + getRelayGroups(): TRelayGroup[] | null { + return this.storage.get('relayGroups') ?? null } setRelayGroups(relayGroups: TRelayGroup[]) { diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts deleted file mode 100644 index 56a2751..0000000 --- a/src/preload/index.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { TDraftEvent, TRelayGroup, TTheme, TThemeSetting } from '@common/types' -import { ElectronAPI } from '@electron-toolkit/preload' -import { Event } from 'nostr-tools' - -declare global { - interface Window { - electron: ElectronAPI - api: { - system: { - isEncryptionAvailable: () => Promise - } - theme: { - onChange: (cb: (theme: TTheme) => void) => void - current: () => Promise - themeSetting: () => Promise - set: (themeSetting: TThemeSetting) => Promise - } - storage: { - getRelayGroups: () => Promise - setRelayGroups: (relayGroups: TRelayGroup[]) => Promise - } - nostr: { - login: (nsec: string) => Promise<{ - pubkey?: string - reason?: string - }> - logout: () => Promise - getPublicKey: () => Promise - signEvent: (draftEvent: TDraftEvent) => Promise - } - } - } -} diff --git a/src/preload/index.ts b/src/preload/index.ts index f03d918..86941b6 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -24,12 +24,16 @@ const api = { }, nostr: { login: (nsec: string) => ipcRenderer.invoke('nostr:login', nsec), - logout: () => ipcRenderer.invoke('nostr:logout'), - getPublicKey: () => ipcRenderer.invoke('nostr:getPublicKey'), - signEvent: (draftEvent: TDraftEvent) => ipcRenderer.invoke('nostr:signEvent', draftEvent) + logout: () => ipcRenderer.invoke('nostr:logout') } } +// NIP-07 +const nostr = { + getPublicKey: () => ipcRenderer.invoke('nostr:getPublicKey'), + signEvent: (draftEvent: TDraftEvent) => ipcRenderer.invoke('nostr:signEvent', draftEvent) +} + // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. @@ -37,6 +41,7 @@ if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('api', api) + contextBridge.exposeInMainWorld('nostr', nostr) } catch (error) { console.error(error) } @@ -45,4 +50,6 @@ if (process.contextIsolated) { window.electron = electronAPI // @ts-ignore (define in dts) window.api = api + // @ts-ignore (define in dts) + window.nostr = nostr } diff --git a/src/renderer/index.html b/src/renderer/index.html index 027e508..a16dfda 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -2,7 +2,7 @@ - Electron + Jumble