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/background/ index.ts
85 lines
2.9 KiB

  1. import '../webextension-polyfill';
  2. import { RpcServer } from 'webextension-rpc';
  3. import type { WebAnnotation } from 'web-annotation-utils';
  4. import { Annotation, IAnnotation, IAnnotationWithSource } from '../storage/Annotation';
  5. import {
  6. AnnotationSource,
  7. AnnotationSourceDescriptor,
  8. } from '../storage/AnnotationSource';
  9. import './detect-annotation';
  10. import './context-menu';
  11. const backgroundRpcServer = new RpcServer({
  12. async getAnnotationsForTargetUrls(urls: string[]): Promise<IAnnotationWithSource[]> {
  13. const annotations = await Annotation.getAnnotationsForUrls(urls);
  14. const annotationsWithSources = await Promise.all(annotations.map(async (a) => a.expand()));
  15. return annotationsWithSources
  16. },
  17. refreshAnnotationSource: async (
  18. source: AnnotationSourceDescriptor,
  19. force = false,
  20. ) => {
  21. const sourceObj = await AnnotationSource.getByUrl(source.url);
  22. await sourceObj.refresh(force);
  23. },
  24. refreshAnnotationSources,
  25. addAnnotationSource: async (source: AnnotationSourceDescriptor) =>
  26. AnnotationSource.addSource(source),
  27. isSourceSubscribed: async (source: AnnotationSourceDescriptor) =>
  28. AnnotationSource.exists(source),
  29. removeSource: async (sourceDescriptor: AnnotationSourceDescriptor) => {
  30. const sourceObj = await AnnotationSource.getByUrl(sourceDescriptor.url);
  31. await sourceObj.delete();
  32. },
  33. async createAnnotation(annotationStub: Partial<WebAnnotation>) {
  34. const annotation = await AnnotationSource.createAnnotation(annotationStub);
  35. return await annotation.expand();
  36. },
  37. async updateAnnotation(id: IAnnotation['_id'], webAnnotation: WebAnnotation) {
  38. const annotation = await Annotation.get(id);
  39. await annotation.update(webAnnotation);
  40. },
  41. async deleteAnnotation(id: IAnnotation['_id']) {
  42. const annotation = await Annotation.get(id);
  43. await annotation.delete();
  44. },
  45. });
  46. export type { backgroundRpcServer };
  47. async function refreshAnnotationSources({
  48. forceAll = false,
  49. }: { forceAll?: boolean } = {}) {
  50. const sourcesToUpdate = await (forceAll
  51. ? AnnotationSource.getActiveSources()
  52. : AnnotationSource.getSourcesNeedingUpdate());
  53. console.log(`Will update ${sourcesToUpdate.length} sources.`);
  54. await Promise.all(
  55. sourcesToUpdate.map(async (source) => {
  56. try {
  57. await source.refresh();
  58. } catch (error) {
  59. console.log(
  60. `Failed to refresh source “${source.data.title}” <${source.data.url}>`,
  61. error,
  62. );
  63. }
  64. }),
  65. );
  66. }
  67. main();
  68. async function main() {
  69. await refreshAnnotationSources();
  70. await browser.alarms.clearAll();
  71. browser.alarms.create('annotationPeriodicRefresh', { periodInMinutes: 1 });
  72. if (!browser.alarms.onAlarm.hasListener(handleAlarm)) {
  73. browser.alarms.onAlarm.addListener(handleAlarm);
  74. }
  75. }
  76. async function handleAlarm(alarm: browser.alarms.Alarm) {
  77. if (alarm.name !== 'annotationPeriodicRefresh') return;
  78. await refreshAnnotationSources();
  79. }