diff --git a/src/annotator/guest.ts b/src/annotator/guest.ts index 7b6eff876c5..f11fde3590a 100644 --- a/src/annotator/guest.ts +++ b/src/annotator/guest.ts @@ -643,6 +643,13 @@ export class Guest this._sidebarRPC.on('deleteAnnotation', (tag: string) => this.detach(tag)); + // Expose document info to the sidebar on demand, so that annotation + // creation can be triggered from there (e.g. via the experimental new note + // button) + this._sidebarRPC.on('getDocumentInfo', async callback => + callback(await this.getDocumentInfo()), + ); + this._sidebarRPC.on( 'loadAnnotations', async (annotations: AnnotationData[]) => { diff --git a/src/annotator/test/guest-test.js b/src/annotator/test/guest-test.js index 22502fdce73..09f09b419a4 100644 --- a/src/annotator/test/guest-test.js +++ b/src/annotator/test/guest-test.js @@ -1,4 +1,5 @@ -import { delay } from '@hypothesis/frontend-testing'; +import { delay, waitFor } from '@hypothesis/frontend-testing'; +import sinon from 'sinon'; import { EventEmitter } from '../../shared/event-emitter'; import { DrawError } from '../draw-tool'; @@ -736,6 +737,30 @@ describe('Guest', () => { }); }); + describe('on "getDocumentInfo" event', () => { + it('gets guest document info', async () => { + const callback = sinon.stub(); + + createGuest(); + emitSidebarEvent('getDocumentInfo', callback); + + await waitFor(() => callback.called); + + assert.calledWith( + callback, + sinon.match({ + uri: 'https://example.com/test.pdf', + metadata: { + title: 'Test title', + documentFingerprint: 'test-fingerprint', + }, + }), + ); + assert.called(fakeIntegration.uri); + assert.called(fakeIntegration.getMetadata); + }); + }); + describe('on "featureFlagsUpdated" event', () => { it('updates active feature flags', () => { const flagsUpdated = sinon.stub(); diff --git a/src/sidebar/components/SidebarTabs.tsx b/src/sidebar/components/SidebarTabs.tsx index 2283c466c3d..8423b7eac6a 100644 --- a/src/sidebar/components/SidebarTabs.tsx +++ b/src/sidebar/components/SidebarTabs.tsx @@ -8,6 +8,7 @@ import { } from '@hypothesis/frontend-shared'; import classnames from 'classnames'; import type { ComponentChildren } from 'preact'; +import { useCallback } from 'preact/hooks'; import { pluralize } from '../../shared/pluralize'; import type { SidebarSettings } from '../../types/config'; @@ -15,6 +16,7 @@ import type { TabName } from '../../types/sidebar'; import { applyTheme } from '../helpers/theme'; import { withServices } from '../service-context'; import type { AnnotationsService } from '../services/annotations'; +import type { FrameSyncService } from '../services/frame-sync'; import { useSidebarStore } from '../store'; import ThreadList from './ThreadList'; import { useRootThread } from './hooks/use-root-thread'; @@ -99,6 +101,7 @@ export type SidebarTabsProps = { // injected settings: SidebarSettings; annotationsService: AnnotationsService; + frameSync: FrameSyncService; }; /** @@ -108,6 +111,7 @@ function SidebarTabs({ annotationsService, isLoading, settings, + frameSync, }: SidebarTabsProps) { const { rootThread, tabCounts } = useRootThread(); const store = useSidebarStore(); @@ -143,6 +147,11 @@ function SidebarTabs({ } const tabCountsSummary = tabCountsSummaryPieces.join(', '); + const createPageNoteWithDocumentMeta = useCallback(async () => { + const { metadata } = await frameSync.getDocumentInfo(); + annotationsService.createPageNote(metadata); + }, [annotationsService, frameSync]); + return ( <>
@@ -201,7 +210,7 @@ function SidebarTabs({