From 6ebcf042ce5f95655b70d25957482d888efcfd1a Mon Sep 17 00:00:00 2001 From: Zlatko Fedor Date: Sun, 6 Aug 2023 19:32:31 +0200 Subject: [PATCH] refactored api refactored auto start and stop services fixed auto login # Conflicts: # packages/api-react/package.json # packages/core/package.json # packages/wallets/package.json --- packages/api-react/package.json | 2 +- .../src/@types/ServiceConstructor.ts | 1 + packages/api-react/src/chiaLazyBaseQuery.ts | 11 +- packages/api-react/src/hooks/useService.ts | 101 +++------ packages/api-react/src/hooks/useServices.ts | 11 +- packages/api-react/src/services/client.ts | 7 +- packages/api-react/src/services/daemon.ts | 8 +- packages/api-react/src/services/index.ts | 2 - packages/api/src/Client.ts | 194 +++------------- packages/api/src/services/Daemon.ts | 211 ++++++++++++++++-- packages/api/src/services/Events.ts | 5 +- packages/api/src/services/Service.test.ts | 1 - packages/api/src/services/Service.ts | 7 +- .../api/src/services/WalletService.test.ts | 1 - packages/core/package.json | 5 +- packages/gui/src/components/app/AppState.tsx | 2 +- .../components/settings/SettingsHarvester.tsx | 12 +- packages/wallets/package.json | 4 +- 18 files changed, 290 insertions(+), 295 deletions(-) diff --git a/packages/api-react/package.json b/packages/api-react/package.json index 06ca376a3a..662304f902 100644 --- a/packages/api-react/package.json +++ b/packages/api-react/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@babel/runtime": "7.22.6", + "@chia-network/api": "1.0.0", "bignumber.js": "9.0.2", "core-js": "3.20.3", "debug": "4.3.3", @@ -38,7 +39,6 @@ "@babel/plugin-transform-runtime": "7.22.9", "@babel/preset-env": "7.22.9", "@babel/preset-typescript": "7.22.5", - "@chia-network/api": "1.0.0", "@reduxjs/toolkit": "1.7.1", "@rollup/plugin-babel": "5.3.0", "@rollup/plugin-commonjs": "21.0.1", diff --git a/packages/api-react/src/@types/ServiceConstructor.ts b/packages/api-react/src/@types/ServiceConstructor.ts index 54575da32c..0cbe397b06 100644 --- a/packages/api-react/src/@types/ServiceConstructor.ts +++ b/packages/api-react/src/@types/ServiceConstructor.ts @@ -1,4 +1,5 @@ export default interface ServiceConstructor { new (...args: any[]): any; isClient?: boolean; + isDaemon?: boolean; } diff --git a/packages/api-react/src/chiaLazyBaseQuery.ts b/packages/api-react/src/chiaLazyBaseQuery.ts index 69456e24ea..96c8e7435d 100644 --- a/packages/api-react/src/chiaLazyBaseQuery.ts +++ b/packages/api-react/src/chiaLazyBaseQuery.ts @@ -21,9 +21,15 @@ async function getInstance( } else { const client = await getInstance(Client, api); + if (service.isDaemon) { + return client.daemon as InstanceType; + } + const Service = service; - const serviceInstance = new Service(client); - instances.set(service, serviceInstance); + // client is async operation, so we need to check if instance is already set + if (!instances.has(Service)) { + instances.set(service, new Service(client)); + } } } @@ -64,6 +70,7 @@ const chiaLazyBaseQuery = async < try { const instance = await getInstance(service, api); const arrayArgs = Array.isArray(args) ? args : [args]; + const data = (await instance[command](...arrayArgs)) as TResult; return { diff --git a/packages/api-react/src/hooks/useService.ts b/packages/api-react/src/hooks/useService.ts index 329a4403b2..3f69c11306 100644 --- a/packages/api-react/src/hooks/useService.ts +++ b/packages/api-react/src/hooks/useService.ts @@ -1,15 +1,18 @@ import { ServiceNameValue } from '@chia-network/api'; -import { useEffect, useState, useMemo, useCallback } from 'react'; +import { useEffect, useState, useCallback } from 'react'; -import { useClientStartServiceMutation } from '../services/client'; -import { useStopServiceMutation, useRunningServicesQuery } from '../services/daemon'; +import { useStartServiceMutation, useStopServiceMutation } from '../services/daemon'; -export type ServiceState = 'starting' | 'running' | 'stopping' | 'stopped'; +export enum ServiceState { + STARTING = 'starting', + RUNNING = 'running', + STOPPING = 'stopping', + STOPPED = 'stopped', +} type Options = { keepState?: ServiceState; disabled?: boolean; - disableWait?: boolean; // Don't wait for ping when starting service }; export default function useService( @@ -17,117 +20,81 @@ export default function useService( options: Options = {} ): { isLoading: boolean; - isProcessing: boolean; isRunning: boolean; state: ServiceState; start: () => Promise; stop: () => Promise; - error?: Error | unknown; + error: Error | undefined; service: ServiceNameValue; } { - const { keepState, disabled = false, disableWait = false } = options; + const { keepState, disabled = false } = options; + + const [error, setError] = useState(); + const [state, setState] = useState(ServiceState.STOPPED); - const [isStarting, setIsStarting] = useState(false); - const [isStopping, setIsStopping] = useState(false); - const [startService] = useClientStartServiceMutation(); + const [startService] = useStartServiceMutation(); const [stopService] = useStopServiceMutation(); - const [latestIsProcessing, setLatestIsProcessing] = useState(false); - // isRunning is not working when stopService is called (backend issue) - const { - data: runningServices, - isLoading, - refetch, - error, - } = useRunningServicesQuery(undefined, { - pollingInterval: latestIsProcessing ? 1000 : 10_000, - skip: disabled, - selectFromResult: (state) => ({ - data: state.data, - error: state.error, - isLoading: state.isLoading, - }), - }); - - const isRunning = useMemo( - () => !!(runningServices && runningServices?.includes(service)), - [runningServices, service] - ); - - const isProcessing = isStarting || isStopping; - - useEffect(() => { - setLatestIsProcessing(isProcessing); - }, [isProcessing]); - - let state: ServiceState = 'stopped'; - if (isRunning) { - state = 'running'; - } else if (isStarting) { - state = 'starting'; - } else if (isStopping) { - state = 'stopping'; - } + const isLoading = [ServiceState.STARTING, ServiceState.STOPPING].includes(state); const handleStart = useCallback(async () => { - if (isProcessing) { + if (isLoading || disabled || state === ServiceState.RUNNING) { return; } try { - setIsStarting(true); + setState(ServiceState.STARTING); + await startService({ service, - disableWait, }).unwrap(); - refetch(); + setState(ServiceState.RUNNING); } catch (e) { + setState(ServiceState.STOPPED); + setError(e as Error); console.error(e); - } finally { - setIsStarting(false); } - }, [disableWait, isProcessing, refetch, service, startService]); + }, [isLoading, service, startService, disabled, state]); const handleStop = useCallback(async () => { - if (isProcessing) { + if (isLoading || disabled || state === ServiceState.STOPPED) { return; } try { - setIsStopping(true); + setState(ServiceState.STOPPING); await stopService({ service, }).unwrap(); - refetch(); + setState(ServiceState.STOPPED); } catch (e) { + setState(ServiceState.RUNNING); + setError(e as Error); console.error(e); - } finally { - setIsStopping(false); } - }, [isProcessing, refetch, service, stopService]); + }, [isLoading, service, stopService, disabled, state]); useEffect(() => { - if (disabled || !runningServices) { + if (disabled || isLoading) { return; } - if (keepState === 'running' && keepState !== state && !isProcessing && isRunning === false) { + if (keepState === 'running' && keepState !== state) { handleStart(); - } else if (keepState === 'stopped' && keepState !== state && !isProcessing && isRunning === true) { + } else if (keepState === 'stopped' && keepState !== state) { handleStop(); } - }, [runningServices, keepState, service, state, isProcessing, disabled, isRunning, handleStart, handleStop]); + }, [keepState, service, state, isLoading, disabled, handleStart, handleStop]); return { state, isLoading, - isProcessing, - isRunning, - error, + isRunning: state === ServiceState.RUNNING, start: handleStart, stop: handleStop, service, + error, }; } diff --git a/packages/api-react/src/hooks/useServices.ts b/packages/api-react/src/hooks/useServices.ts index 43ce29a58a..b54d2a265a 100644 --- a/packages/api-react/src/hooks/useServices.ts +++ b/packages/api-react/src/hooks/useServices.ts @@ -12,10 +12,10 @@ type Options = { function getServiceKeepState(service: ServiceNameValue, options: Options): ServiceState | undefined { const { keepRunning, keepStopped } = options; if (keepRunning && keepRunning.includes(service)) { - return 'running'; + return ServiceState.RUNNING; } if (keepStopped && keepStopped.includes(service)) { - return 'stopped'; + return ServiceState.STOPPED; } return undefined; } @@ -68,7 +68,6 @@ export default function useMonitorServices( const datalayerServerState = useService(ServiceName.DATALAYER_SERVER, { ...getServiceOptions(ServiceName.DATALAYER_SERVER, services, options), - disableWait: true, }); const states = [ @@ -87,9 +86,9 @@ export default function useMonitorServices( const isLoading = !!states.find((state) => state.isLoading); const error = states.find((state) => state.error)?.error; - const starting = states.filter((state) => state.state === 'starting').map((state) => state.service); - const stopping = states.filter((state) => state.state === 'stopping').map((state) => state.service); - const running = states.filter((state) => state.state === 'running').map((state) => state.service); + const starting = states.filter((state) => state.state === ServiceState.STARTING).map((state) => state.service); + const stopping = states.filter((state) => state.state === ServiceState.STOPPING).map((state) => state.service); + const running = states.filter((state) => state.state === ServiceState.RUNNING).map((state) => state.service); const objectToReturn = { isLoading, diff --git a/packages/api-react/src/services/client.ts b/packages/api-react/src/services/client.ts index 6a5fc99cbd..e3ec033fc1 100644 --- a/packages/api-react/src/services/client.ts +++ b/packages/api-react/src/services/client.ts @@ -42,12 +42,7 @@ export const clientApi = apiWithTag.injectEndpoints({ } }, }), - - clientStartService: mutation(build, Client, 'startService'), - - clientStopService: mutation(build, Client, 'stopService'), }), }); -export const { useCloseMutation, useGetStateQuery, useClientStartServiceMutation, useClientStopServiceMutation } = - clientApi; +export const { useCloseMutation, useGetStateQuery } = clientApi; diff --git a/packages/api-react/src/services/daemon.ts b/packages/api-react/src/services/daemon.ts index d07a255b66..30080cce15 100644 --- a/packages/api-react/src/services/daemon.ts +++ b/packages/api-react/src/services/daemon.ts @@ -68,13 +68,7 @@ export const daemonApi = apiWithTag.injectEndpoints({ command: 'onKeyringStatusChanged', service: Daemon, onUpdate: (draft, data) => { - // empty base array - draft.splice(0); - - const { status, ...rest } = data; - - // assign new items - Object.assign(draft, rest); + Object.assign(draft, data); }, }, ]), diff --git a/packages/api-react/src/services/index.ts b/packages/api-react/src/services/index.ts index 630cce2cb9..9d15593828 100644 --- a/packages/api-react/src/services/index.ts +++ b/packages/api-react/src/services/index.ts @@ -11,8 +11,6 @@ export const { useCloseMutation, useGetStateQuery, - useClientStartServiceMutation, - useClientStopServiceMutation, } = client; // daemon hooks diff --git a/packages/api/src/Client.ts b/packages/api/src/Client.ts index a3eca5b960..69cd2ccbaa 100644 --- a/packages/api/src/Client.ts +++ b/packages/api/src/Client.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-constant-condition -- Used for waiting for service to start */ -/* eslint-disable no-await-in-loop -- Used for waiting for service to start */ import EventEmitter from 'events'; import debug from 'debug'; @@ -9,7 +7,6 @@ import Message from './Message'; import ConnectionState from './constants/ConnectionState'; import ServiceName, { type ServiceNameValue } from './constants/ServiceName'; import Daemon from './services/Daemon'; -import type Service from './services/Service'; import ErrorData from './utils/ErrorData'; import sleep from './utils/sleep'; @@ -43,19 +40,11 @@ export default class Client extends EventEmitter { } > = new Map(); - private services: Set = new Set(); - - private started: Set = new Set(); - private connectedPromise: Promise | null = null; private connectedPromiseResponse: { resolve: any; reject: any } | null = null; - private connectServicePromise: Map> = new Map(); - - private stopServicePromise: Map> = new Map(); - - private daemon: Daemon; + readonly daemon: Daemon; private closed = false; @@ -63,8 +52,6 @@ export default class Client extends EventEmitter { private reconnectAttempt = 0; - private startingService?: ServiceNameValue; - constructor(options: Options) { super(); @@ -85,10 +72,6 @@ export default class Client extends EventEmitter { this.daemon = new Daemon(this); - this.options.services.forEach((service) => { - this.services.add(service); - }); - if (this.options.services.length) { this.connect(); } @@ -97,14 +80,10 @@ export default class Client extends EventEmitter { getState(): { state: ConnectionState; attempt: number; - startingService?: string; - startedServices: ServiceNameValue[]; } { return { state: this.state, attempt: this.reconnectAttempt, - startingService: this.startingService, - startedServices: Array.from(this.started), }; } @@ -117,10 +96,6 @@ export default class Client extends EventEmitter { this.reconnectAttempt = 0; } - if (state !== ConnectionState.CONNECTING) { - this.startingService = undefined; - } - this.state = state; this.emit('state', this.getState()); } @@ -141,18 +116,6 @@ export default class Client extends EventEmitter { return this.options.debug; } - isStarted(args: { service: ServiceNameValue }) { - const { service } = args; - return this.started.has(service); - } - - addService(args: { service: Service }) { - const { service } = args; - if (!this.services.has(service.name)) { - this.services.add(service.name); - } - } - async connect(reconnect?: boolean) { if (this.closed) { log('Client is permanently closed'); @@ -204,137 +167,15 @@ export default class Client extends EventEmitter { return this.connectedPromise; } - async startService(args: { service: ServiceNameValue; disableWait?: boolean }) { - const { service, disableWait } = args; - - if (this.connectServicePromise.has(service)) { - await this.connectServicePromise.get(service); - return; - } - - const startServiceAction = async () => { - if (this.stopServicePromise.has(service)) { - await this.stopServicePromise.get(service); - } - - if (this.started.has(service)) { - return; - } - - const response = await this.daemon.isRunning({ service }); - if (!response.isRunning) { - log(`Starting service: ${service}`); - await this.daemon.startService({ service }); - } - - // wait for service initialisation - log(`Waiting for ping from service: ${service}`); - if (!disableWait) { - while (true) { - try { - const { data } = await this.send( - new Message({ - command: 'ping', - origin: this.origin, - destination: service, - }), - 1000 - ); - - if (data.success) { - break; - } - } catch (error) { - await sleep(1000); - } - } - - log(`Service: ${service} started`); - } - - this.started.add(service); - this.emit('state', this.getState()); - }; - - const startServiceTask = startServiceAction(); - this.connectServicePromise.set(service, startServiceTask); - await startServiceTask.finally(() => { - this.connectServicePromise.delete(service); - }); - } - - private async startServices() { - if (!this.connected) { - return; - } - - const services = Array.from(this.services); - - await Promise.all(services.map(async (service) => this.startService({ service }))); - } - - async stopService(args: { service: ServiceNameValue; disableWait?: boolean }) { - const { service, disableWait } = args; - - const stopServiceAction = async () => { - if (!this.started.has(service)) { - log(`Service: ${service} is already stopped`); - return; - } - - const response = await this.daemon.isRunning({ service }); - if (response.isRunning) { - log(`Closing down service: ${service}`); - await this.daemon.stopService({ service }); - } - - // wait for service initialisation - log(`Waiting for service: ${service}`); - if (!disableWait) { - while (true) { - try { - const { data } = await this.send( - new Message({ - command: 'ping', - origin: this.origin, - destination: service, - }), - 1000 - ); - - if (data.success) { - await sleep(1000); - } - } catch (error) { - break; - } - } - } - - log(`Service: ${service} stopped`); - - this.started.delete(service); - this.connectServicePromise.delete(service); - this.emit('state', this.getState()); - }; - - const stopServiceTask = stopServiceAction(); - this.stopServicePromise.set(service, stopServiceTask); - await stopServiceTask.finally(() => { - this.stopServicePromise.delete(service); - }); - } - private handleOpen = async () => { this.connected = true; - this.started.clear(); + // todo clear deamon running services because it has old state this.emit('state', this.getState()); this.changeState(ConnectionState.CONNECTED); await this.registerService(ServiceName.EVENTS); - await this.startServices(); if (this.connectedPromiseResponse) { this.connectedPromiseResponse.resolve(); @@ -349,7 +190,6 @@ export default class Client extends EventEmitter { private handleClose = () => { this.connected = false; this.connectedPromise = null; - this.connectServicePromise.clear(); this.requests.forEach((request) => { request.reject(new Error(`Connection closed`)); @@ -417,16 +257,34 @@ export default class Client extends EventEmitter { } = this; const currentTimeout = timeout ?? defaultTimeout; + const serviceName = message.destination; if (!connected) { log('API is not connected trying to connect'); await this.connect(); } - if (message.destination !== ServiceName.DAEMON && message.command !== 'ping') { - const isServiceLaunching = this.connectServicePromise.has(message.destination); - if (isServiceLaunching) { - await this.connectServicePromise.get(message.destination); + if (serviceName !== ServiceName.DAEMON && message.command !== 'ping') { + // when client was closed we need to wait for new connection + if (this.closed) { + await new Promise((resolve) => { + // wait for connected event + const handleStateChange = (state: { state: ConnectionState }) => { + if (state.state === ConnectionState.CONNECTED) { + this.off('state', handleStateChange); + resolve(undefined); + } + }; + + this.on('state', handleStateChange); + }); + } + + // verify if service is started + const isStarted = await this.daemon.isServiceStarted(serviceName); + if (!isStarted) { + await this.daemon.waitForKeyringUnlocked(); + await this.daemon.startService({ service: serviceName }); } } @@ -463,11 +321,11 @@ export default class Client extends EventEmitter { return; } - await Promise.all(Array.from(this.started).map(async (service) => this.stopService({ service }))); + await this.daemon.stopAllServices(); await this.daemon.exit(); this.ws.close(); - // this.changeState(ConnectionState.DISCONNECTED); + this.changeState(ConnectionState.DISCONNECTED); } } diff --git a/packages/api/src/services/Daemon.ts b/packages/api/src/services/Daemon.ts index e65160ef15..9e66a10aab 100644 --- a/packages/api/src/services/Daemon.ts +++ b/packages/api/src/services/Daemon.ts @@ -1,20 +1,40 @@ +import debug from 'debug'; + import KeyData from '../@types/KeyData'; import KeyringStatus from '../@types/KeyringStatus'; import PlotQueueItem from '../@types/PlotQueueItem'; import { PlottersApi } from '../@types/Plotter'; +import Response from '../@types/Response'; import WalletAddress from '../@types/WalletAddress'; import type Client from '../Client'; -import type Message from '../Message'; +import Message from '../Message'; import ServiceName, { type ServiceNameValue } from '../constants/ServiceName'; +import sleep from '../utils/sleep'; import Service from './Service'; import type { Options } from './Service'; +const log = debug('chia-api:daemon'); + export default class Daemon extends Service { + static isDaemon = true; + + private serviceStartPromises: Partial< + Record< + ServiceNameValue, + Promise<{ + service: ServiceNameValue; + }> + > + > = {}; + + private serviceStopPromises: Partial>> = {}; + + #runningServices: Partial> = {}; + + private waitForKeyringUnlockedPromise: Promise | null = null; + constructor(client: Client, options?: Options) { - super(ServiceName.DAEMON, client, { - skipAddService: true, - ...options, - }); + super(ServiceName.DAEMON, client, options); } registerService(args: { service: ServiceNameValue }) { @@ -23,14 +43,83 @@ export default class Daemon extends Service { }>('register_service', args); } - startService(args: { service: ServiceNameValue; testing?: boolean }) { - return this.command<{ - service: ServiceNameValue; - }>('start_service', args); + async startService(args: { service: ServiceNameValue; testing?: boolean; noWait?: boolean }) { + const { noWait, ...rest } = args; + const { service } = args; + + if (noWait) { + return this.command<{ + service: ServiceNameValue; + }>('start_service', rest); + } + + if (service in this.serviceStartPromises) { + return this.serviceStartPromises[service]; + } + + const startServiceAndWait = async () => { + const response = await this.command<{ + service: ServiceNameValue; + }>('start_service', rest); + + // wait for service to be running + await this.waitForService(service, true); + + // remove service from serviceStartPromises + if (service in this.serviceStartPromises) { + delete this.serviceStartPromises[service]; + } + + this.#runningServices[service] = true; + + return response; + }; + + const promise = startServiceAndWait(); + + this.serviceStartPromises[service] = promise; + + return promise; + } + + async stopService(args: { service: ServiceNameValue; noWait?: boolean }) { + // remove service from servicesState + const { noWait, ...rest } = args; + const { service } = args; + + this.#runningServices[service] = false; + + if (noWait) { + return this.command('stop_service', rest); + } + + if (service in this.serviceStopPromises) { + return this.serviceStopPromises[service]; + } + + const stopServiceAndWait = async () => { + const response = await this.command('stop_service', rest); + + // wait for service to be stopped + await this.waitForService(service, false); + + // remove service from serviceStartPromises + if (service in this.serviceStopPromises) { + delete this.serviceStopPromises[service]; + } + + return response; + }; + + const promise = stopServiceAndWait(); + + this.serviceStopPromises[service] = promise; + + return promise; } - stopService(args: { service: ServiceNameValue }) { - return this.command('stop_service', args); + isServiceStarted(service: ServiceNameValue) { + return service in this.#runningServices && this.#runningServices[service]; } isRunning(args: { service: ServiceNameValue }) { @@ -39,12 +128,50 @@ export default class Daemon extends Service { }>('is_running', args); } - runningServices() { + async runningServices() { return this.command<{ runningServices: [string]; }>('running_services'); } + async waitForService(service: ServiceNameValue, waitForStart: boolean, maxRetries: number = 600) { + // 10 minutes + const { client } = this; + + if (maxRetries <= 0) { + throw new Error(`Max retries reached. Service: ${service} did not start`); + } + + if (waitForStart) { + try { + const { data } = await client.send( + new Message({ + command: 'ping', + origin: client.origin, + destination: service, + }), + 1000 + ); + + if (data.success) { + log(`Service: ${service} started`); + return; + } + } catch (error) { + log(`Service ping: ${service} failed. ${error.message}`); + } + } else { + const { isRunning } = await this.isRunning({ service }); + if (!isRunning) { + log(`Service: ${service} stopped`); + return; + } + } + + await sleep(1000); + await this.waitForService(service, waitForStart, maxRetries - 1); + } + addPrivateKey(args: { mnemonic: string; label?: string }) { return this.command<{ fingerprint: string; @@ -84,7 +211,7 @@ export default class Daemon extends Service { return this.command('delete_label', args); } - keyringStatus() { + async keyringStatus() { return this.command('keyring_status'); } @@ -230,8 +357,64 @@ export default class Daemon extends Service { return this.command('exit'); } + async stopAllServices() { + const { runningServices } = (await this.runningServices()) as { runningServices: ServiceNameValue[] }; + + return Promise.all( + runningServices.map((service) => { + if (service.startsWith('chia_')) { + return this.stopService({ service }); + } + + return undefined; + }) + ); + } + onKeyringStatusChanged(callback: (data: any, message: Message) => void, processData?: (data: any) => any) { - return this.onStateChanged('keyring_status_changed', callback, processData); + return this.onCommand('keyring_status_changed', callback, processData); + } + + async waitForKeyringUnlocked(): Promise { + const checkKeyringStatusAndWait = async (resolve: (value: void) => void, reject: (reason: Error) => void) => { + let unsubscribe: undefined | (() => void); + + try { + unsubscribe = this.onKeyringStatusChanged((data: any) => { + if (data.isKeyringLocked === false) { + if (unsubscribe) { + unsubscribe(); + } + resolve(); + } + }); + + const { isKeyringLocked } = await this.keyringStatus(); + if (!isKeyringLocked) { + if (unsubscribe) { + unsubscribe(); + } + resolve(); + } + } catch (err) { + if (unsubscribe) { + unsubscribe(); + } + reject(err as Error); + } + }; + + if (this.waitForKeyringUnlockedPromise) { + return this.waitForKeyringUnlockedPromise; + } + + this.waitForKeyringUnlockedPromise = new Promise((resolve, reject) => { + checkKeyringStatusAndWait(resolve, reject); + }).finally(() => { + this.waitForKeyringUnlockedPromise = null; + }); + + return this.waitForKeyringUnlockedPromise; } getVersion() { diff --git a/packages/api/src/services/Events.ts b/packages/api/src/services/Events.ts index 3eafecb096..ef5a9d18e6 100644 --- a/packages/api/src/services/Events.ts +++ b/packages/api/src/services/Events.ts @@ -5,9 +5,6 @@ import type { Options } from './Service'; export default class Events extends Service { constructor(client: Client, options?: Options) { - super(ServiceName.EVENTS, client, { - skipAddService: true, - ...options, - }); + super(ServiceName.EVENTS, client, options); } } diff --git a/packages/api/src/services/Service.test.ts b/packages/api/src/services/Service.test.ts index 9ae0500cb9..d27693842d 100644 --- a/packages/api/src/services/Service.test.ts +++ b/packages/api/src/services/Service.test.ts @@ -23,7 +23,6 @@ describe('Service', () => { client = { origin: 'test_origin', - addService: jest.fn(), on: jest.fn(), send: jest.fn(), }; diff --git a/packages/api/src/services/Service.ts b/packages/api/src/services/Service.ts index b2db89ecf7..4306e13895 100644 --- a/packages/api/src/services/Service.ts +++ b/packages/api/src/services/Service.ts @@ -8,7 +8,6 @@ import { type ServiceNameValue } from '../constants/ServiceName'; export type Options = { origin?: ServiceNameValue; - skipAddService?: boolean; }; export default abstract class Service extends EventEmitter { @@ -23,16 +22,12 @@ export default abstract class Service extends EventEmitter { constructor(name: ServiceNameValue, client: Client, options: Options = {}, onInit?: () => Promise) { super(); - const { origin, skipAddService } = options; + const { origin } = options; this.client = client; this.name = name; this.origin = origin ?? client.origin; - if (!skipAddService) { - client.addService({ service: this }); - } - client.on('message', this.handleMessage); this.#readyPromise = new Promise((resolve, reject) => { diff --git a/packages/api/src/services/WalletService.test.ts b/packages/api/src/services/WalletService.test.ts index 3d7d3511e6..c8e207db35 100644 --- a/packages/api/src/services/WalletService.test.ts +++ b/packages/api/src/services/WalletService.test.ts @@ -20,7 +20,6 @@ describe('WalletService', () => { client = { origin: 'test_origin', - addService: jest.fn(), on: jest.fn(), send: jest.fn(), }; diff --git a/packages/core/package.json b/packages/core/package.json index 2c3c8680cd..6ea60ade2b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,6 +23,8 @@ }, "dependencies": { "@babel/runtime": "7.22.6", + "@chia-network/api": "1.0.0", + "@chia-network/api-react": "1.0.0", "@chia-network/icons": "1.0.0", "@emoji-mart/data": "1.1.2", "@fontsource/roboto": "4.5.1", @@ -59,8 +61,6 @@ "@babel/preset-env": "7.22.9", "@babel/preset-react": "7.22.5", "@babel/preset-typescript": "7.22.5", - "@chia-network/api": "1.0.0", - "@chia-network/api-react": "1.0.0", "@electron/remote": "2.0.8", "@emotion/react": "11.10.4", "@emotion/styled": "11.10.4", @@ -108,6 +108,7 @@ "peerDependencies": { "@chia-network/api": "1.0.0", "@chia-network/api-react": "1.0.0", + "@chia-network/icons": "1.0.0", "@electron/remote": "2.0.8", "@lingui/core": "4.2.1", "@lingui/react": "4.2.1", diff --git a/packages/gui/src/components/app/AppState.tsx b/packages/gui/src/components/app/AppState.tsx index c73f9fcdab..d974ca97a1 100644 --- a/packages/gui/src/components/app/AppState.tsx +++ b/packages/gui/src/components/app/AppState.tsx @@ -170,7 +170,7 @@ export default function AppState(props: Props) { Closing down services - {ALL_SERVICES.filter((service) => !!clientState?.startedServices.includes(service)).map((service) => ( + {servicesState.running.map((service) => ( {ServiceHumanName[service]} diff --git a/packages/gui/src/components/settings/SettingsHarvester.tsx b/packages/gui/src/components/settings/SettingsHarvester.tsx index 732ba4daf4..b0e225dce9 100644 --- a/packages/gui/src/components/settings/SettingsHarvester.tsx +++ b/packages/gui/src/components/settings/SettingsHarvester.tsx @@ -3,8 +3,8 @@ import { useGetHarvesterConfigQuery, useGetPlottersQuery, useUpdateHarvesterConfigMutation, - useClientStartServiceMutation, - useClientStopServiceMutation, + useStartServiceMutation, + useStopServiceMutation, } from '@chia-network/api-react'; import { ButtonLoading, Flex, SettingsSection, SettingsTitle, SettingsText } from '@chia-network/core'; import { Trans } from '@lingui/macro'; @@ -18,8 +18,8 @@ export default function SettingsHarvester() { const { data: plotters } = useGetPlottersQuery(); const { data, isLoading } = useGetHarvesterConfigQuery(); const [updateHarvesterConfig, { isLoading: isUpdating }] = useUpdateHarvesterConfigMutation(); - const [startService, { isLoading: isStarting }] = useClientStartServiceMutation(); - const [stopService, { isLoading: isStopping }] = useClientStopServiceMutation(); + const [startService, { isLoading: isStarting }] = useStartServiceMutation(); + const [stopService, { isLoading: isStopping }] = useStopServiceMutation(); const [message, setMessage] = React.useState(false); const [configUpdateRequests, setConfigUpdateRequests] = React.useState({ useGpuHarvesting: null, @@ -186,11 +186,11 @@ export default function SettingsHarvester() { return; } - await stopService({ service: ServiceName.HARVESTER, disableWait: true }).catch(onError); + await stopService({ service: ServiceName.HARVESTER }).catch(onError); if (error) { return; } - await startService({ service: ServiceName.HARVESTER, disableWait: true }).catch(onError); + await startService({ service: ServiceName.HARVESTER }).catch(onError); if (error) { return; } diff --git a/packages/wallets/package.json b/packages/wallets/package.json index 8dba02a7fd..fed23f5e2e 100644 --- a/packages/wallets/package.json +++ b/packages/wallets/package.json @@ -23,6 +23,7 @@ "dependencies": { "@babel/runtime": "7.22.6", "@chia-network/api": "1.0.0", + "@chia-network/api-react": "1.0.0", "@chia-network/core": "1.0.0", "@chia-network/icons": "1.0.0", "@lingui/macro": "4.2.1", @@ -54,7 +55,6 @@ "@babel/preset-env": "7.22.9", "@babel/preset-react": "7.22.5", "@babel/preset-typescript": "7.22.5", - "@chia-network/api-react": "1.0.0", "@lingui/cli": "4.2.1", "@lingui/core": "4.2.1", "@lingui/react": "4.2.1", @@ -91,7 +91,9 @@ "typescript": "4.9.5" }, "peerDependencies": { + "@chia-network/api": "1.0.0", "@chia-network/api-react": "1.0.0", + "@chia-network/core": "1.0.0", "@lingui/core": "4.2.1", "@lingui/react": "4.2.1", "@mui/icons-material": "5.11.16",