From 621b6c5d53e7cae6aa36d4d09bcc2f95996886f7 Mon Sep 17 00:00:00 2001 From: Undyingwraith Date: Mon, 20 Jan 2025 01:15:36 +0100 Subject: [PATCH] Fix release issues --- packages/desktop/electron-builder.yml | 21 ++++---- packages/desktop/package.json | 5 -- packages/desktop/src/main/index.ts | 8 ++- packages/desktop/src/preload/index.ts | 37 ++++++++------ packages/desktop/src/renderer/index.html | 22 +++++---- .../src/Services/IConfigurationService.ts | 8 +-- packages/ui/src/IpmcApp.tsx | 49 ++++++++++--------- packages/ui/src/IpmcLauncher.tsx | 2 +- .../components/molecules/ProfileEditor.tsx | 37 +++++--------- .../components/molecules/ProfileSelector.tsx | 33 ++++++++----- packages/webui/src/App.tsx | 6 ++- 11 files changed, 118 insertions(+), 110 deletions(-) diff --git a/packages/desktop/electron-builder.yml b/packages/desktop/electron-builder.yml index 6bb664c..91514d9 100644 --- a/packages/desktop/electron-builder.yml +++ b/packages/desktop/electron-builder.yml @@ -1,20 +1,17 @@ -appId: com.electron.app -productName: desktop +appId: com.electron.ipmc +productName: IPMC +asar: true +artifactName: ${productName}-${version}.${ext} directories: buildResources: build files: - - '!**/.vscode/*' - - '!src/*' - - '!electron.vite.config.{js,ts,mjs,cjs}' - - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' - - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' - - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' + - 'out/**/*' asarUnpack: - resources/** win: - executableName: desktop + executableName: IPMC nsis: - artifactName: ${name}-${version}-setup.${ext} + artifactName: ${productName}-${version}-setup.${ext} shortcutName: ${productName} uninstallDisplayName: ${productName} createDesktopShortcut: always @@ -27,7 +24,7 @@ mac: - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. notarize: false dmg: - artifactName: ${name}-${version}.${ext} + artifactName: ${productName}-${version}.${ext} linux: target: - AppImage @@ -36,7 +33,7 @@ linux: maintainer: electronjs.org category: Utility appImage: - artifactName: ${name}-${version}.${ext} + artifactName: ${productName}-${version}.${ext} npmRebuild: false publish: provider: generic diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 5d68081..f8cbe50 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -20,11 +20,6 @@ "build:mac": "electron-vite build && electron-builder --mac --config", "build:linux": "electron-vite build && electron-builder --linux --config" }, - "build": { - "files": [ - "out/**/*" - ] - }, "dependencies": { "@chainsafe/libp2p-gossipsub": "^13.0.0", "@chainsafe/libp2p-noise": "^15.0.0", diff --git a/packages/desktop/src/main/index.ts b/packages/desktop/src/main/index.ts index de0a390..84609cd 100644 --- a/packages/desktop/src/main/index.ts +++ b/packages/desktop/src/main/index.ts @@ -1,4 +1,4 @@ -import { app, shell, BrowserWindow } from 'electron'; +import { app, shell, BrowserWindow, ipcMain } from 'electron'; import { join } from 'path'; import { electronApp, optimizer, is } from '@electron-toolkit/utils'; import icon from '../../resources/icon.png?asset'; @@ -8,6 +8,10 @@ process.on('uncaughtException', function (error) { process.stdout.write(error.name + ': ' + error.message + '\n' + (error.stack != undefined ? error.stack + '\n' : '')); }); +function getAppPath(): string { + return app.getPath('appData'); +} + function createWindow(): void { // Create the browser window. const mainWindow = new BrowserWindow({ @@ -52,6 +56,8 @@ function createWindow(): void { // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { + ipcMain.handle('getAppPath', getAppPath); + // Set app user model id for windows electronApp.setAppUserModelId('com.electron'); diff --git a/packages/desktop/src/preload/index.ts b/packages/desktop/src/preload/index.ts index 5c1a5b4..f3aa9bd 100644 --- a/packages/desktop/src/preload/index.ts +++ b/packages/desktop/src/preload/index.ts @@ -22,7 +22,7 @@ import { uPnPNAT } from '@libp2p/upnp-nat'; import { webSockets } from '@libp2p/websockets'; import { FsBlockstore } from 'blockstore-fs'; import { LevelDatastore } from 'datastore-level'; -import { contextBridge } from 'electron'; +import { contextBridge, ipcRenderer } from 'electron'; import fs from 'fs'; import { createHelia } from 'helia'; import { IConfigurationService, IFileInfo, IInternalProfile, IIpfsService, INodeService, IProfile } from 'ipmc-interfaces'; @@ -30,10 +30,12 @@ import { ipnsSelector } from 'ipns/selector'; import { ipnsValidator } from 'ipns/validator'; import * as libp2pInfo from 'libp2p/version'; import { CID } from 'multiformats'; +import path from 'path'; import { concat } from 'uint8arrays'; -function getProfileFolder(name: string): string { - return `./profiles/${name}`; +async function getProfileFolder(id?: string): Promise { + const appPath = path.join(await ipcRenderer.invoke('getAppPath'), 'profiles'); + return id ? path.join(appPath, id) : appPath; } const nodes = new Map(); @@ -44,10 +46,12 @@ const nodeService: INodeService = { return nodes.get(profile.id)!; } + const folder = await getProfileFolder(profile.id); + const agentVersion = `helia/2.0.0 ${libp2pInfo.name}/${libp2pInfo.version} UserAgent=${process.version}`; - const datastore = new LevelDatastore(`${getProfileFolder(profile.id)}/data`); + const datastore = new LevelDatastore(path.join(folder, 'data')); await datastore.open(); - const blockstore = new FsBlockstore(`${getProfileFolder(profile.id)}/blocks`); + const blockstore = new FsBlockstore(path.join(folder, 'blocks')); await blockstore.open(); const helia = await createHelia({ @@ -196,25 +200,28 @@ const nodeService: INodeService = { }; const configService: IConfigurationService = { - getProfiles(): string[] { + async getProfiles(): Promise { try { - const profiles = fs.readdirSync('./profiles'); + const profiles = fs.readdirSync(await getProfileFolder()); return profiles; } catch (_) { return []; } }, - getProfile(id: string): IProfile { - return JSON.parse(fs.readFileSync(getProfileFolder(id) + '/profile.json', 'utf-8')); + async getProfile(id: string): Promise { + return JSON.parse(fs.readFileSync(path.join(await getProfileFolder(id), '/profile.json'), 'utf-8')); }, - setProfile(id: string, profile: IProfile) { - if (!fs.existsSync(getProfileFolder(id))) { - fs.mkdirSync(getProfileFolder(id)); + async setProfile(id: string, profile: IProfile) { + const folder = await getProfileFolder(id); + if (!fs.existsSync(folder)) { + fs.mkdirSync(folder, { + recursive: true + }); } - fs.writeFileSync(getProfileFolder(id) + '/profile.json', JSON.stringify(profile)); + fs.writeFileSync(path.join(folder, '/profile.json'), JSON.stringify(profile)); }, - removeProfile(id) { - fs.rmSync(getProfileFolder(id), { recursive: true }); + async removeProfile(id) { + fs.rmSync(await getProfileFolder(id), { recursive: true }); }, }; diff --git a/packages/desktop/src/renderer/index.html b/packages/desktop/src/renderer/index.html index 88377c1..a42a3fa 100644 --- a/packages/desktop/src/renderer/index.html +++ b/packages/desktop/src/renderer/index.html @@ -1,17 +1,19 @@ - - - Electron - - + - + + + +
+ + - -
- - diff --git a/packages/interfaces/src/Services/IConfigurationService.ts b/packages/interfaces/src/Services/IConfigurationService.ts index 20c80f9..a1c548d 100644 --- a/packages/interfaces/src/Services/IConfigurationService.ts +++ b/packages/interfaces/src/Services/IConfigurationService.ts @@ -9,24 +9,24 @@ export interface IConfigurationService { /** * Gets a list of all {@link IProfile} names. */ - getProfiles(): string[]; + getProfiles(): Promise; /** * Returns the specified {@link IProfile}. * @param id id of the {@link IProfile}. */ - getProfile(id: string): IProfile; + getProfile(id: string): Promise; /** * Updates the specified {@link IProfile}. * @param id id of the {@link IProfile}. * @param profile updated {@link IProfile}. */ - setProfile(id: string, profile: IProfile): void; + setProfile(id: string, profile: IProfile): Promise; /** * Deletes the specified {@link IProfile}. * @param id id of the {@link IProfile}. */ - removeProfile(id: string): void; + removeProfile(id: string): Promise; } diff --git a/packages/ui/src/IpmcApp.tsx b/packages/ui/src/IpmcApp.tsx index deb485e..9a52475 100644 --- a/packages/ui/src/IpmcApp.tsx +++ b/packages/ui/src/IpmcApp.tsx @@ -1,13 +1,14 @@ +import { Box, CssBaseline } from '@mui/material'; import { BrowserModule, CoreModule, FileExportService, IModule } from 'ipmc-core'; import { IConfigurationService, IConfigurationServiceSymbol, IFileExportServiceSymbol, INodeService, ITranslation, ITranslationsSymbol } from 'ipmc-interfaces'; import React from 'react'; -import { Switch } from 'wouter'; +import { Router, Switch } from 'wouter'; +import { useHashLocation } from 'wouter/use-hash-location'; import { AppBar } from './components/organisms/AppBar'; import { AppContextProvider, ThemeProvider } from './context'; +import { IpmcLauncher } from './IpmcLauncher'; import { UiModule } from './services/UiModule'; import translations from './translations'; -import { IpmcLauncher } from './IpmcLauncher'; -import { Box, CssBaseline } from '@mui/material'; export interface IIpmcAppProps { setup?: IModule; @@ -19,26 +20,28 @@ export function IpmcApp(props: IIpmcAppProps) { const { setup } = props; return ( - { - app.use(CoreModule); - app.use(BrowserModule); - app.use(UiModule); - app.registerConstant(props.configService, IConfigurationServiceSymbol); - app.register(FileExportService, IFileExportServiceSymbol); - app.registerConstantMultiple(translations, ITranslationsSymbol); - setup && app.use(setup); - }}> - - - - - - - - + + { + app.use(CoreModule); + app.use(BrowserModule); + app.use(UiModule); + app.registerConstant(props.configService, IConfigurationServiceSymbol); + app.register(FileExportService, IFileExportServiceSymbol); + app.registerConstantMultiple(translations, ITranslationsSymbol); + setup && app.use(setup); + }}> + + + + + + + + + - - - + + + ); } diff --git a/packages/ui/src/IpmcLauncher.tsx b/packages/ui/src/IpmcLauncher.tsx index e9a482c..6e7906d 100644 --- a/packages/ui/src/IpmcLauncher.tsx +++ b/packages/ui/src/IpmcLauncher.tsx @@ -37,7 +37,7 @@ export function IpmcLauncher(props: PropsWithChildren) { async function start(name: string) { if (name == undefined) return; - const currentProfile = props.configService.getProfile(name); + const currentProfile = await props.configService.getProfile(name); if (currentProfile != undefined) { profile.value = currentProfile; try { diff --git a/packages/ui/src/components/molecules/ProfileEditor.tsx b/packages/ui/src/components/molecules/ProfileEditor.tsx index 4b179ee..4d40e59 100644 --- a/packages/ui/src/components/molecules/ProfileEditor.tsx +++ b/packages/ui/src/components/molecules/ProfileEditor.tsx @@ -9,37 +9,22 @@ import { LibraryEditor } from './LibraryEditor'; import { uuid } from 'ipmc-core'; import { useService } from '@src/context'; -export function ProfileEditor(props: { id: string, onCancel: () => void, onSave: () => void; }) { - const { id, onCancel, onSave } = props; +export function ProfileEditor(props: { profile: IProfile, onCancel: () => void, onSave: () => void; }) { + const { profile, onCancel, onSave } = props; const configService = useService(IConfigurationServiceSymbol); const _t = useTranslation(); - const profile = useComputed(() => { - const defaultProfile = { - id: props.id, - libraries: [], - name: '', - type: 'internal', - } as IProfile; - try { - const profile = configService.getProfile(id); - return profile ?? defaultProfile; - } catch (ex) { - return defaultProfile; - } - }); - - const name = useSignal(profile.value?.name ?? id); - const type = useSignal<'internal' | 'remote'>(profile.value?.type ?? 'internal'); - const apiUrl = useSignal(isRemoteProfile(profile.value) ? profile.value.url ?? '' : ''); - const swarmKey = useSignal(isInternalProfile(profile.value) ? profile.value.swarmKey ?? '' : ''); - const port = useSignal(isInternalProfile(profile.value) ? profile.value.port?.toString() ?? '' : ''); - const bootstrap = useSignal[]>(isInternalProfile(profile.value) ? profile.value.bootstrap?.map(i => new Signal(i)) ?? [] : []); - const libraries = useSignal[]>(profile.value.libraries.map(i => new Signal(i))); + const name = useSignal(profile.name); + const type = useSignal<'internal' | 'remote'>(profile.type ?? 'internal'); + const apiUrl = useSignal(isRemoteProfile(profile) ? profile.url ?? '' : ''); + const swarmKey = useSignal(isInternalProfile(profile) ? profile.swarmKey ?? '' : ''); + const port = useSignal(isInternalProfile(profile) ? profile.port?.toString() ?? '' : ''); + const bootstrap = useSignal[]>(isInternalProfile(profile) ? profile.bootstrap?.map(i => new Signal(i)) ?? [] : []); + const libraries = useSignal[]>(profile.libraries.map(i => new Signal(i))); function save() { - configService.setProfile(id, { - ...(profile.value ?? {}), + configService.setProfile(profile.id, { + ...(profile ?? {}), name: name.value, type: type.value, ...(type.value === 'internal' ? { diff --git a/packages/ui/src/components/molecules/ProfileSelector.tsx b/packages/ui/src/components/molecules/ProfileSelector.tsx index e0c2428..6e68c57 100644 --- a/packages/ui/src/components/molecules/ProfileSelector.tsx +++ b/packages/ui/src/components/molecules/ProfileSelector.tsx @@ -1,5 +1,5 @@ import { Box, Button, ButtonGroup, Card, CardActions, CardHeader, Container, Stack } from "@mui/material"; -import { useComputed, useSignal } from "@preact/signals-react"; +import { useComputed, useSignal, useSignalEffect } from "@preact/signals-react"; import { useService } from '../../context'; import { uuid } from 'ipmc-core'; import { IConfigurationService, IConfigurationServiceSymbol, IDialogService, IDialogServiceSymbol, IFileExportService, IFileExportServiceSymbol, IPopupService, IPopupServiceSymbol, IProfile } from 'ipmc-interfaces'; @@ -14,13 +14,17 @@ export function ProfileSelector(props: { profile?: IProfile, switchProfile: (nam const dialogService = useService(IDialogServiceSymbol); const configService = useService(IConfigurationServiceSymbol); - const profiles = useSignal<(IProfile & { id: string; })[]>(loadProfiles()); + const profiles = useSignal<(IProfile)[]>([]); - function loadProfiles() { - return configService.getProfiles().map(p => ({ - ...configService.getProfile(p), + useSignalEffect(() => { + loadProfiles(); + }); + + async function loadProfiles(): Promise { + profiles.value = await Promise.all((await configService.getProfiles()).map(async (p) => ({ + ...(await configService.getProfile(p)), id: p, - })); + }))); } const content = useComputed(() => profiles.value.map(p => ( @@ -37,7 +41,7 @@ export function ProfileSelector(props: { profile?: IProfile, switchProfile: (nam .then(r => { if (r) { configService.removeProfile(p.id); - profiles.value = loadProfiles(); + loadProfiles(); } }); }} @@ -52,11 +56,11 @@ export function ProfileSelector(props: { profile?: IProfile, switchProfile: (nam popupService.show({ content: (close) => ( { close(); - profiles.value = loadProfiles(); + loadProfiles(); }} /> ) @@ -81,11 +85,16 @@ export function ProfileSelector(props: { profile?: IProfile, switchProfile: (nam popupService.show({ content: (close) => ( { close(); - profiles.value = loadProfiles(); + loadProfiles(); }} /> ) @@ -102,7 +111,7 @@ export function ProfileSelector(props: { profile?: IProfile, switchProfile: (nam const fileReader = new FileReader(); fileReader.onloadend = () => { configService.setProfile(id, { ...JSON.parse(fileReader.result as string), id: id }); - profiles.value = loadProfiles(); + loadProfiles(); }; fileReader.readAsText(file); }); diff --git a/packages/webui/src/App.tsx b/packages/webui/src/App.tsx index 2210f7f..a33aca3 100644 --- a/packages/webui/src/App.tsx +++ b/packages/webui/src/App.tsx @@ -48,7 +48,7 @@ export function App() { }, getProfiles() { const value = window.localStorage.getItem('profiles'); - return value ? Object.keys(JSON.parse(value)) : []; + return Promise.resolve(value ? Object.keys(JSON.parse(value)) : []); }, setProfile(id, profile) { const profilesString = window.localStorage.getItem('profiles'); @@ -56,6 +56,8 @@ export function App() { profiles[id] = profile; window.localStorage.setItem('profiles', JSON.stringify(profiles)); + + return Promise.resolve(); }, removeProfile(id: string) { const profilesString = window.localStorage.getItem('profiles'); @@ -63,6 +65,8 @@ export function App() { delete profiles[id]; window.localStorage.setItem('profiles', JSON.stringify(profiles)); + + return Promise.resolve(); } }} nodeService={{