Browser extension that demonstrates the Web Annotation Discovery mechanism: subscribe to people’s annotation collections/‘feeds’, to see their notes on the web; and create & publish annotations yourself.

web-annotation-discovery-we.../src/content_script/ AnnotationCreationHelper.tsx
73 lines
2.2 KiB

  1. import { Component } from 'preact';
  2. import type { IAnnotationWithSource } from '../storage/Annotation';
  3. import { RpcClient } from 'webextension-rpc';
  4. import { describeTextQuote } from '@apache-annotator/dom';
  5. import type { WebAnnotation } from 'web-annotation-utils';
  6. import type { backgroundRpcServer } from '../background';
  7. const backgroundRpc = new RpcClient<typeof backgroundRpcServer>();
  8. const createAnnotation = backgroundRpc.func('createAnnotation');
  9. export async function annotateSelection() {
  10. // TODO Use something else than document, so the page’s own scripts cannot observe the reader’s annotation activity.
  11. document.dispatchEvent(new CustomEvent('createAnnotation'));
  12. }
  13. function deselect() {
  14. const selection = window.getSelection();
  15. if (selection) selection.removeAllRanges();
  16. }
  17. interface AnnotationCreationHelperProps {
  18. onAnnotationCreated: (annotation: IAnnotationWithSource) => void;
  19. }
  20. interface AnnotationCreationHelperState {}
  21. export class AnnotationCreationHelper extends Component<
  22. AnnotationCreationHelperProps,
  23. AnnotationCreationHelperState
  24. > {
  25. state: AnnotationCreationHelperState = {};
  26. constructor() {
  27. super();
  28. this.onCreateAnnotation = this.onCreateAnnotation.bind(this);
  29. }
  30. override componentDidMount(): void {
  31. document.addEventListener('createAnnotation', this.onCreateAnnotation);
  32. }
  33. override componentWillUnmount(): void {
  34. document.removeEventListener('createAnnotation', this.onCreateAnnotation);
  35. }
  36. async onCreateAnnotation() {
  37. const target: WebAnnotation['target'] = {
  38. source: document.URL.split('#')[0],
  39. };
  40. const selection = window.getSelection();
  41. if (selection && !selection.isCollapsed) {
  42. const range = selection.getRangeAt(0);
  43. const selector = await describeTextQuote(range);
  44. target.selector = selector;
  45. deselect();
  46. }
  47. let createdAnnotation;
  48. const annotationStub = { target }
  49. try {
  50. createdAnnotation = await createAnnotation(annotationStub);
  51. } catch (error: any) {
  52. const newError = new Error(`Error creating the annotation: ${error.message}`)
  53. alert(newError.message);
  54. throw newError;
  55. }
  56. this.props.onAnnotationCreated(createdAnnotation);
  57. }
  58. render() {
  59. return undefined;
  60. }
  61. }