|
- import { h, Fragment, Component } from 'preact';
- import type { AnnotationSourceDescriptor } from '../storage/AnnotationSource';
- import { RpcClient } from 'webextension-rpc';
- import type { backgroundRpcServer } from '../background';
- import { MarginalAnnotations } from './MarginalAnnotations';
- import { ToolbarButtons } from './ToolbarButtons';
- import {
- discoverAnnotationsEmbeddedAsJSONLD,
- discoverAnnotationSources,
- } from './discovery';
- import { targetsUrl } from 'web-annotation-utils';
- import { unique } from '../util/unique';
- import type { IAnnotationWithSource } from '../storage/Annotation';
- import { AnnotationCreationHelper } from './AnnotationCreationHelper';
-
- const backgroundRpc = new RpcClient<typeof backgroundRpcServer>();
- const getAnnotationsForTargetUrls = backgroundRpc.func(
- 'getAnnotationsForTargetUrls',
- );
- const isSourceSubscribed = backgroundRpc.func('isSourceSubscribed');
- const updateAnnotation = backgroundRpc.func('updateAnnotation');
- const deleteAnnotation = backgroundRpc.func('deleteAnnotation');
-
- interface AppProps {
- appContainer: Node;
- }
-
- interface AppState {
- storedAnnotations: IAnnotationWithSource[];
- embeddedAnnotations: IAnnotationWithSource[];
- annotationInFocus?: IAnnotationWithSource;
- discoveredLinkedSources: (AnnotationSourceDescriptor & {
- subscribed: boolean;
- })[];
- discoveredEmbeddedSources: (AnnotationSourceDescriptor & {
- subscribed: boolean;
- })[];
- }
-
- export class App extends Component<AppProps, AppState> {
- state: AppState = {
- storedAnnotations: [],
- embeddedAnnotations: [],
- discoveredLinkedSources: [],
- discoveredEmbeddedSources: [],
- };
-
- async componentDidMount() {
- try {
- await Promise.all([
- this.loadStoredAnnotations(),
- this.discoverEmbeddedAnnotations(),
- this.discoverLinkedAnnotationSources(),
- ]);
- } catch (error) {
- console.log(error);
- }
- }
-
- async loadStoredAnnotations() {
- // Find annotations in our storage that target the current page.
- const urls = [document.URL];
- const canonicalLink = document.querySelector(
- 'link[rel~="canonical"]',
- ) as HTMLLinkElement | null;
- if (canonicalLink) urls.push(canonicalLink.href);
- const storedAnnotations = await getAnnotationsForTargetUrls(urls);
- console.log(
- `We got these annotations for <${document.URL}>:`,
- storedAnnotations,
- );
- this.setState({
- storedAnnotations,
- });
- }
-
- async discoverEmbeddedAnnotations() {
- // Find annotations embedded inside the page.
- const embeddedAnnotations = discoverAnnotationsEmbeddedAsJSONLD();
- const embeddedAnnotationsTargetingThisPage = embeddedAnnotations.filter(
- (annotation) => targetsUrl(annotation.target, document.URL),
- );
- console.log(
- `Found ${embeddedAnnotations.length} embedded annotations in the page, of which ${embeddedAnnotationsTargetingThisPage.length} target this page itself.`,
- );
- this.setState({
- embeddedAnnotations: embeddedAnnotationsTargetingThisPage.map(
- (annotation) => ({
- _id: 0,
- source: {
- _id: -1,
- active: false,
- type: 'embeddedJsonld',
- url: document.URL,
- },
- annotation,
- }),
- ),
- });
-
- // A page with embedded annotations targeting *other* pages is considered an annotation source.
- if (
- embeddedAnnotations.length > embeddedAnnotationsTargetingThisPage.length
- ) {
- const pageAsAnnotationSource: AnnotationSourceDescriptor = {
- title: document.title,
- url: document.URL.split('#')[0],
- type: 'embeddedJsonld',
- };
- this.setState({
- discoveredEmbeddedSources: await this.checkDiscoveredAnnotationSources([
- pageAsAnnotationSource,
- ]),
- });
- }
- }
-
- async discoverLinkedAnnotationSources() {
- // Find annotations sources advertised by the current page.
- const discoveredSources = discoverAnnotationSources();
- this.setState({
- discoveredLinkedSources: await this.checkDiscoveredAnnotationSources(
- discoveredSources,
- ),
- });
- }
-
- async checkDiscoveredAnnotationSources(
- discoveredSources: AnnotationSourceDescriptor[],
- ) {
- // For each discovered source, note if we already have it in our database.
- return await Promise.all(
- discoveredSources.map(async (source) => ({
- ...source,
- subscribed: await isSourceSubscribed(source),
- })),
- );
- }
-
- async onAnnotationCreated(annotation: IAnnotationWithSource) {
- await this.loadStoredAnnotations();
- this.setState({ annotationInFocus: annotation });
- }
-
- onSubscriptionChange() {
- this.discoverLinkedAnnotationSources();
- this.discoverEmbeddedAnnotations();
- }
-
- render(
- { appContainer }: AppProps,
- {
- storedAnnotations,
- embeddedAnnotations,
- annotationInFocus,
- discoveredLinkedSources,
- discoveredEmbeddedSources,
- }: AppState,
- ) {
- const annotationsToShow = unique(
- [...storedAnnotations, ...embeddedAnnotations],
- (obj) => obj.annotation.canonical || obj.annotation.id,
- );
- const discoveredSources = [
- ...discoveredLinkedSources,
- ...discoveredEmbeddedSources,
- ];
- const toolbarButtons =
- discoveredSources.length > 0 ? (
- <ToolbarButtons
- {...{
- onChange: () => this.onSubscriptionChange(),
- discoveredSources,
- }}
- />
- ) : undefined;
- return (
- <>
- <MarginalAnnotations
- {...{
- annotations: annotationsToShow,
- annotationInFocus,
- toolbarButtons,
- appContainer,
- onUpdateAnnotation: async (...args) => {
- await updateAnnotation(...args);
- // messes up text while editing.
- // await this.loadStoredAnnotations();
- },
- onDeleteAnnotation: async (...args) => {
- await deleteAnnotation(...args);
- await this.loadStoredAnnotations();
- },
- }}
- />
- <AnnotationCreationHelper
- onAnnotationCreated={(annotation) =>
- this.onAnnotationCreated(annotation)
- }
- />
- </>
- );
- }
- }
|