From e6ac8e69b5b779444817d32f6782aa93740193b1 Mon Sep 17 00:00:00 2001 From: Alec Jacobs Date: Mon, 13 Mar 2023 09:27:50 -0700 Subject: [PATCH] fix(shortcuts): client handles state updates from storageApi Signed-off-by: Alec Jacobs --- .../src/api/LocalStoredShortcuts.test.ts | 3 +- .../shortcuts/src/api/LocalStoredShortcuts.ts | 54 +++++++++++++------ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts index dbea91863c..86890fc75b 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts @@ -36,8 +36,9 @@ describe('LocalStoredShortcuts', () => { resolve(); }, }); - shortcutApi.add(shortcut); }); + observerNextHandler.mockClear(); // handler is called with current state to start + await shortcutApi.add(shortcut); expect(observerNextHandler).toHaveBeenCalledTimes(1); expect(observerNextHandler).toHaveBeenCalledWith( diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts index 65aeb86e3f..44f7364f69 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -16,10 +16,10 @@ import { pageTheme } from '@backstage/theme'; import { v4 as uuid } from 'uuid'; -import { ShortcutApi } from './ShortcutApi'; -import { Shortcut } from '../types'; -import { StorageApi } from '@backstage/core-plugin-api'; -import Observable from 'zen-observable'; +import { ShortcutApi, type Shortcut } from '@backstage/plugin-shortcuts'; +import type { StorageApi } from '@backstage/core-plugin-api'; +import type { Observable } from '@backstage/types'; +import ObservableImpl from 'zen-observable'; /** * Implementation of the ShortcutApi that uses the StorageApi to store shortcuts. @@ -27,27 +27,41 @@ import Observable from 'zen-observable'; * @public */ export class LocalStoredShortcuts implements ShortcutApi { - private readonly shortcuts: Observable; + private shortcuts: Shortcut[]; + private readonly subscribers = new Set< + ZenObservable.SubscriptionObserver + >(); + + private readonly observable = new ObservableImpl(subscriber => { + // forward the the latest value + subscriber.next(this.shortcuts); + + this.subscribers.add(subscriber); + return () => { + this.subscribers.delete(subscriber); + }; + }); constructor(private readonly storageApi: StorageApi) { - this.shortcuts = Observable.from( - this.storageApi.observe$('items'), - ).map(snapshot => snapshot.value ?? []); + this.shortcuts = this.storageApi.snapshot('items').value ?? []; + this.storageApi.observe$('items').subscribe({ + next: next => { + this.shortcuts = next.value ?? []; + this.notifyChanges(); + }, + }); } - shortcut$() { - return this.shortcuts; + shortcut$(): Observable { + return this.observable; } get() { - return Array.from( - this.storageApi.snapshot('items').value ?? [], - ).sort((a, b) => (a.title >= b.title ? 1 : -1)); + return this.shortcuts; } async add(shortcut: Omit) { - const shortcuts = this.get(); - shortcuts.push({ ...shortcut, id: uuid() }); + const shortcuts = this.sort([...this.get(), { ...shortcut, id: uuid() }]); await this.storageApi.set('items', shortcuts); } @@ -78,4 +92,14 @@ export class LocalStoredShortcuts implements ShortcutApi { catalog: 'home', docs: 'documentation', }; + + private sort(shortcuts: Shortcut[]): Shortcut[] { + return shortcuts.slice().sort((a, b) => (a.title >= b.title ? 1 : -1)); + } + + private notifyChanges() { + for (const subscription of this.subscribers) { + subscription.next(this.shortcuts); + } + } }