Notifications implementation and general improvements

This commit is contained in:
everoddandeven 2024-10-18 22:41:53 +02:00
parent 68927d3276
commit 5996037153
17 changed files with 341 additions and 175 deletions

View file

@ -1,4 +1,4 @@
import { app, BrowserWindow, ipcMain, screen, dialog, Tray, Menu, MenuItemConstructorOptions, IpcMainInvokeEvent } from 'electron';
import { app, BrowserWindow, ipcMain, screen, dialog, Tray, Menu, MenuItemConstructorOptions, IpcMainInvokeEvent, Notification, NotificationConstructorOptions } from 'electron';
import { ChildProcessWithoutNullStreams, exec, ExecException, spawn } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
@ -248,22 +248,46 @@ function getMonerodVersion(monerodFilePath: string): void {
})
}
function checkValidMonerodPath(monerodPath: string): void {
let foundUsage: boolean = false;
const monerodProcess = spawn(monerodPath, ['--help']);
monerodProcess.stderr.on('data', (data) => {
win?.webContents.send('on-check-valid-monerod-path', false);
});
monerodProcess.stdout.on('data', (data) => {
if (`${data}`.includes('monerod [options|settings] [daemon_command...]')) {
foundUsage = true;
}
});
monerodProcess.on('close', (code: number) => {
win?.webContents.send('on-check-valid-monerod-path', foundUsage);
})
}
let moneroFirstStdout: boolean = true;
function startMoneroDaemon(commandOptions: string[]): ChildProcessWithoutNullStreams {
const monerodPath = commandOptions.shift();
if (!monerodPath) {
win?.webContents.send('monero-stderr', `Invalid monerod path provided: ${monerodPath}`);
const error = `Invalid monerod path provided: ${monerodPath}`;
win?.webContents.send('monero-stderr', error);
throw new Error("Invalid monerod path provided");
}
if (monerodProcess != null) {
win?.webContents.send('monero-stderr', 'Monerod already started');
const error: string = 'Monero daemon already started';
win?.webContents.send('monero-stderr', error);
throw new Error("Monerod already started");
}
console.log("Starting monerod daemon with options: " + commandOptions.join(" "));
const message: string = "Starting monerod daemon with options: " + commandOptions.join(" ");
console.log(message);
moneroFirstStdout = true;
@ -471,6 +495,10 @@ const extractTarBz2 = (filePath: string, destination: string): Promise<string> =
});
};
function showNotification(options?: NotificationConstructorOptions): void {
new Notification(options).show();
}
try {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
@ -587,6 +615,14 @@ try {
monitorMonerod();
});
ipcMain.handle('check-valid-monerod-path', (event: IpcMainInvokeEvent, path: string) => {
checkValidMonerodPath(path);
})
ipcMain.handle('show-notification', (event: IpcMainInvokeEvent, options?: NotificationConstructorOptions) => {
showNotification(options);
});
} catch (e) {
// Catch Error
console.error(e);

View file

@ -46,6 +46,12 @@ contextBridge.exposeInMainWorld('electronAPI', {
onDownloadProgress: (callback) => {
ipcRenderer.on('download-progress', callback);
},
checkValidMonerodPath: (path) => {
ipcRenderer.invoke('check-valid-monerod-path', path);
},
onCheckValidMonerodPath: (callback) => {
ipcRenderer.on('on-check-valid-monerod-path', callback);
},
selectFolder: () => {
ipcRenderer.invoke('select-folder')
},
@ -70,6 +76,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
gotOsType: (callback) => {
ipcRenderer.on('got-os-type', callback);
},
showNotification: (options) => {
ipcRenderer.invoke('show-notification', options);
},
quit: () => {
ipcRenderer.invoke('quit');
}

View file

@ -1,6 +1,6 @@
import { EventEmitter, Injectable, NgZone } from '@angular/core';
import { DaemonService } from './daemon.service';
import { BlockCount, BlockHeader, Chain, Connection, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PeerInfo, ProcessStats, PublicNode, SyncInfo, TxBacklogEntry, TxPool } from '../../../../common';
import { BlockCount, BlockHeader, Chain, Connection, CoreIsBusyError, DaemonInfo, MinerData, MiningStatus, NetStats, NetStatsHistory, PeerInfo, ProcessStats, PublicNode, SyncInfo, TimeUtils, TxBacklogEntry, TxPool } from '../../../../common';
@Injectable({
providedIn: 'root'
@ -332,36 +332,6 @@ export class DaemonDataService {
public syncDisabledByWifiPolicy: boolean = false;
public syncDisabledByPeriodPolicy: boolean = false;
private isInTimeRange(fromHours: string, toHours: string): boolean {
const now = new Date();
// Estraiamo l'ora e i minuti dalla stringa in formato hh:mm
const [fromHour, fromMinute] = fromHours.split(":").map(Number);
const [toHour, toMinute] = toHours.split(":").map(Number);
// Otteniamo l'ora corrente in ore e minuti
const currentHour = now.getHours();
const currentMinute = now.getMinutes();
// Creiamo oggetti Date per le ore 'from', 'to', e l'ora attuale
const currentTime = new Date();
currentTime.setHours(currentHour, currentMinute, 0, 0);
const fromTime = new Date();
fromTime.setHours(fromHour, fromMinute, 0, 0);
const toTime = new Date();
toTime.setHours(toHour, toMinute, 0, 0);
// Gestione del caso in cui la fascia oraria attraversi la mezzanotte
if (fromTime > toTime) {
// Se l'ora attuale è dopo 'fromTime' o prima di 'toTime'
return currentTime >= fromTime || currentTime <= toTime;
} else {
// Caso normale: la fascia oraria è nello stesso giorno
return currentTime >= fromTime && currentTime <= toTime;
}
}
private async refresh(): Promise<void> {
if (this.refreshing || this.tooEarlyForRefresh) {
@ -407,14 +377,20 @@ export class DaemonDataService {
this.syncDisabledByWifiPolicy = false;
}
if (!syncAlreadyDisabled && !this.syncDisabledByPeriodPolicy && settings.syncPeriodEnabled && !this.isInTimeRange(settings.syncPeriodFrom, settings.syncPeriodTo)) {
if (!syncAlreadyDisabled && !this.syncDisabledByWifiPolicy && !this.syncDisabledByPeriodPolicy && settings.syncPeriodEnabled && !TimeUtils.isInTimeRange(settings.syncPeriodFrom, settings.syncPeriodTo)) {
await this.daemonService.disableSync();
this.syncDisabledByPeriodPolicy = true;
}
else if (syncAlreadyDisabled && this.syncDisabledByPeriodPolicy && settings.syncPeriodEnabled && this.isInTimeRange(settings.syncPeriodFrom, settings.syncPeriodTo)) {
else if (syncAlreadyDisabled && !this.syncDisabledByWifiPolicy && this.syncDisabledByPeriodPolicy && settings.syncPeriodEnabled && TimeUtils.isInTimeRange(settings.syncPeriodFrom, settings.syncPeriodTo)) {
await this.daemonService.enableSync();
this.syncDisabledByPeriodPolicy = false;
}
else if (syncAlreadyDisabled && !this.syncDisabledByWifiPolicy && settings.syncPeriodEnabled && !TimeUtils.isInTimeRange(settings.syncPeriodFrom, settings.syncPeriodTo)) {
this.syncDisabledByPeriodPolicy = true;
}
else {
this.syncDisabledByPeriodPolicy = false;
}
this.syncStart.emit({ first: this._firstRefresh });

View file

@ -78,7 +78,7 @@ import { TxInfo } from '../../../../common/TxInfo';
import { DaemonSettings } from '../../../../common/DaemonSettings';
import { MethodNotFoundError } from '../../../../common/error/MethodNotFoundError';
import { openDB, IDBPDatabase } from "idb"
import { PeerInfo, ProcessStats, TxPool } from '../../../../common';
import { PeerInfo, ProcessStats, TimeUtils, TxPool } from '../../../../common';
import { MoneroInstallerService } from '../monero-installer/monero-installer.service';
@Injectable({
@ -124,6 +124,7 @@ export class DaemonService {
public readonly onDaemonStatusChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
public readonly onDaemonStopStart: EventEmitter<void> = new EventEmitter<void>();
public readonly onDaemonStopEnd: EventEmitter<void> = new EventEmitter<void>();
public readonly onSavedSettings: EventEmitter<DaemonSettings> = new EventEmitter<DaemonSettings>();
private isRunningPromise?: Promise<boolean>;
@ -169,6 +170,12 @@ export class DaemonService {
public async disableSync(): Promise<void> {
this.disablingSync = true;
window.electronAPI.showNotification({
title: 'Disabling sync',
body: 'Node sync is about to be disabled',
closeButtonText: 'Dismiss'
});
try {
const running: boolean = await this.isRunning();
@ -185,9 +192,21 @@ export class DaemonService {
this.settings.noSync = true;
await this.startDaemon(this.settings);
window.electronAPI.showNotification({
title: 'Sync disabled',
body: 'Node sync disabled successfully',
closeButtonText: 'Dismiss'
});
}
catch(error: any) {
console.error(error);
window.electronAPI.showNotification({
title: 'Error',
body: 'An error occurred while disabling sync',
closeButtonText: 'Dimiss'
})
}
this.disablingSync = false;
@ -246,6 +265,7 @@ export class DaemonService {
public async saveSettings(settings: DaemonSettings, restartDaemon: boolean = true): Promise<void> {
const db = await this.openDbPromise;
await db.put(this.storeName, { id: 1, ...settings });
this.onSavedSettings.emit(settings);
if (restartDaemon) {
const running = await this.isRunning();
@ -263,6 +283,22 @@ export class DaemonService {
}
}
public async checkValidMonerodPath(path: string): Promise<boolean> {
if (path == null || path == undefined || path.replace(' ', '') == '') {
return false;
}
const checkPromise = new Promise<boolean>((resolve) => {
window.electronAPI.onCheckValidMonerodPath((event: any, valid: boolean) => {
resolve(valid);
});
});
window.electronAPI.checkValidMonerodPath(path);
return await checkPromise;
}
public async getSettings(): Promise<DaemonSettings> {
const db = await this.openDbPromise;
const result = await db.get(this.storeName, 1);
@ -359,10 +395,10 @@ export class DaemonService {
this.settings.noSync = true;
}
else if (!this.settings.noSync && !this.settings.syncOnWifi && !await this.isWifiConnected()) {
console.log("Enabling sync ...");
else if (!this.settings.noSync && this.settings.syncPeriodEnabled && !TimeUtils.isInTimeRange(this.settings.syncPeriodFrom, this.settings.syncPeriodTo)) {
console.log("Disabling sync ...");
this.settings.noSync = false;
this.settings.noSync = true;
}
const startPromise = new Promise<void>((resolve, reject) => {
@ -370,15 +406,25 @@ export class DaemonService {
console.debug(event);
if (started) {
console.log("Daemon started");
console.log("monerod started");
this.delay(3000).then(() => {
this.isRunning(true).then((running: boolean) => {
window.electronAPI.showNotification({
title: 'Daemon started',
body: 'Successfully started daemon',
closeButtonText: 'Dismiss'
});
this.onDaemonStatusChanged.emit(running);
this.startedAt = new Date();
this.starting = false;
resolve();
}).catch((error: any) => {
console.error(error);
window.electronAPI.showNotification({
title: 'Daemon error',
body: 'An error occurred while checking daemon status',
closeButtonText: 'Dismiss'
});
this.onDaemonStatusChanged.emit(false);
this.startedAt = undefined;
this.starting = false;
@ -391,12 +437,15 @@ export class DaemonService {
}
else {
console.log("Daemon not started");
window.electronAPI.showNotification({
title: 'Daemon Error',
body: 'Could not start monerod'
});
this.onDaemonStatusChanged.emit(false);
this.startedAt = undefined;
this.starting = false;
reject('Could not start daemon');
}
})
});
@ -706,33 +755,19 @@ export class DaemonService {
}
else if (dontUseRpc) {
const monerodPath: string = (await this.getSettings()).monerodPath;
const wdw = (window as any);
if (monerodPath == '') {
throw new Error("Daemon not configured");
}
return new Promise<DaemonVersion>((resolve, reject) => {
if (this.electronService.isElectron) {
this.electronService.ipcRenderer.on('on-monerod-version', (event, version: string) => {
resolve(DaemonVersion.parse(version));
});
this.electronService.ipcRenderer.on('on-monerod-version-error', (event, version: string) => {
reject(version);
});
this.electronService.ipcRenderer.send('get-monerod-version', monerodPath);
}
else if (wdw.electronAPI && wdw.electronAPI.getMoneroVersion) {
wdw.electronAPI.onMoneroVersion((event: any, version: string) => {
window.electronAPI.onMoneroVersion((event: any, version: string) => {
resolve(DaemonVersion.parse(version));
})
wdw.electronAPI.onMoneroVersionError((event: any, error: string) => {
window.electronAPI.onMoneroVersionError((event: any, error: string) => {
reject(error);
});
wdw.electronAPI.getMoneroVersion(monerodPath);
}
window.electronAPI.getMoneroVersion(monerodPath);
});
}
@ -946,16 +981,6 @@ export class DaemonService {
}
throw new Error('Could not stop daemon');
/*
if (this.electronService.isElectron) {
return;
}
this.daemonRunning = false;
this.onDaemonStatusChanged.emit(false);
this.onDaemonStopEnd.emit();
*/
}
public async setLimit(limitDown: number, limitUp: number): Promise<{ limitDown: number, limitUp: number }> {
@ -1073,10 +1098,8 @@ export class DaemonService {
throw new Error("Download path not configured");
}
//const downloadUrl = 'https://downloads.getmonero.org/cli/linux64'; // Cambia in base al sistema
const destination = settings.downloadUpgradePath; // Aggiorna con il percorso desiderato
const moneroFolder = await this.installer.downloadMonero(destination);
const moneroFolder = await this.installer.downloadMonero(destination, settings.monerodPath != '');
settings.monerodPath = `${moneroFolder}/monerod`;

View file

@ -16,30 +16,36 @@ export class MoneroInstallerService {
linuxriscv64: 'https://downloads.getmonero.org/cli/linuxriscv64'
};
private _upgrading: boolean = false;
private _progress: { progress: number, status: string } = { progress: 0, status: 'Starting upgrade' }
private alreadyConfigured: boolean = false;
public get upgrading(): boolean {
return this._upgrading;
return this._downloading && this.alreadyConfigured;
}
public get installing(): boolean {
return this._downloading && !this.alreadyConfigured;
}
private _downloading: boolean = false;
public get progress(): { progress: number, status: string } {
return this._progress;
}
constructor(private ngZone: NgZone) {}
public async downloadMonero(destination: string): Promise<string> {
this._upgrading = true;
public async downloadMonero(destination: string, alreadyConfigured: boolean): Promise<string> {
this.alreadyConfigured = alreadyConfigured;
this._downloading = true;
const downloadUrl = await this.getMoneroDownloadLink();
try {
const result = await new Promise<string>((resolve, reject) => {
const wdw = (window as any);
if (wdw.electronAPI && wdw.electronAPI.onDownloadProgress && wdw.electronAPI.downloadMonerod) {
wdw.electronAPI.onDownloadProgress((event: any, progress: { progress: number, status: string }) => {
//console.log(`${progress.progress.toFixed(2)} % ${progress.status}`);
window.electronAPI.onDownloadProgress((event: any, progress: { progress: number, status: string }) => {
this.ngZone.run(() => {
this._progress = progress;
});
@ -54,20 +60,18 @@ export class MoneroInstallerService {
});
wdw.electronAPI.downloadMonerod(downloadUrl, destination);
}
window.electronAPI.downloadMonerod(downloadUrl, destination);
});
this._upgrading = false;
this._downloading = false;
return result;
}
catch (error) {
console.error(error);
this._upgrading = false;
this._downloading = false;
throw error;
}
}
private async getMoneroDownloadLink(): Promise<string> {

View file

@ -11,7 +11,7 @@
</ul>
</div>
</div>
<div *ngIf="daemonRunning && !daemonStopping" class="tab-content" id="pills-tabContent">
<div *ngIf="daemonRunning && !daemonStopping && !daemonStarting" class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-last-block-header" role="tabpanel" aria-labelledby="pills-last-block-header-tab" tabindex="0">
<div *ngIf="getLastBlockError !== ''" class="alert alert-danger d-flex align-items-center justify-content-center text-center" role="alert">

View file

@ -21,6 +21,10 @@ export class BlockchainComponent implements AfterViewInit {
return this.daemonData.stopping;
}
public get daemonStarting(): boolean {
return this.daemonService.starting;
}
public get lastBlockHeader(): BlockHeader | undefined {
return this.daemonData.lastBlockHeader;
}

View file

@ -12,14 +12,14 @@
</div>
<app-daemon-not-running></app-daemon-not-running>
<div *ngIf="daemonRunning && syncDisabledByWifiPolicy" class="alert alert-warning d-flex align-items-center justify-content-center text-center" role="alert">
<div *ngIf="daemonRunning && !stoppingDaemon && syncDisabledByWifiPolicy" class="alert alert-warning d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
Sync on wifi is disabled
</div>
</div>
<div *ngIf="daemonRunning && syncDisabledByPeriodPolicy" class="alert alert-warning d-flex align-items-center justify-content-center text-center" role="alert">
<div *ngIf="daemonRunning && !stoppingDaemon && syncDisabledByPeriodPolicy" class="alert alert-warning d-flex align-items-center justify-content-center text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle m-2"></i></h4>&nbsp;&nbsp;
<div>
Sync disabled from {{ syncDisabledTo }} to {{ syncDisabledFrom }}

View file

@ -1,5 +1,4 @@
import { EventEmitter, Injectable, NgZone } from '@angular/core';
import { ElectronService } from '../../core/services';
import { LogCategories } from '../../../common';
@Injectable({
@ -11,23 +10,14 @@ export class LogsService {
public readonly maxLines: number = 250;
public readonly categories: LogCategories = new LogCategories();
constructor(private electronService: ElectronService, private ngZone: NgZone) {
const wdw = (window as any);
if (this.electronService.isElectron) {
this.electronService.ipcRenderer.on('monero-stdout', (event, message: string) => this.log(message));
this.electronService.ipcRenderer.on('monero-stderr', (event, message: string) => this.log(message));
}
else if (wdw.electronAPI && wdw.electronAPI.onMoneroStdout) {
wdw.electronAPI.onMoneroStdout((event: any, message: string) => {
constructor(private ngZone: NgZone) {
window.electronAPI.onMoneroStdout((event: any, message: string) => {
this.log(message);
});
}
}
public cleanLog(message: string): string {
return message.replace(/\u001b\[[0-9;]*m/g, '').replace(/[\r\n]+/g, '\n').trim(); // eslint-disable-line
//return message.replace(/[\r\n]+/g, '\n').trim();
}
public log(message: string): void {

View file

@ -1,48 +1,67 @@
<div *ngIf="!daemonRunning || stoppingDaemon || restartingDaemon" class="h-100 p-5 text-bg-dark rounded-3 m-4 text-center">
<h2 *ngIf="!daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && daemonConfigured && !quittingDaemon"><i class="bi bi-exclamation-diamond m-4"></i> Daemon not running</h2>
<h2 *ngIf="!daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !daemonConfigured && !quittingDaemon"><i class="bi bi-exclamation-diamond m-4"></i> Daemon not configured or installed</h2>
<h2 *ngIf="restartingDaemon && !upgrading"><i class="bi bi-arrow-clockwise m-4"></i> Daemon restarting</h2>
<h2 *ngIf="stoppingDaemon && !upgrading && !quittingDaemon"><i class="bi bi-stop-fill m-4"></i> Daemon is stopping</h2>
<h2 *ngIf="upgrading"><i class="bi bi-cloud-download m-4"></i> Daemon is upgrading</h2>
<h2 *ngIf="quittingDaemon"><i class="bi bi-power m-4"></i> Daemon is quiting</h2>
<div *ngIf="!daemonRunning || stoppingDaemon || restartingDaemon || enablingSync || disablingSync || installing" class="h-100 p-5 text-bg-dark rounded-3 m-4 text-center">
<h2 *ngIf="!installing && !enablingSync && !disablingSync && !daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && daemonConfigured && !quittingDaemon"><i class="bi bi-exclamation-diamond m-4"></i> Daemon not running</h2>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && !daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !daemonConfigured && !quittingDaemon"><i class="bi bi-exclamation-diamond m-4"></i> Daemon not configured or installed</h2>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && restartingDaemon && !upgrading"><i class="bi bi-arrow-clockwise m-4"></i> Daemon restarting</h2>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && stoppingDaemon && !upgrading && !quittingDaemon"><i class="bi bi-stop-fill m-4"></i> Daemon is stopping</h2>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && upgrading"><i class="bi bi-cloud-download m-4"></i> Daemon is upgrading</h2>
<h2 *ngIf="installing && !enablingSync && !disablingSync && !upgrading"><i class="bi bi-arrow-down-circle m-4"></i> Daemon is installing</h2>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && quittingDaemon"><i class="bi bi-power m-4"></i> Daemon is quiting</h2>
<h2 *ngIf="!installing && enablingSync"><i class="bi bi-repeat m-4"></i>Daemon is enabling sync</h2>
<h2 *ngIf="!installing && disablingSync"><i class="bi bi-slash-circle m-4"></i>Daemon is disabling sync</h2>
<p *ngIf="!daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && daemonConfigured && !upgrading && !quittingDaemon">Start monero daemon</p>
<p *ngIf="!startingDaemon && !startingDaemon && !stoppingDaemon && !restartingDaemon && !daemonConfigured && !upgrading && !quittingDaemon">Configure or install monero daemon</p>
<p *ngIf="!installing && !enablingSync && !disablingSync && !daemonRunning && !startingDaemon && !stoppingDaemon && !restartingDaemon && daemonConfigured && !upgrading && !quittingDaemon">Start monero daemon</p>
<p *ngIf="!installing && !enablingSync && !disablingSync && !startingDaemon && !startingDaemon && !stoppingDaemon && !restartingDaemon && !daemonConfigured && !upgrading && !quittingDaemon">Configure or install monero daemon</p>
<h2 *ngIf="startingDaemon"><i class="bi bi-play-fill m-4"></i> Daemon is starting</h2>
<p *ngIf="startingDaemon">Starting monero daemon</p>
<p *ngIf="restartingDaemon">Restarting monero daemon</p>
<p *ngIf="upgrading">Upgrading monero daemon to latest version</p>
<p *ngIf="quittingDaemon">Quiting monero daemon</p>
<h2 *ngIf="!installing && !enablingSync && !disablingSync && startingDaemon"><i class="bi bi-play-fill m-4"></i> Daemon is starting</h2>
<p *ngIf="!installing && !enablingSync && !disablingSync && startingDaemon">Starting monero daemon</p>
<p *ngIf="!installing && !enablingSync && !disablingSync && restartingDaemon">Restarting monero daemon</p>
<p *ngIf="!installing && !enablingSync && !disablingSync && upgrading">Upgrading monero daemon to latest version</p>
<p *ngIf="installing && !enablingSync && !disablingSync && !upgrading">Installing monero daemon</p>
<p *ngIf="!installing && quittingDaemon">Quiting monero daemon</p>
<p *ngIf="!installing && enablingSync">Enabling monero daemon blockchain sync</p>
<p *ngIf="!installing && disablingSync">Disabling monero daemon blokchain sync</p>
<button *ngIf="!startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && daemonConfigured && !quittingDaemon" class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button>
<button *ngIf="startingDaemon" class="btn btn-outline-light" type="button" disabled>
<button *ngIf="!installing && !enablingSync && !disablingSync && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && daemonConfigured && !quittingDaemon" class="btn btn-outline-light" type="button" (click)="startDaemon()"><i class="bi bi-play-fill"></i> Start</button>
<button *ngIf="!installing && !enablingSync && !disablingSync && startingDaemon" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Starting monerod
</button>
<button *ngIf="restartingDaemon" class="btn btn-outline-light" type="button" disabled>
<button *ngIf="!installing && !enablingSync && !disablingSync && restartingDaemon" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Restarting monerod
</button>
<button *ngIf="stoppingDaemon && !quittingDaemon" class="btn btn-outline-light" type="button" disabled>
<button *ngIf="!installing && !enablingSync && !disablingSync && stoppingDaemon && !quittingDaemon" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Stopping monerod
</button>
<button *ngIf="quittingDaemon" class="btn btn-outline-light" type="button" disabled>
<button *ngIf="!installing && quittingDaemon" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Quiting monerod
</button>
<button *ngIf="upgrading" class="btn btn-outline-light" type="button" disabled>
<button *ngIf="!enablingSync && !disablingSync && (upgrading || installing)" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{{ progressStatus }}
</button>
<button *ngIf="!installing && enablingSync" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Enabling sync
</button>
<button *ngIf="!installing && disablingSync" class="btn btn-outline-light" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Disabling sync
</button>
&nbsp;
<button *ngIf="!startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !quittingDaemon" routerLink="/settings" class="btn btn-outline-light" type="button"><i class="bi bi-gear"></i> Configure</button>
<button *ngIf="!installing && !enablingSync && !disablingSync && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !quittingDaemon" routerLink="/settings" class="btn btn-outline-light" type="button"><i class="bi bi-gear"></i> Configure</button>
&nbsp;
<button *ngIf="!startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !daemonConfigured && !quittingDaemon" routerLink="/version" class="btn btn-outline-light" type="button"><i class="bi bi-arrow-down-circle"></i> Install</button>
<button *ngIf="!installing && !enablingSync && !disablingSync && !startingDaemon && !stoppingDaemon && !restartingDaemon && !upgrading && !daemonConfigured && !quittingDaemon" routerLink="/version" class="btn btn-outline-light" type="button"><i class="bi bi-arrow-down-circle"></i> Install</button>
</div>

View file

@ -1,24 +1,37 @@
import { Component, NgZone } from '@angular/core';
import { Component, OnDestroy } from '@angular/core';
import { DaemonService } from '../../../core/services/daemon/daemon.service';
import { DaemonDataService, MoneroInstallerService } from '../../../core/services';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-daemon-not-running',
templateUrl: './daemon-not-running.component.html',
styleUrl: './daemon-not-running.component.scss'
})
export class DaemonNotRunningComponent {
export class DaemonNotRunningComponent implements OnDestroy {
public get upgrading(): boolean {
return this.installer.upgrading && !this.quittingDaemon;
}
public get installing(): boolean {
return this.installer.installing;
}
public get daemonRunning(): boolean {
return this.daemonData.running && !this.startingDaemon && !this.stoppingDaemon && !this.restartingDaemon && !this.upgrading && !this.quittingDaemon;
}
public daemonConfigured: boolean = true;
public get disablingSync(): boolean {
return this.daemonService.disablingSync;
}
public get enablingSync(): boolean {
return this.daemonService.enablingSync;
}
public get startingDaemon(): boolean {
return this.daemonService.starting && !this.restartingDaemon && !this.stoppingDaemon && !this.upgrading && !this.quittingDaemon;
}
@ -45,7 +58,13 @@ export class DaemonNotRunningComponent {
return this.daemonService.quitting;
}
constructor(private installer: MoneroInstallerService, private daemonData: DaemonDataService, private daemonService: DaemonService, private ngZone: NgZone) {
private subscriptions: Subscription[] = [];
constructor(private installer: MoneroInstallerService, private daemonData: DaemonDataService, private daemonService: DaemonService) {
const onSavedSettingsSub: Subscription = this.daemonService.onSavedSettings.subscribe((settings) => {
this.daemonConfigured = settings.monerodPath != '';
});
this.daemonService.getSettings().then((settings) => {
this.daemonConfigured = settings.monerodPath != '';
}).catch((error: any) => {
@ -54,6 +73,13 @@ export class DaemonNotRunningComponent {
});
this.daemonService.isRunning().then().catch((error: any) => console.error(error));
this.subscriptions.push(onSavedSettingsSub);
}
public ngOnDestroy(): void {
this.subscriptions.forEach((sub: Subscription) => sub.unsubscribe());
this.subscriptions = [];
}
public async startDaemon(): Promise<void> {

View file

@ -24,21 +24,21 @@
</span>
<ul class="navbar-nav flex-row">
<li *ngIf="!quitting && !running && !stopping && !starting && !restarting" class="nav-item text-nowrap">
<li *ngIf="!quitting && !running && !stopping && !starting && !restarting && !installing && !upgrading && daemonConfigured" class="nav-item text-nowrap">
<button class="btn btn-outline-secondary px-3 text-white" type="button" data-bs-toggle="collapse" aria-expanded="false" aria-label="Start daemon" (click)="startDaemon()">
<i class="bi bi-play-fill"></i>
</button>
&nbsp;
</li>
<li *ngIf="!quitting && running && !stopping && !starting" class="nav-item text-nowrap">
<li *ngIf="!quitting && running && !stopping && !starting && !restarting && !installing && !upgrading" class="nav-item text-nowrap">
<button class="btn btn-outline-secondary px-3 text-white" type="button" data-bs-toggle="collapse" aria-expanded="false" aria-label="Stop daemon" (click)="stopDaemon()">
<i class="bi bi-stop-fill"></i>
</button>
&nbsp;
</li>
<li *ngIf="!quitting && running && !stopping && !starting" class="nav-item text-nowrap">
<li *ngIf="!quitting && running && !stopping && !starting && !installing && !upgrading" class="nav-item text-nowrap">
<button class="btn btn-outline-secondary px-3 text-white" type="button" data-bs-toggle="collapse" aria-expanded="false" aria-label="Restart daemon" (click)="restartDaemon()">
<i class="bi bi-arrow-clockwise"></i>
</button>
@ -46,7 +46,7 @@
</li>
<li class="nav-item text-nowrap">
<button class="btn btn-outline-secondary px-3 text-white" type="button" data-bs-toggle="offcanvas" aria-label="Quit" [disabled]="stopping || starting || quitting || restarting" (click)="quit()">
<button class="btn btn-outline-secondary px-3 text-white" type="button" data-bs-toggle="offcanvas" aria-label="Quit" [disabled]="stopping || starting || quitting || restarting || upgrading || installing" (click)="quit()">
<i class="bi bi-power"></i>
</button>
&nbsp;
@ -55,11 +55,13 @@
</ul>
<ul class="navbar-nav flex-row d-md-none">
<!--
<li class="nav-item text-nowrap">
<button class="nav-link px-3 text-white" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSearch" aria-controls="navbarSearch" aria-expanded="false" aria-label="Toggle search">
<i class="bi bi-search"></i>
</button>
</li>
-->
<li class="nav-item text-nowrap">
<button class="nav-link px-3 text-white" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-list"></i>

View file

@ -1,17 +1,28 @@
import { Component, NgZone } from '@angular/core';
import { Component, NgZone, OnDestroy } from '@angular/core';
import { NavbarService } from './navbar.service';
import { NavbarLink } from './navbar.model';
import { DaemonService } from '../../../core/services/daemon/daemon.service';
import { MoneroInstallerService } from '../../../core/services';
import { DaemonSettings } from '../../../../common';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrl: './navbar.component.scss'
})
export class NavbarComponent {
export class NavbarComponent implements OnDestroy {
private _running: boolean = false;
public get installing(): boolean {
return this.installerService.installing;
}
public get upgrading(): boolean {
return this.installerService.upgrading;
}
public get quitting(): boolean {
return this.daemonService.quitting;
}
@ -36,7 +47,23 @@ export class NavbarComponent {
return this.daemonService.restarting;
}
constructor(private navbarService: NavbarService, private daemonService: DaemonService, private ngZone: NgZone) {
public get daemonConfigured(): boolean {
return this.daemonSettings.monerodPath != '';
}
private daemonSettings: DaemonSettings = new DaemonSettings();
private subscriptions: Subscription[] = [];
constructor(private navbarService: NavbarService, private daemonService: DaemonService, private installerService: MoneroInstallerService, private ngZone: NgZone) {
const onSavedSettingsSub: Subscription = this.daemonService.onSavedSettings.subscribe((settings: DaemonSettings) => {
this.daemonSettings = settings;
});
this.daemonService.getSettings().then((settings: DaemonSettings) => {
this.daemonSettings = settings;
}).catch((error: any) => {
console.error(error);
});
this.daemonService.isRunning().then((running: boolean) => {
this.ngZone.run(() => {
@ -47,12 +74,13 @@ export class NavbarComponent {
this._running = false;
});
this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
const onStatusChangedSub: Subscription = this.daemonService.onDaemonStatusChanged.subscribe((running: boolean) => {
this.ngZone.run(() => {
this._running = running;
});
});
this.subscriptions.push(onSavedSettingsSub, onStatusChangedSub);
}
public async startDaemon(): Promise<void> {
@ -78,4 +106,9 @@ export class NavbarComponent {
public async quit(): Promise<void> {
await this.daemonService.quit();
}
public ngOnDestroy(): void {
this.subscriptions.forEach((sub: Subscription) => sub.unsubscribe());
this.subscriptions = [];
}
}

View file

@ -42,3 +42,4 @@ export { ProcessStats } from './ProcessStats';
export * from './error';
export * from './request';
export * from './utils';

View file

@ -0,0 +1,33 @@
export abstract class TimeUtils {
public static isInTimeRange(fromHours: string, toHours: string): boolean {
const now = new Date();
// Estraiamo l'ora e i minuti dalla stringa in formato hh:mm
const [fromHour, fromMinute] = fromHours.split(":").map(Number);
const [toHour, toMinute] = toHours.split(":").map(Number);
// Otteniamo l'ora corrente in ore e minuti
const currentHour = now.getHours();
const currentMinute = now.getMinutes();
// Creiamo oggetti Date per le ore 'from', 'to', e l'ora attuale
const currentTime = new Date();
currentTime.setHours(currentHour, currentMinute, 0, 0);
const fromTime = new Date();
fromTime.setHours(fromHour, fromMinute, 0, 0);
const toTime = new Date();
toTime.setHours(toHour, toMinute, 0, 0);
// Gestione del caso in cui la fascia oraria attraversi la mezzanotte
if (fromTime > toTime) {
// Se l'ora attuale è dopo 'fromTime' o prima di 'toTime'
return currentTime >= fromTime || currentTime <= toTime;
} else {
// Caso normale: la fascia oraria è nello stesso giorno
return currentTime >= fromTime && currentTime <= toTime;
}
}
}

View file

@ -0,0 +1 @@
export { TimeUtils } from './TimeUtils';

View file

@ -54,6 +54,7 @@ import 'zone.js'; // Included with Angular CLI.
import 'jquery';
import 'bootstrap-table';
import { NotificationConstructorOptions } from 'electron';
declare global {
interface Window {
@ -72,6 +73,13 @@ declare global {
) => void) => void;
onMonitorMonerodError: (callback: (event: any, error: string) => void) => void;
onMonerodStarted: (callback: (event: any, started: boolean) => void) => void;
getMoneroVersion: (path: string) => void;
onMoneroVersion: (callback: (event: any, version: string) => void) => void;
onMoneroVersionError: (callback: (event: any, error: string) => void) => void;
downloadMonerod: (downloadUrl:string, destination: string) => void;
onDownloadProgress: (callback: (event: any, progress: { progress: number, status: string }) => void) => void;
checkValidMonerodPath: (path: string) => void;
onCheckValidMonerodPath: (callback: (event: any, valid: boolean) => void) => void;
unsubscribeOnMonerodStarted: () => void;
onMoneroClose: (callback: (event: any, code: number) => void) => void;
onMoneroStdout: (callbak: (event: any, out: string) => void) => void;
@ -83,6 +91,7 @@ declare global {
onSelectedFile: (callback: (event: any, path: string) => void) => void;
getOsType: () => void;
gotOsType: (callback: (event: any, osType: { platform: string, arch: string }) => void) => void;
showNotification: (options: NotificationConstructorOptions) => void;
quit: () => void;
};