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/ ToolbarButtons.tsx
134 lines
4.8 KiB

  1. import { h, Component, ComponentChild } from 'preact';
  2. import classes from './MarginalAnnotations.module.scss';
  3. import type { backgroundRpcServer } from '../background';
  4. import type { AnnotationSourceDescriptor } from '../storage/AnnotationSource';
  5. import { RpcClient } from 'webextension-rpc';
  6. import rssIcon from '../assets/icons/rss.svg';
  7. import infoIcon from '../assets/icons/info.svg';
  8. import downloadIcon from '../assets/icons/download.svg';
  9. const backgroundRpc = new RpcClient<typeof backgroundRpcServer>();
  10. const addAnnotationSource = backgroundRpc.func('addAnnotationSource');
  11. const removeSource = backgroundRpc.func('removeSource');
  12. const refreshAnnotationSource = backgroundRpc.func('refreshAnnotationSource');
  13. interface ToolbarButtonsProps {
  14. discoveredSources: (AnnotationSourceDescriptor & { subscribed: boolean })[];
  15. onChange: () => void;
  16. }
  17. interface ToolbarButtonsState {
  18. showAll: boolean;
  19. }
  20. export class ToolbarButtons extends Component<
  21. ToolbarButtonsProps,
  22. ToolbarButtonsState
  23. > {
  24. render(
  25. { discoveredSources, onChange }: ToolbarButtonsProps,
  26. {}: ToolbarButtonsState,
  27. ) {
  28. const buttons: ComponentChild[] = [];
  29. for (const source of discoveredSources) {
  30. let explanationSummary, explanationFull;
  31. if (source.type === 'container') {
  32. explanationSummary = (
  33. <span>
  34. <img src={rssIcon} /> This page provides an annotation source{' '}
  35. <i>“{source.title}”</i>. <img src={infoIcon} />
  36. </span>
  37. );
  38. explanationFull = `If you subscribe to it, annotations published by this website will be stored in your browser, and displayed on the web pages they target. Its annotations are automatically refreshed periodically.`;
  39. } else if (source.type === 'embeddedJsonld') {
  40. explanationSummary = (
  41. <span>
  42. This page contains embedded annotations. <img src={infoIcon} />
  43. </span>
  44. );
  45. explanationFull = `If you import them, they will be stored in your browser, and displayed on the web pages they target. Note they will not be refreshed automatically.`;
  46. }
  47. buttons.push(
  48. <details class={classes.hiddenWhenClosed}>
  49. <summary>{explanationSummary}</summary>
  50. {explanationFull}
  51. </details>,
  52. );
  53. if (source.subscribed) {
  54. if (source.type === 'container') {
  55. buttons.push(
  56. <button
  57. title="Unsubscribe from this annotation source (forget its annotations)"
  58. onClick={async () => {
  59. await removeSource(source);
  60. onChange();
  61. }}
  62. >
  63. ❌<span class={classes.hiddenWhenClosed}> Unsubscribe</span>
  64. </button>,
  65. <button
  66. title="Refresh annotations from this source."
  67. onClick={() => refreshAnnotationSource(source, true)}
  68. >
  69. 🗘<span class={classes.hiddenWhenClosed}> Refresh</span>
  70. </button>,
  71. );
  72. } else if (source.type === 'embeddedJsonld') {
  73. buttons.push(
  74. <button
  75. title="Remove annotations from this source."
  76. onClick={async () => {
  77. await removeSource(source);
  78. onChange();
  79. }}
  80. >
  81. ❌<span class={classes.hiddenWhenClosed}> Remove</span>
  82. </button>,
  83. <button
  84. title="Refresh annotations from this source."
  85. onClick={() => refreshAnnotationSource(source, true)}
  86. >
  87. 🗘<span class={classes.hiddenWhenClosed}> Refresh</span>
  88. </button>,
  89. );
  90. }
  91. } else {
  92. if (source.type === 'container') {
  93. buttons.push(
  94. <button
  95. onClick={async () => {
  96. await addAnnotationSource(source);
  97. onChange();
  98. }}
  99. title="Subscribe to annotations from this website."
  100. >
  101. <img src={rssIcon} class={classes.icon} />
  102. <span class={classes.hiddenWhenClosed}> Subscribe</span>
  103. </button>,
  104. );
  105. } else if (source.type === 'embeddedJsonld') {
  106. buttons.push(
  107. <button
  108. onClick={async () => {
  109. await addAnnotationSource(source);
  110. onChange();
  111. }}
  112. title="Import the annotations embedded in this page."
  113. >
  114. <img src={downloadIcon} class={classes.icon} />
  115. <span class={classes.hiddenWhenClosed}> Import annotations</span>
  116. </button>,
  117. );
  118. }
  119. }
  120. }
  121. return buttons.length > 0 ? buttons : null;
  122. }
  123. }
  124. // TODO Refresh components properly. instead of reload the whole page. :)
  125. function pageReload() {
  126. window.location = window.location;
  127. }