Skip to content

Commit 937965b

Browse files
committed
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.
1 parent 4f1c870 commit 937965b

5 files changed

Lines changed: 30 additions & 18 deletions

File tree

surfsense_obsidian/src/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export default class SurfSensePlugin extends Plugin {
183183
this.settings.vaultId !== previousVaultId ||
184184
this.settings.connectorId !== previousConnectorId;
185185
if (!changed) return;
186+
this.engine?.refreshStatus();
186187
this.notifyStatusChange();
187188
if (this.settings.searchSpaceId !== null) {
188189
void this.engine.ensureConnected();
@@ -242,6 +243,7 @@ export default class SurfSensePlugin extends Plugin {
242243
}
243244

244245
private notifyAuthError(): void {
246+
this.engine?.reportAuthError();
245247
const now = Date.now();
246248
if (now - this.lastAuthToastAt < 10_000) return;
247249
this.lastAuthToastAt = now;
@@ -265,8 +267,6 @@ export default class SurfSensePlugin extends Plugin {
265267

266268
async saveSettings() {
267269
await this.saveData(this.settings);
268-
// Ensures the indicator reacts to settings edits (token paste, search-space pick)
269-
// without waiting for the next sync trigger.
270270
this.engine?.refreshStatus();
271271
}
272272

surfsense_obsidian/src/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export class SurfSenseSettingTab extends PluginSettingTab {
9191
try {
9292
await this.plugin.api.verifyToken();
9393
new Notice("Surfsense: token verified.");
94+
this.plugin.engine.refreshStatus({ force: true });
9495
await this.refreshSearchSpaces();
9596
this.display();
9697
} catch (err) {

surfsense_obsidian/src/status-modal.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type App, Modal, Notice, Setting } from "obsidian";
22
import type SurfSensePlugin from "./main";
3+
import { STATUS_VISUALS } from "./status-visuals";
34

45
/** Live status panel reachable from the status bar / command palette. */
56
export class StatusModal extends Modal {
@@ -28,7 +29,7 @@ export class StatusModal extends Modal {
2829
const s = plugin.settings;
2930

3031
const rows: Array<[string, string]> = [
31-
["Status", plugin.lastStatus.kind],
32+
["Status", STATUS_VISUALS[plugin.lastStatus.kind].label],
3233
[
3334
"Last sync",
3435
s.lastSyncAt ? new Date(s.lastSyncAt).toLocaleString() : "—",

surfsense_obsidian/src/status-visuals.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import type { StatusKind } from "./types";
22

3-
/**
4-
* Single source of truth for status icons + labels. Both the status bar
5-
* and the settings "Connection" heading render from this table so a change
6-
* here updates both surfaces.
7-
*/
8-
3+
/** Shared by the status bar and the settings "Connection" heading. */
94
export interface StatusVisual {
105
icon: string;
116
label: string;

surfsense_obsidian/src/sync-engine.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export class SyncEngine {
7171
private idleReconcileStreak = 0;
7272
/** 2^streak is capped at this value (e.g. 8 → max ×8 backoff). */
7373
private readonly maxBackoffMultiplier = 8;
74+
private lastAppliedKind: StatusKind = "needs-setup";
7475

7576
constructor(deps: SyncEngineDeps) {
7677
this.deps = deps;
@@ -502,24 +503,38 @@ export class SyncEngine {
502503
// ---- status helpers ---------------------------------------------------
503504

504505
/**
505-
* Recomputes status from settings + queue depth. Call from main.ts after
506-
* settings change so the indicator reacts to token paste / search-space
507-
* pick without waiting for the next sync trigger.
506+
* Conservative by default: real errors are preserved while setup is
507+
* complete, so unrelated edits don't optimistically clear the indicator.
508+
* Pass `force: true` after an explicit verify/reconcile confirmation.
508509
*/
509-
refreshStatus(): void {
510+
refreshStatus(opts: { force?: boolean } = {}): void {
511+
if (!opts.force) {
512+
const last = this.lastAppliedKind;
513+
const isError =
514+
last === "auth-error" || last === "offline" || last === "error";
515+
const s = this.deps.getSettings();
516+
const setupComplete = !!(s.apiToken && s.searchSpaceId && s.connectorId);
517+
if (isError && setupComplete) return;
518+
}
510519
this.setStatus(this.queueStatusKind(), this.statusDetail());
511520
}
512521

522+
reportAuthError(message?: string): void {
523+
this.setStatus("auth-error", message ?? "API token expired or invalid");
524+
}
525+
513526
private setStatus(kind: StatusKind, detail?: string): void {
514-
// Errors carry meaningful signal; only "happy" kinds get downgraded
515-
// to needs-setup when prerequisites are missing.
516-
if (kind !== "auth-error" && kind !== "offline" && kind !== "error") {
517-
const s = this.deps.getSettings();
518-
if (!s.apiToken || !s.searchSpaceId || !s.connectorId) {
527+
const s = this.deps.getSettings();
528+
if (!s.apiToken) {
529+
kind = "needs-setup";
530+
detail = this.setupHint(s);
531+
} else if (kind !== "auth-error" && kind !== "offline" && kind !== "error") {
532+
if (!s.searchSpaceId || !s.connectorId) {
519533
kind = "needs-setup";
520534
detail = this.setupHint(s);
521535
}
522536
}
537+
this.lastAppliedKind = kind;
523538
this.deps.setStatus({ kind, detail, queueDepth: this.deps.queue.size });
524539
}
525540

0 commit comments

Comments
 (0)