Show WebSocket status on UI
This commit is contained in:
parent
a249919061
commit
1a05e184a7
6 changed files with 71 additions and 28 deletions
|
|
@ -222,7 +222,7 @@ export class SyncSettingsTab extends PluginSettingTab {
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button.setButtonText("Test connection").onClick(async () => {
|
button.setButtonText("Test connection").onClick(async () => {
|
||||||
new Notice(
|
new Notice(
|
||||||
(await this.syncClient.checkConnection()).message
|
(await this.syncClient.checkConnection()).serverMessage
|
||||||
);
|
);
|
||||||
await this.statusDescription.updateConnectionState();
|
await this.statusDescription.updateConnectionState();
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ import "./status-description.scss";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
HistoryStats,
|
HistoryStats,
|
||||||
CheckConnectionResult,
|
NetworkConnectionStatus,
|
||||||
SyncClient
|
SyncClient
|
||||||
} from "sync-client";
|
} from "sync-client";
|
||||||
|
|
||||||
export class StatusDescription {
|
export class StatusDescription {
|
||||||
private lastHistoryStats: HistoryStats | undefined;
|
private lastHistoryStats: HistoryStats | undefined;
|
||||||
private lastRemaining: number | undefined;
|
private lastRemaining: number | undefined;
|
||||||
private lastConnectionState: CheckConnectionResult | undefined;
|
private lastConnectionState: NetworkConnectionStatus | undefined;
|
||||||
|
|
||||||
private statusChangeListeners: (() => void)[] = [];
|
private statusChangeListeners: (() => void)[] = [];
|
||||||
|
|
||||||
|
|
@ -28,9 +28,13 @@ export class StatusDescription {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.syncClient.addOnSettingsChangeListener(() => {
|
this.syncClient.addWebSocketStatusChangeListener(
|
||||||
void this.updateConnectionState();
|
() => void this.updateConnectionState()
|
||||||
});
|
);
|
||||||
|
|
||||||
|
this.syncClient.addOnSettingsChangeListener(
|
||||||
|
() => void this.updateConnectionState()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateConnectionState(): Promise<void> {
|
public async updateConnectionState(): Promise<void> {
|
||||||
|
|
@ -61,7 +65,15 @@ export class StatusDescription {
|
||||||
|
|
||||||
if (!this.lastConnectionState.isSuccessful) {
|
if (!this.lastConnectionState.isSuccessful) {
|
||||||
container.createSpan({
|
container.createSpan({
|
||||||
text: `VaultLink failed to connect to the remote server with the error "${this.lastConnectionState.message}"`,
|
text: `VaultLink failed to connect to the remote server with error '${this.lastConnectionState.serverMessage}'`,
|
||||||
|
cls: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.lastConnectionState.isWebSocketConnected) {
|
||||||
|
container.createSpan({
|
||||||
|
text: `${this.lastConnectionState.serverMessage} but the WebSocket connection could not be established.`,
|
||||||
cls: "error"
|
cls: "error"
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ export {
|
||||||
type HistoryEntry
|
type HistoryEntry
|
||||||
} from "./tracing/sync-history";
|
} from "./tracing/sync-history";
|
||||||
export { Logger, LogLevel, LogLine } from "./tracing/logger";
|
export { Logger, LogLevel, LogLine } from "./tracing/logger";
|
||||||
export type { CheckConnectionResult } from "./services/sync-service";
|
|
||||||
export { type SyncSettings } from "./persistence/settings";
|
export { type SyncSettings } from "./persistence/settings";
|
||||||
export type { RelativePath, StoredDatabase } from "./persistence/database";
|
export type { RelativePath, StoredDatabase } from "./persistence/database";
|
||||||
export type { FileSystemOperations } from "./file-operations/filesystem-operations";
|
export type { FileSystemOperations } from "./file-operations/filesystem-operations";
|
||||||
export type { PersistenceProvider } from "./persistence/persistence";
|
export type { PersistenceProvider } from "./persistence/persistence";
|
||||||
|
|
||||||
|
export type { NetworkConnectionStatus } from "./sync-client";
|
||||||
export { SyncClient } from "./sync-client";
|
export { SyncClient } from "./sync-client";
|
||||||
|
|
|
||||||
|
|
@ -307,13 +307,13 @@ export class SyncService {
|
||||||
if (result.isAuthenticated) {
|
if (result.isAuthenticated) {
|
||||||
return {
|
return {
|
||||||
isSuccessful: true,
|
isSuccessful: true,
|
||||||
message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated.`
|
message: `Successfully connected to server (version: ${result.serverVersion}) and authenticated`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSuccessful: false,
|
isSuccessful: false,
|
||||||
message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate.`
|
message: `Successfully connected to server (version: ${result.serverVersion}) but failed to authenticate`
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import type { RelativePath, StoredDatabase } from "./persistence/database";
|
||||||
import { Database } from "./persistence/database";
|
import { Database } from "./persistence/database";
|
||||||
import type { SyncSettings } from "./persistence/settings";
|
import type { SyncSettings } from "./persistence/settings";
|
||||||
import { Settings } from "./persistence/settings";
|
import { Settings } from "./persistence/settings";
|
||||||
import type { CheckConnectionResult } from "./services/sync-service";
|
|
||||||
import { SyncService } from "./services/sync-service";
|
import { SyncService } from "./services/sync-service";
|
||||||
import { Syncer } from "./sync-operations/syncer";
|
import { Syncer } from "./sync-operations/syncer";
|
||||||
import type { FileSystemOperations } from "./file-operations/filesystem-operations";
|
import type { FileSystemOperations } from "./file-operations/filesystem-operations";
|
||||||
|
|
@ -16,6 +15,12 @@ import { FileOperations } from "./file-operations/file-operations";
|
||||||
import { ConnectionStatus } from "./services/connection-status";
|
import { ConnectionStatus } from "./services/connection-status";
|
||||||
import { UnrestrictedSyncer } from "./sync-operations/unrestricted-syncer";
|
import { UnrestrictedSyncer } from "./sync-operations/unrestricted-syncer";
|
||||||
|
|
||||||
|
export interface NetworkConnectionStatus {
|
||||||
|
isSuccessful: boolean;
|
||||||
|
serverMessage: string;
|
||||||
|
isWebSocketConnected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class SyncClient {
|
export class SyncClient {
|
||||||
// eslint-disable-next-line @typescript-eslint/max-params
|
// eslint-disable-next-line @typescript-eslint/max-params
|
||||||
private constructor(
|
private constructor(
|
||||||
|
|
@ -134,8 +139,13 @@ export class SyncClient {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkConnection(): Promise<CheckConnectionResult> {
|
public async checkConnection(): Promise<NetworkConnectionStatus> {
|
||||||
return this.syncService.checkConnection();
|
const server = await this.syncService.checkConnection();
|
||||||
|
return {
|
||||||
|
isSuccessful: server.isSuccessful,
|
||||||
|
serverMessage: server.message,
|
||||||
|
isWebSocketConnected: this.syncer.isWebSocketConnected
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHistoryEntries(): readonly HistoryEntry[] {
|
public getHistoryEntries(): readonly HistoryEntry[] {
|
||||||
|
|
@ -202,6 +212,10 @@ export class SyncClient {
|
||||||
this.syncer.addRemainingOperationsListener(listener);
|
this.syncer.addRemainingOperationsListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addWebSocketStatusChangeListener(listener: () => void): void {
|
||||||
|
this.syncer.addWebSocketStatusChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public async syncLocallyCreatedFile(
|
public async syncLocallyCreatedFile(
|
||||||
relativePath: RelativePath
|
relativePath: RelativePath
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export class Syncer {
|
||||||
private readonly remainingOperationsListeners: ((
|
private readonly remainingOperationsListeners: ((
|
||||||
remainingOperations: number
|
remainingOperations: number
|
||||||
) => void)[] = [];
|
) => void)[] = [];
|
||||||
|
private readonly webSocketStatusChangeListeners: (() => void)[] = [];
|
||||||
private readonly syncQueue: PQueue;
|
private readonly syncQueue: PQueue;
|
||||||
|
|
||||||
private runningScheduleSyncForOfflineChanges: Promise<void> | undefined;
|
private runningScheduleSyncForOfflineChanges: Promise<void> | undefined;
|
||||||
|
|
@ -70,15 +71,8 @@ export class Syncer {
|
||||||
this.setWebSocketRefreshInterval();
|
this.setWebSocketRefreshInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async reset(): Promise<void> {
|
public get isWebSocketConnected(): boolean {
|
||||||
await this.waitUntilFinished();
|
return this.applyRemoteChangesWebSocket?.readyState === WebSocket.OPEN;
|
||||||
this.setWebSocketRefreshInterval();
|
|
||||||
this.updateWebSocket(this.settings.getSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
public stop(): void {
|
|
||||||
clearInterval(this.refreshApplyRemoteChangesWebSocketInterval);
|
|
||||||
this.applyRemoteChangesWebSocket?.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public addRemainingOperationsListener(
|
public addRemainingOperationsListener(
|
||||||
|
|
@ -87,6 +81,10 @@ export class Syncer {
|
||||||
this.remainingOperationsListeners.push(listener);
|
this.remainingOperationsListeners.push(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addWebSocketStatusChangeListener(listener: () => void): void {
|
||||||
|
this.webSocketStatusChangeListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public async syncLocallyCreatedFile(
|
public async syncLocallyCreatedFile(
|
||||||
relativePath: RelativePath
|
relativePath: RelativePath
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
@ -245,6 +243,17 @@ export class Syncer {
|
||||||
return this.syncQueue.onEmpty();
|
return this.syncQueue.onEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async reset(): Promise<void> {
|
||||||
|
await this.waitUntilFinished();
|
||||||
|
this.setWebSocketRefreshInterval();
|
||||||
|
this.updateWebSocket(this.settings.getSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public stop(): void {
|
||||||
|
clearInterval(this.refreshApplyRemoteChangesWebSocketInterval);
|
||||||
|
this.applyRemoteChangesWebSocket?.close();
|
||||||
|
}
|
||||||
|
|
||||||
private updateWebSocket(settings: SyncSettings): void {
|
private updateWebSocket(settings: SyncSettings): void {
|
||||||
this.applyRemoteChangesWebSocket?.close();
|
this.applyRemoteChangesWebSocket?.close();
|
||||||
|
|
||||||
|
|
@ -277,14 +286,22 @@ export class Syncer {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.applyRemoteChangesWebSocket.onerror = (event): void => {
|
// The JS WebSocket API doesn't support setting headers, so we have to send the token as a message
|
||||||
console.error(event);
|
this.applyRemoteChangesWebSocket.onopen = (): void => {
|
||||||
this.logger.error(`WebSocket error`);
|
this.applyRemoteChangesWebSocket?.send(settings.token);
|
||||||
|
this.webSocketStatusChangeListeners.forEach((listener) => {
|
||||||
|
listener();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// The JS WebSocket API doesn't support setting headers, so we have to send the token as a message
|
this.applyRemoteChangesWebSocket.onclose = (event): void => {
|
||||||
this.applyRemoteChangesWebSocket.onopen = (): void =>
|
this.logger.error(
|
||||||
this.applyRemoteChangesWebSocket?.send(settings.token);
|
`WebSocket closed with code ${event.code}: ${event.reason}`
|
||||||
|
);
|
||||||
|
this.webSocketStatusChangeListeners.forEach((listener) => {
|
||||||
|
listener();
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private setWebSocketRefreshInterval(): void {
|
private setWebSocketRefreshInterval(): void {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue