Skip to content

Commit

Permalink
feat: Add syncing with link
Browse files Browse the repository at this point in the history
make tests pass
  • Loading branch information
auscyber committed Jan 12, 2025
1 parent a04581c commit 9a7d8e9
Show file tree
Hide file tree
Showing 18 changed files with 7,085 additions and 33 deletions.
6,767 changes: 6,767 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ const builds: (BuildOptions & { entryPoints: [string] })[] = [
sourcemap,
target,
},
{
bundle: true,
entryPoints: ['src/content/prefs/dialog.tsx'],
external: ['components/*', 'react', 'react-dom'],
format: 'iife',
globalName: 'notero',
outdir: 'build/content/prefs',
sourcemap,
target,
},
];

Promise.all(
Expand Down
10 changes: 10 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {

buildInputs = [
pkgs.nodejs
pkgs.pnpm
];

}
11 changes: 11 additions & 0 deletions src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ function log(msg: string) {
Zotero.debug(`${LOG_PREFIX}${msg}`);
Zotero.log(`${LOG_PREFIX}${msg}`, 'info');
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let chromeHandle;

/**
*
Expand Down Expand Up @@ -40,7 +42,16 @@ async function startup(
) {
log(`Starting v${version}`);

Zotero.log(rootURI);
Services.scriptloader.loadSubScript(rootURI + 'content/notero.js');
// @ts-expect-error - `Zotero.Notero` is not defined in the Zotero namespace
const aomStartup = Components.classes[
'@mozilla.org/addons/addon-manager-startup;1'
].getService(Components.interfaces.amIAddonManagerStartup);
const manifestURI = Services.io.newURI(rootURI + 'manifest.json');
chromeHandle = aomStartup.registerChrome(manifestURI, [
['content', 'notero', rootURI + 'content/'],
]);

await Zotero.Notero?.startup({ pluginID: id, rootURI, version });
}
Expand Down
1 change: 1 addition & 0 deletions src/content/prefs/collection-sync-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getNoteroPref, NoteroPref, setNoteroPref } from './notero-pref';
export type CollectionSyncConfig = {
notionOptionID?: string;
syncEnabled: boolean;
associatedLink?: string;
};

export type CollectionSyncConfigsRecord = Record<
Expand Down
72 changes: 72 additions & 0 deletions src/content/prefs/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { isFullPage } from '@notionhq/client';

import { getGlobalNotero, getXULElementById } from '../utils';
import { setMenuItems } from '../utils/elements';

import { getNoteroPref, NoteroPref } from './notero-pref';

type DialogArguments = {
accepted: boolean;
associatedLink: string;
syncEnabled: boolean;
};

class Dialog {
private collectionDialogContainer!: XUL.XULElement;
private associatedLinkElement!: XUL.MenuListElement;
private syncEnabledElement!: XUL.CheckboxElement;
private params!: DialogArguments;

public async init(): Promise<void> {
await Zotero.uiReadyPromise;

/* eslint-disable @typescript-eslint/no-non-null-assertion */
this.params = window.arguments![0]! as DialogArguments;

/* eslint-disable @typescript-eslint/no-non-null-assertion */
this.collectionDialogContainer = getXULElementById(
'notero-collection-dialog',
)!;
this.associatedLinkElement = getXULElementById('notero-associatedLink')!;

const notero = getGlobalNotero();
const pref = getNoteroPref(NoteroPref.linkedCollectionID);
if (pref) {
const client = await notero.getNotionClient();
const res = await client.databases.query({ database_id: pref });
const results = res.results
.map((result: (typeof res.results)[0]) => {
if (isFullPage(result)) {
return {
value: result.id,
label: Object.values(result.properties).find(
(prop) => prop.type === 'title',
)?.title[0]!.plain_text,
};
}
})
.filter((item) => item != undefined);
setMenuItems(this.associatedLinkElement, results);
}

this.associatedLinkElement.value = this.params.associatedLink;
this.syncEnabledElement = getXULElementById('notero-syncEnabled')!;
this.syncEnabledElement.checked = this.params.syncEnabled;
document.addEventListener('dialogaccept', () => {
return this.accept();
});
}
accept(): boolean {
// @ts-expect-error dataOut is not defined in the type
window.arguments![0]!.dataOut = {
associatedLink: this.associatedLinkElement.value,
syncEnabled: this.syncEnabledElement.checked,
accepted: true,
};
return true;
}
}

module.exports = {
dialog: new Dialog(),
};
48 changes: 48 additions & 0 deletions src/content/prefs/dialog.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet
href="chrome://zotero/skin/zotero.css" type="text/css"?> <?xml-stylesheet
href="chrome://zotero-platform/content/zotero.css" type="text/css"?>

<window
type="child"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
style="width: 100px; height: 100px"
buttons="cancel,accept"
onload="notero.dialog.init();"
>
<script>
Services.scriptloader.loadSubScript(
'chrome://zotero/content/customElements.js',
this,
);

Services.scriptloader.loadSubScript(
'chrome://notero/content/prefs/dialog.js',
this,
);
</script>
<dialog
id="notero-collection-dialog"
title="Collection Properties"
onload="notero.dialog.init();"
>
<div>
<label>Collection Settings</label>
<grid>
<rows>
<row>
<label value="Associated Link:" />
<menulist id="notero-associatedLink">
<menupopup />
</menulist>
</row>
<row>
<label value="Sync Enabled:" />
<checkbox id="notero-syncEnabled" />
</row>
</rows>
</grid>
</div>
</dialog>
</window>
3 changes: 3 additions & 0 deletions src/content/prefs/notero-pref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MissingPrefError } from '../errors';

export enum NoteroPref {
collectionSyncConfigs = 'collectionSyncConfigs',
linkedCollectionID = 'linkedCollectionID',
notionDatabaseID = 'notionDatabaseID',
notionToken = 'notionToken',
pageTitleFormat = 'pageTitleFormat',
Expand Down Expand Up @@ -37,6 +38,7 @@ export const PAGE_TITLE_FORMAT_L10N_IDS: Record<

type NoteroPrefValue = Partial<{
[NoteroPref.collectionSyncConfigs]: string;
[NoteroPref.linkedCollectionID]: string;
[NoteroPref.notionDatabaseID]: string;
[NoteroPref.notionToken]: string;
[NoteroPref.pageTitleFormat]: PageTitleFormat;
Expand Down Expand Up @@ -84,6 +86,7 @@ function convertRawPrefValue<P extends NoteroPref>(

return {
[NoteroPref.collectionSyncConfigs]: stringPref,
[NoteroPref.linkedCollectionID]: stringPref,
[NoteroPref.notionDatabaseID]: stringPref,
[NoteroPref.notionToken]: stringPref,
[NoteroPref.pageTitleFormat]: pageTitleFormatPref,
Expand Down
37 changes: 12 additions & 25 deletions src/content/prefs/preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,24 @@ import React from 'react';
import ReactDOM from 'react-dom';
import type { createRoot } from 'react-dom/client';

import type { FluentMessageId } from '../../locale/fluent-types';
import type { NotionAuthManager } from '../auth';
import { LocalizableError } from '../errors';
import type { EventManager } from '../services';
import { getNotionClient } from '../sync/notion-client';
import { isNotionErrorWithCode, normalizeID } from '../sync/notion-utils';
import {
createXULElement,
getGlobalNotero,
getLocalizedErrorMessage,
getXULElementById,
logger,
} from '../utils';
import { MenuItem, setMenuItems } from '../utils/elements';

import { PAGE_TITLE_FORMAT_L10N_IDS, PageTitleFormat } from './notero-pref';
import { SyncConfigsTable } from './sync-configs-table';

type ReactDOMClient = typeof ReactDOM & { createRoot: typeof createRoot };

type MenuItem = {
disabled?: boolean;
l10nId?: FluentMessageId;
label?: string;
value: string;
};

function setMenuItems(menuList: XUL.MenuListElement, items: MenuItem[]): void {
menuList.menupopup.replaceChildren();

items.forEach(({ disabled, l10nId, label, value }) => {
const item = createXULElement(document, 'menuitem');
item.value = value;
item.disabled = Boolean(disabled);
if (l10nId) {
document.l10n.setAttributes(item, l10nId);
} else {
item.label = label || value;
}
menuList.menupopup.append(item);
});
}

class Preferences {
private eventManager!: EventManager;
private notionAuthManager!: NotionAuthManager;
Expand All @@ -54,6 +30,7 @@ class Preferences {
private notionConnectButton!: XUL.ButtonElement;
private notionUpgradeConnectionButton!: XUL.ButtonElement;
private notionDatabaseMenu!: XUL.MenuListElement;
private notionLinkedDatabaseMenu!: XUL.MenuListElement;
private notionError!: XUL.LabelElement;
private notionWorkspaceLabel!: XUL.LabelElement;
private pageTitleFormatMenu!: XUL.MenuListElement;
Expand All @@ -77,6 +54,9 @@ class Preferences {
)!;
this.notionWorkspaceLabel = getXULElementById('notero-notionWorkspace')!;
this.notionDatabaseMenu = getXULElementById('notero-notionDatabase')!;
this.notionLinkedDatabaseMenu = getXULElementById(
'notero-notionLinkedDatabase',
)!;
this.notionError = getXULElementById('notero-notionError')!;
this.pageTitleFormatMenu = getXULElementById('notero-pageTitleFormat')!;
/* eslint-enable @typescript-eslint/no-non-null-assertion */
Expand Down Expand Up @@ -133,9 +113,13 @@ class Preferences {
const syncEnabled = await document.l10n.formatValue(
'notero-preferences-sync-enabled-column',
);
const associatedLink = await document.l10n.formatValue(
'notero-preferences-associatedLink-column',
);
const columnLabels = {
collectionFullName: collection || 'Collection',
syncEnabled: syncEnabled || 'Sync Enabled',
associatedLink: associatedLink || 'Associated Link',
};

(ReactDOM as ReactDOMClient)
Expand Down Expand Up @@ -215,6 +199,7 @@ class Preferences {
let menuItems: MenuItem[] = [];

this.notionDatabaseMenu.disabled = true;
this.notionLinkedDatabaseMenu.disabled = true;

try {
const databases = await this.retrieveNotionDatabases(notion);
Expand All @@ -231,8 +216,10 @@ class Preferences {
});

this.notionDatabaseMenu.disabled = false;
this.notionLinkedDatabaseMenu.disabled = false;
} finally {
setMenuItems(this.notionDatabaseMenu, menuItems);
setMenuItems(this.notionLinkedDatabaseMenu, menuItems);
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/content/prefs/preferences.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@
<menupopup />
</menulist>
</hbox>
<hbox align="center">
<label
control="notero-notionLinkedDatabase"
data-l10n-id="notero-preferences-notion-linked-database"
/>
<menulist
id="notero-notionLinkedDatabase"
disabled="true"
native="true"
preference="extensions.notero.linkedCollectionID"
>
<menupopup />
</menulist>
</hbox>
</vbox>
<label class="notero-error" hidden="true" id="notero-notionError" />
</groupbox>
Expand Down
Loading

0 comments on commit 9a7d8e9

Please sign in to comment.