From 2e17ad5e30d26b00f7dc1844784224e0b1b0b4f3 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Wed, 13 Jul 2022 16:17:54 +0200 Subject: [PATCH] replaced internal observable with a mapping from the storage API Signed-off-by: Alex Rybchenko --- plugins/shortcuts/src/Shortcuts.tsx | 3 +- .../src/api/LocalStoredShortcuts.test.ts | 19 ++++--- .../shortcuts/src/api/LocalStoredShortcuts.ts | 49 ++++++------------- plugins/shortcuts/src/api/ShortcutApi.ts | 5 ++ 4 files changed, 33 insertions(+), 43 deletions(-) diff --git a/plugins/shortcuts/src/Shortcuts.tsx b/plugins/shortcuts/src/Shortcuts.tsx index b8af1f2e29..3c48e2bae2 100644 --- a/plugins/shortcuts/src/Shortcuts.tsx +++ b/plugins/shortcuts/src/Shortcuts.tsx @@ -40,7 +40,8 @@ export interface ShortcutsProps { export const Shortcuts = (props: ShortcutsProps) => { const shortcutApi = useApi(shortcutsApiRef); const shortcuts = useObservable( - useMemo(() => shortcutApi.shortcut$(), [shortcutApi]), + shortcutApi.shortcut$(), + shortcutApi.snapshot(), ); const [anchorEl, setAnchorEl] = React.useState(); const loading = Boolean(!shortcuts); diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts index c8401e8b1a..dbea91863c 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.test.ts @@ -27,17 +27,22 @@ describe('LocalStoredShortcuts', () => { ); const shortcut: Shortcut = { id: 'id', title: 'title', url: '/url' }; - await shortcutApi.add(shortcut); + const observerNextHandler = jest.fn(); await new Promise(resolve => { - const subscription = shortcutApi.shortcut$().subscribe(data => { - expect(data).toEqual( - expect.arrayContaining([{ ...shortcut, id: expect.anything() }]), - ); - subscription.unsubscribe(); - resolve(); + shortcutApi.shortcut$().subscribe({ + next: data => { + observerNextHandler(data); + resolve(); + }, }); + shortcutApi.add(shortcut); }); + + expect(observerNextHandler).toHaveBeenCalledTimes(1); + expect(observerNextHandler).toHaveBeenCalledWith( + expect.arrayContaining([{ ...shortcut, id: expect.anything() }]), + ); }); it('should add shortcuts with ids', async () => { diff --git a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts index 884f561aaa..ba3722da66 100644 --- a/plugins/shortcuts/src/api/LocalStoredShortcuts.ts +++ b/plugins/shortcuts/src/api/LocalStoredShortcuts.ts @@ -15,41 +15,45 @@ */ import { pageTheme } from '@backstage/theme'; -import ObservableImpl from 'zen-observable'; 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'; /** * Implementation of the ShortcutApi that uses the StorageApi to store shortcuts. */ export class LocalStoredShortcuts implements ShortcutApi { - constructor(private readonly storageApi: StorageApi) { - this.storageApi.observe$('items').subscribe({ - next: () => this.notify(), - }); - } + constructor(private readonly storageApi: StorageApi) {} shortcut$() { - return this.observable; + return Observable.from(this.storageApi.observe$('items')).map( + snapshot => snapshot.value ?? [], + ); + } + + snapshot() { + return Array.from( + this.storageApi.snapshot('items').value ?? [], + ).sort((a, b) => (a.title >= b.title ? 1 : -1)); } async add(shortcut: Omit) { - const shortcuts = this.get(); + const shortcuts = this.snapshot(); shortcuts.push({ ...shortcut, id: uuid() }); await this.storageApi.set('items', shortcuts); } async remove(id: string) { - const shortcuts = this.get().filter(s => s.id !== id); + const shortcuts = this.snapshot().filter(s => s.id !== id); await this.storageApi.set('items', shortcuts); } async update(shortcut: Shortcut) { - const shortcuts = this.get().filter(s => s.id !== shortcut.id); + const shortcuts = this.snapshot().filter(s => s.id !== shortcut.id); shortcuts.push(shortcut); await this.storageApi.set('items', shortcuts); @@ -64,33 +68,8 @@ export class LocalStoredShortcuts implements ShortcutApi { return pageTheme[theme].colors[0]; } - private subscribers = new Set< - ZenObservable.SubscriptionObserver - >(); - - private readonly observable = new ObservableImpl(subscriber => { - subscriber.next(this.get()); - this.subscribers.add(subscriber); - - return () => { - this.subscribers.delete(subscriber); - }; - }); - private readonly THEME_MAP: Record = { catalog: 'home', docs: 'documentation', }; - - private get() { - return Array.from( - this.storageApi.snapshot('items').value ?? [], - ).sort((a, b) => (a.title >= b.title ? 1 : -1)); - } - - private notify() { - for (const subscription of this.subscribers) { - subscription.next(this.get()); - } - } } diff --git a/plugins/shortcuts/src/api/ShortcutApi.ts b/plugins/shortcuts/src/api/ShortcutApi.ts index 893a2fd74b..542e77bac8 100644 --- a/plugins/shortcuts/src/api/ShortcutApi.ts +++ b/plugins/shortcuts/src/api/ShortcutApi.ts @@ -28,6 +28,11 @@ export interface ShortcutApi { */ shortcut$(): Observable; + /** + * Returns an immediate snapshot of shortcuts, sorted by title + */ + snapshot(): Shortcut[]; + /** * Generates a unique id for the shortcut and then saves it. */