mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-01 20:03:30 +02:00
feat: improve status handling and user feedback in SurfSense plugin
- Added refreshStatus method to update connection status immediately after settings changes. - Enhanced error reporting with reportAuthError method for better authentication feedback. - Updated status visuals in the status modal to reflect the new centralized structure. - Improved connection handling in the settings tab to ensure accurate status representation.
This commit is contained in:
parent
4f1c870987
commit
937965b335
5 changed files with 30 additions and 18 deletions
|
|
@ -183,6 +183,7 @@ export default class SurfSensePlugin extends Plugin {
|
||||||
this.settings.vaultId !== previousVaultId ||
|
this.settings.vaultId !== previousVaultId ||
|
||||||
this.settings.connectorId !== previousConnectorId;
|
this.settings.connectorId !== previousConnectorId;
|
||||||
if (!changed) return;
|
if (!changed) return;
|
||||||
|
this.engine?.refreshStatus();
|
||||||
this.notifyStatusChange();
|
this.notifyStatusChange();
|
||||||
if (this.settings.searchSpaceId !== null) {
|
if (this.settings.searchSpaceId !== null) {
|
||||||
void this.engine.ensureConnected();
|
void this.engine.ensureConnected();
|
||||||
|
|
@ -242,6 +243,7 @@ export default class SurfSensePlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyAuthError(): void {
|
private notifyAuthError(): void {
|
||||||
|
this.engine?.reportAuthError();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - this.lastAuthToastAt < 10_000) return;
|
if (now - this.lastAuthToastAt < 10_000) return;
|
||||||
this.lastAuthToastAt = now;
|
this.lastAuthToastAt = now;
|
||||||
|
|
@ -265,8 +267,6 @@ export default class SurfSensePlugin extends Plugin {
|
||||||
|
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
await this.saveData(this.settings);
|
||||||
// Ensures the indicator reacts to settings edits (token paste, search-space pick)
|
|
||||||
// without waiting for the next sync trigger.
|
|
||||||
this.engine?.refreshStatus();
|
this.engine?.refreshStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ export class SurfSenseSettingTab extends PluginSettingTab {
|
||||||
try {
|
try {
|
||||||
await this.plugin.api.verifyToken();
|
await this.plugin.api.verifyToken();
|
||||||
new Notice("Surfsense: token verified.");
|
new Notice("Surfsense: token verified.");
|
||||||
|
this.plugin.engine.refreshStatus({ force: true });
|
||||||
await this.refreshSearchSpaces();
|
await this.refreshSearchSpaces();
|
||||||
this.display();
|
this.display();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { type App, Modal, Notice, Setting } from "obsidian";
|
import { type App, Modal, Notice, Setting } from "obsidian";
|
||||||
import type SurfSensePlugin from "./main";
|
import type SurfSensePlugin from "./main";
|
||||||
|
import { STATUS_VISUALS } from "./status-visuals";
|
||||||
|
|
||||||
/** Live status panel reachable from the status bar / command palette. */
|
/** Live status panel reachable from the status bar / command palette. */
|
||||||
export class StatusModal extends Modal {
|
export class StatusModal extends Modal {
|
||||||
|
|
@ -28,7 +29,7 @@ export class StatusModal extends Modal {
|
||||||
const s = plugin.settings;
|
const s = plugin.settings;
|
||||||
|
|
||||||
const rows: Array<[string, string]> = [
|
const rows: Array<[string, string]> = [
|
||||||
["Status", plugin.lastStatus.kind],
|
["Status", STATUS_VISUALS[plugin.lastStatus.kind].label],
|
||||||
[
|
[
|
||||||
"Last sync",
|
"Last sync",
|
||||||
s.lastSyncAt ? new Date(s.lastSyncAt).toLocaleString() : "—",
|
s.lastSyncAt ? new Date(s.lastSyncAt).toLocaleString() : "—",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import type { StatusKind } from "./types";
|
import type { StatusKind } from "./types";
|
||||||
|
|
||||||
/**
|
/** Shared by the status bar and the settings "Connection" heading. */
|
||||||
* Single source of truth for status icons + labels. Both the status bar
|
|
||||||
* and the settings "Connection" heading render from this table so a change
|
|
||||||
* here updates both surfaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface StatusVisual {
|
export interface StatusVisual {
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ export class SyncEngine {
|
||||||
private idleReconcileStreak = 0;
|
private idleReconcileStreak = 0;
|
||||||
/** 2^streak is capped at this value (e.g. 8 → max ×8 backoff). */
|
/** 2^streak is capped at this value (e.g. 8 → max ×8 backoff). */
|
||||||
private readonly maxBackoffMultiplier = 8;
|
private readonly maxBackoffMultiplier = 8;
|
||||||
|
private lastAppliedKind: StatusKind = "needs-setup";
|
||||||
|
|
||||||
constructor(deps: SyncEngineDeps) {
|
constructor(deps: SyncEngineDeps) {
|
||||||
this.deps = deps;
|
this.deps = deps;
|
||||||
|
|
@ -502,24 +503,38 @@ export class SyncEngine {
|
||||||
// ---- status helpers ---------------------------------------------------
|
// ---- status helpers ---------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recomputes status from settings + queue depth. Call from main.ts after
|
* Conservative by default: real errors are preserved while setup is
|
||||||
* settings change so the indicator reacts to token paste / search-space
|
* complete, so unrelated edits don't optimistically clear the indicator.
|
||||||
* pick without waiting for the next sync trigger.
|
* Pass `force: true` after an explicit verify/reconcile confirmation.
|
||||||
*/
|
*/
|
||||||
refreshStatus(): void {
|
refreshStatus(opts: { force?: boolean } = {}): void {
|
||||||
|
if (!opts.force) {
|
||||||
|
const last = this.lastAppliedKind;
|
||||||
|
const isError =
|
||||||
|
last === "auth-error" || last === "offline" || last === "error";
|
||||||
|
const s = this.deps.getSettings();
|
||||||
|
const setupComplete = !!(s.apiToken && s.searchSpaceId && s.connectorId);
|
||||||
|
if (isError && setupComplete) return;
|
||||||
|
}
|
||||||
this.setStatus(this.queueStatusKind(), this.statusDetail());
|
this.setStatus(this.queueStatusKind(), this.statusDetail());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportAuthError(message?: string): void {
|
||||||
|
this.setStatus("auth-error", message ?? "API token expired or invalid");
|
||||||
|
}
|
||||||
|
|
||||||
private setStatus(kind: StatusKind, detail?: string): void {
|
private setStatus(kind: StatusKind, detail?: string): void {
|
||||||
// Errors carry meaningful signal; only "happy" kinds get downgraded
|
const s = this.deps.getSettings();
|
||||||
// to needs-setup when prerequisites are missing.
|
if (!s.apiToken) {
|
||||||
if (kind !== "auth-error" && kind !== "offline" && kind !== "error") {
|
kind = "needs-setup";
|
||||||
const s = this.deps.getSettings();
|
detail = this.setupHint(s);
|
||||||
if (!s.apiToken || !s.searchSpaceId || !s.connectorId) {
|
} else if (kind !== "auth-error" && kind !== "offline" && kind !== "error") {
|
||||||
|
if (!s.searchSpaceId || !s.connectorId) {
|
||||||
kind = "needs-setup";
|
kind = "needs-setup";
|
||||||
detail = this.setupHint(s);
|
detail = this.setupHint(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.lastAppliedKind = kind;
|
||||||
this.deps.setStatus({ kind, detail, queueDepth: this.deps.queue.size });
|
this.deps.setStatus({ kind, detail, queueDepth: this.deps.queue.size });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue