refactor: make prefs & locale to modules

This commit is contained in:
xiangyu 2023-01-05 20:03:50 +08:00
parent 4469cbc82d
commit 626cff548b
11 changed files with 198 additions and 163 deletions

View File

@ -82,7 +82,6 @@ Search `@example` in `src/examples.ts`. The examples are called in `src/hooks.ts
1. When install/enable/startup triggered from Zotero, `bootstrap.js` > `startup` is called
- Wait for Zotero ready
- Prepare global variables `ctx`. They are available globally in the plugin scope
- Load `index.js` (the main entrance of plugin code, built from `index.ts`)
- Register resources if Zotero 7+
2. In the main entrance `index.js`, the plugin object is injected under `Zotero` and `hooks.ts` > `onStartup` is called.
@ -194,7 +193,7 @@ This section shows the directory structure of a template.
│ │
│ └─zh-CN
│ | overlay.dtd
addon.properties
└─ addon.properties
├─builds # build dir
│ └─.xpi
@ -203,10 +202,12 @@ This section shows the directory structure of a template.
│ index.ts # main entry
│ addon.ts # base class
│ hooks.ts # lifecycle hooks
│ examples.ts # examples factory
│ locale.ts # Locale class for properties files
└─ prefs.ts # preferences class
|
└─modules # sub modules
│ examples.ts # examples factory
│ locale.ts # locale .properties
│ preferenceScript.ts # script runs in preferences.xhtml
│ progressWindow.ts # progressWindow tool
```
### Build

View File

@ -1,6 +1,6 @@
<vbox
id="zotero-prefpane-__addonRef__"
onload="Zotero.AddonTemplate.prefs.initPreferences(window)"
onload="Zotero.AddonTemplate.hooks.onCustomEvent('prefLoad', {window})"
>
<groupbox>
<label><html:h2>&zotero.__addonRef__.pref.title;</html:h2></label>
@ -22,5 +22,7 @@
</groupbox>
</vbox>
<vbox>
<label value="&zotero.__addonRef__.help.version.label; &zotero.__addonRef__.help.releasetime.label;"></label>
<label
value="&zotero.__addonRef__.help.version.label; &zotero.__addonRef__.help.releasetime.label;"
></label>
</vbox>

View File

@ -1,24 +1,26 @@
import AddonHooks from "./hooks";
import AddonPrefs from "./prefs";
import AddonLocale from "./locale";
import hooks from "./hooks";
class Addon {
// Env type, see build.js
public env!: "development" | "production";
// If addon is disabled/removed, set it false
public alive: boolean;
// Lifecycle events
public hooks: AddonHooks;
// Scripts for prefpane window
public prefs: AddonPrefs;
// Runtime locale with .properties
public locale: AddonLocale;
public data: {
alive: boolean;
// Env type, see build.js
env: "development" | "production";
locale?: {
stringBundle: any;
};
prefs?: {
window: Window;
};
};
// Lifecycle hooks
public hooks: typeof hooks;
constructor() {
this.alive = true;
this.hooks = new AddonHooks();
this.prefs = new AddonPrefs();
this.locale = new AddonLocale();
this.data = {
alive: true,
env: __env__,
};
this.hooks = hooks;
}
}

View File

@ -1,62 +1,85 @@
import { BasicExampleFactory, UIExampleFactory } from "./examples";
import { changeProgressWindowLine, showProgressWindow } from "./tools/progress";
import { BasicExampleFactory, UIExampleFactory } from "./modules/examples";
import {
changeProgressWindowLine,
showProgressWindow,
} from "./modules/progressWindow";
import { config } from "../package.json";
import { getString, initLocale } from "./modules/locale";
import { registerPrefsScripts } from "./modules/preferenceScript";
class AddonHooks {
public async onStartup() {
addon.locale.initLocale();
async function onStartup() {
initLocale();
const w = showProgressWindow(
config.addonName,
addon.locale.getString("startup.begin"),
"default",
-1
);
changeProgressWindowLine(w, { newProgress: 0 });
const w = showProgressWindow(
config.addonName,
getString("startup.begin"),
"default",
-1
);
changeProgressWindowLine(w, { newProgress: 0 });
BasicExampleFactory.registerPrefs();
BasicExampleFactory.registerPrefs();
BasicExampleFactory.registerNotifier();
BasicExampleFactory.registerNotifier();
await Zotero.Promise.delay(1000);
changeProgressWindowLine(w, {
newProgress: 30,
newText: `[30%] ${addon.locale.getString("startup.begin")}`,
});
await Zotero.Promise.delay(1000);
changeProgressWindowLine(w, {
newProgress: 30,
newText: `[30%] ${getString("startup.begin")}`,
});
UIExampleFactory.registerStyleSheet();
UIExampleFactory.registerStyleSheet();
UIExampleFactory.registerRightClickMenuItem();
UIExampleFactory.registerRightClickMenuItem();
UIExampleFactory.registerRightClickMenuPopup();
UIExampleFactory.registerRightClickMenuPopup();
UIExampleFactory.registerWindowMenuWithSeprator();
UIExampleFactory.registerWindowMenuWithSeprator();
await UIExampleFactory.registerExtraColumn();
await UIExampleFactory.registerExtraColumn();
await UIExampleFactory.registerExtraColumnWithCustomCell();
await UIExampleFactory.registerExtraColumnWithCustomCell();
await UIExampleFactory.registerCustomCellRenderer();
await UIExampleFactory.registerCustomCellRenderer();
UIExampleFactory.registerLibraryTabPanel();
UIExampleFactory.registerLibraryTabPanel();
await UIExampleFactory.registerReaderTabPanel();
await UIExampleFactory.registerReaderTabPanel();
await Zotero.Promise.delay(1000);
changeProgressWindowLine(w, {
newProgress: 100,
newText: `[100%] ${addon.locale.getString("startup.finish")}`,
});
w.startCloseTimer(5000);
}
await Zotero.Promise.delay(1000);
changeProgressWindowLine(w, {
newProgress: 100,
newText: `[100%] ${getString("startup.finish")}`,
});
w.startCloseTimer(5000);
}
public onShutdown(): void {
BasicExampleFactory.unregisterPrefs();
UIExampleFactory.unregisterUIExamples();
// Remove addon object
addon.alive = false;
delete Zotero.AddonTemplate;
function onShutdown(): void {
BasicExampleFactory.unregisterPrefs();
UIExampleFactory.unregisterUIExamples();
// Remove addon object
addon.data.alive = false;
delete Zotero.AddonTemplate;
}
/**
* This function is just a dispatcher for UI events.
* Any operations should be placed in a function to keep this funcion clear
* @param type event type
* @param data event data
*/
function onCustomEvent(type: string, data: { [key: string]: any }) {
switch (type) {
case "prefLoad":
registerPrefsScripts(data.window);
break;
default:
return;
}
}
export default AddonHooks;
export default {
onStartup,
onShutdown,
onCustomEvent,
};

View File

@ -12,10 +12,9 @@ if (!getGlobal("Zotero").AddonTemplate) {
_globalThis.document = getGlobal("document");
_globalThis.ztoolkit = new ZoteroToolkit();
_globalThis.addon = new Addon();
// The env will be replaced after esbuild
addon.env = __env__;
ztoolkit.Tool.logOptionsGlobal.prefix = `[${config.addonName}]`;
ztoolkit.Tool.logOptionsGlobal.disableConsole = addon.env === "production";
ztoolkit.Tool.logOptionsGlobal.disableConsole =
addon.data.env === "production";
Zotero.AddonTemplate = addon;
// Trigger addon hook for initialization
addon.hooks.onStartup();

View File

@ -1,17 +0,0 @@
import { config } from "../package.json";
class AddonLocale {
private stringBundle: any;
public initLocale() {
this.stringBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle(`chrome://${config.addonRef}/locale/addon.properties`);
}
public getString(localString: string): string {
return this.stringBundle.GetStringFromName(localString);
}
}
export default AddonLocale;

View File

@ -1,19 +1,31 @@
import { log } from "zotero-plugin-toolkit/dist/utils";
import { config } from "../package.json";
import { config } from "../../package.json";
import { getString } from "./locale";
export function example(type?: string): MethodDecorator {
return function (
target: Object,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) {
log("Calling example", target, type, propertyKey, descriptor);
return descriptor;
function example(
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any) {
try {
ztoolkit.Tool.log(
`Calling example ${target.name}.${String(propertyKey)}`
);
return original.apply(this, args);
} catch (e) {
ztoolkit.Tool.log(
`Error in example ${target.name}.${String(propertyKey)}`,
e
);
throw e;
}
};
return descriptor;
}
export class BasicExampleFactory {
@example()
@example
static registerNotifier() {
const callback = {
notify: async (
@ -22,7 +34,7 @@ export class BasicExampleFactory {
ids: Array<string>,
extraData: { [key: string]: any }
) => {
if (!addon.alive) {
if (!addon.data.alive) {
this.unregisterNotifier(notifierID);
return;
}
@ -58,23 +70,20 @@ export class BasicExampleFactory {
);
}
@example()
@example
private static unregisterNotifier(notifierID: string) {
Zotero.Notifier.unregisterObserver(notifierID);
}
@example()
@example
static registerPrefs() {
const prefOptions = {
pluginID: config.addonID,
src: rootURI + "chrome/content/preferences.xhtml",
label: addon.locale.getString("prefs.title"),
label: getString("prefs.title"),
image: `chrome://${config.addonRef}/content/icons/favicon.png`,
extraDTD: [`chrome://${config.addonRef}/locale/overlay.dtd`],
defaultXUL: true,
onload: (win: Window) => {
addon.prefs.initPreferences(win);
},
};
if (ztoolkit.Compat.isZotero7()) {
Zotero.PreferencePanes.register(prefOptions);
@ -83,7 +92,7 @@ export class BasicExampleFactory {
}
}
@example()
@example
static unregisterPrefs() {
if (!ztoolkit.Compat.isZotero7()) {
ztoolkit.Compat.unregisterPrefPane();
@ -92,7 +101,7 @@ export class BasicExampleFactory {
}
export class UIExampleFactory {
@example()
@example
static registerStyleSheet() {
const styles = ztoolkit.UI.creatElementsFromJSON(document, {
tag: "link",
@ -108,30 +117,30 @@ export class UIExampleFactory {
?.classList.add("makeItRed");
}
@example()
@example
static registerRightClickMenuItem() {
const menuIcon = `chrome://${config.addonRef}/content/icons/favicon@0.5x.png`;
// item menuitem with icon
ztoolkit.UI.insertMenuItem("item", {
tag: "menuitem",
id: "zotero-itemmenu-addontemplate-test",
label: addon.locale.getString("menuitem.label"),
label: getString("menuitem.label"),
oncommand: "alert('Hello World! Default Menuitem.')",
icon: menuIcon,
});
}
@example()
@example
static registerRightClickMenuPopup() {
ztoolkit.UI.insertMenuItem(
"item",
{
tag: "menu",
label: addon.locale.getString("menupopup.label"),
label: getString("menupopup.label"),
subElementOptions: [
{
tag: "menuitem",
label: addon.locale.getString("menuitem.submenulabel"),
label: getString("menuitem.submenulabel"),
oncommand: "alert('Hello World! Sub Menuitem.')",
},
],
@ -143,7 +152,7 @@ export class UIExampleFactory {
);
}
@example()
@example
static registerWindowMenuWithSeprator() {
ztoolkit.UI.insertMenuItem("menuFile", {
tag: "menuseparator",
@ -151,12 +160,12 @@ export class UIExampleFactory {
// menu->File menuitem
ztoolkit.UI.insertMenuItem("menuFile", {
tag: "menuitem",
label: addon.locale.getString("menuitem.filemenulabel"),
label: getString("menuitem.filemenulabel"),
oncommand: "alert('Hello World! File Menuitem.')",
});
}
@example()
@example
static async registerExtraColumn() {
await ztoolkit.ItemTree.register(
"test1",
@ -175,7 +184,7 @@ export class UIExampleFactory {
);
}
@example()
@example
static async registerExtraColumnWithCustomCell() {
await ztoolkit.ItemTree.register(
"test2",
@ -202,7 +211,7 @@ export class UIExampleFactory {
);
}
@example()
@example
static async registerCustomCellRenderer() {
await ztoolkit.ItemTree.addRenderCellHook(
"title",
@ -218,10 +227,10 @@ export class UIExampleFactory {
await ztoolkit.ItemTree.refresh();
}
@example()
@example
static registerLibraryTabPanel() {
const tabId = ztoolkit.UI.registerLibraryTabPanel(
addon.locale.getString("tabpanel.lib.tab.label"),
getString("tabpanel.lib.tab.label"),
(panel: XUL.Element, win: Window) => {
const elem = ztoolkit.UI.creatElementsFromJSON(win.document, {
tag: "vbox",
@ -266,10 +275,10 @@ export class UIExampleFactory {
);
}
@example()
@example
static async registerReaderTabPanel() {
const tabId = await ztoolkit.UI.registerReaderTabPanel(
addon.locale.getString("tabpanel.reader.tab.label"),
getString("tabpanel.reader.tab.label"),
(
panel: XUL.TabPanel | undefined,
deck: XUL.Deck,
@ -344,8 +353,11 @@ export class UIExampleFactory {
);
}
@example()
@example
static unregisterUIExamples() {
ztoolkit.unregisterAll();
}
}
function initPreferences(win: Window) {
throw new Error("Function not implemented.");
}

13
src/modules/locale.ts Normal file
View File

@ -0,0 +1,13 @@
import { config } from "../../package.json";
export function initLocale() {
addon.data.locale = {
stringBundle: Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle(`chrome://${config.addonRef}/locale/addon.properties`),
};
}
export function getString(localString: string): string {
return addon.data.locale?.stringBundle.GetStringFromName(localString);
}

View File

@ -0,0 +1,41 @@
import { config } from "../../package.json";
export function registerPrefsScripts(_window: Window) {
// This function is called when the prefs window is opened
// See addon/chrome/content/preferences.xul onpaneload
addon.data.prefs = {
window: _window,
};
updatePrefsUI();
bindPrefEvents();
}
function updatePrefsUI() {
// You can initialize some UI elements on prefs window
// with addon.data.prefs.window.document
// Or bind some events to the elements
}
function bindPrefEvents() {
addon.data
.prefs!.window.document.querySelector(
`#zotero-prefpane-${config.addonRef}-enable`
)
?.addEventListener("command", (e) => {
ztoolkit.Tool.log(e);
addon.data.prefs!.window.alert(
`Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
);
});
addon.data
.prefs!!.window.document.querySelector(
`#zotero-prefpane-${config.addonRef}-input`
)
?.addEventListener("change", (e) => {
ztoolkit.Tool.log(e);
addon.data.prefs!.window.alert(
`Successfully changed to ${(e.target as HTMLInputElement).value}!`
);
});
}

View File

@ -1,41 +0,0 @@
import { config } from "../package.json";
class AddonPrefs {
private _window!: Window;
public initPreferences(_window: Window) {
// This function is called when the prefs window is opened
// See addon/chrome/content/preferences.xul onpaneload
this._window = _window;
this.updatePrefsUI();
this.bindPrefEvents();
}
private updatePrefsUI() {
// You can initialize some UI elements on prefs window
// with this._window.document
// Or bind some events to the elements
}
private bindPrefEvents() {
this._window.document
.querySelector(`#zotero-prefpane-${config.addonRef}-enable`)
?.addEventListener("command", (e) => {
ztoolkit.Tool.log(e);
this._window.alert(
`Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
);
});
this._window.document
.querySelector(`#zotero-prefpane-${config.addonRef}-input`)
?.addEventListener("change", (e) => {
ztoolkit.Tool.log(e);
this._window.alert(
`Successfully changed to ${(e.target as HTMLInputElement).value}!`
);
});
}
}
export default AddonPrefs;