Skip to content

Commit

Permalink
refactor: edit action
Browse files Browse the repository at this point in the history
  • Loading branch information
windingwind committed Nov 10, 2023
1 parent f71723b commit 154f4fc
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 335 deletions.
8 changes: 5 additions & 3 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
unInitWindowShortcuts,
} from "./modules/shortcuts";
import { buildItemMenu, initMenu, initReaderMenu } from "./modules/menu";
import { editAction } from "./modules/edit";

async function onStartup() {
await Promise.all([
Expand All @@ -32,7 +33,7 @@ async function onStartup() {

await addon.api.actionManager.dispatchActionByEvent(
ActionEventTypes.programStartup,
{},
{}
);

initReaderShortcuts();
Expand All @@ -49,7 +50,7 @@ async function onMainWindowLoad(win: Window): Promise<void> {
ActionEventTypes.mainWindowLoad,
{
window: win,
},
}
);
}

Expand All @@ -59,7 +60,7 @@ async function onMainWindowUnload(win: Window): Promise<void> {
ActionEventTypes.mainWindowUnload,
{
window: win,
},
}
);
}

Expand Down Expand Up @@ -108,4 +109,5 @@ export default {
onMainWindowUnload,
onPrefsEvent,
onMenuEvent,
onActionEdit: editAction,
};
331 changes: 331 additions & 0 deletions src/modules/edit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
import { ActionEventTypes, ActionOperationTypes } from "../utils/actions";
import { getString } from "../utils/locale";
import { getPref } from "../utils/prefs";
import { KeyModifier } from "../utils/shorcut";
import { waitUtilAsync } from "../utils/wait";
import { closeWindow, isWindowAlive } from "../utils/window";

export { editAction };

async function editAction(currentKey?: string) {
let edited = false;
currentKey = currentKey || addon.data.actions.selectedKey;
if (!currentKey) return false;
const action = addon.data.actions.map.get(currentKey);
if (!action) return false;
const win = addon.data.prefs?.window;
if (!isWindowAlive(win)) return false;
closeWindow(addon.data.prefs.dialogWindow!);
addon.data.prefs.editorInstance = undefined;

const dialogData: { [key: string | number]: any } = Object.assign({}, action);
dialogData.shortcut =
new KeyModifier(action.shortcut || "").getLocalized() ||
`[${getString("prefs-rule-edit-shortcut-empty")}]`;
const dialog = new ztoolkit.Dialog(1, 1)
.setDialogData(dialogData)
.addCell(0, 0, {
tag: "div",
styles: {
display: "grid",
gridTemplateColumns: "1fr 4fr",
rowGap: "10px",
columnGap: "5px",
},
children: [
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-name"),
},
},
{
tag: "input",
attributes: {
"data-bind": "name",
"data-prop": "value",
},
styles: {
width: "fit-content",
},
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-event"),
},
},
{
tag: "select",
properties: {
value: action.event,
},
attributes: {
"data-bind": "event",
"data-prop": "value",
},
children: getEnumKeys(ActionEventTypes).map((key) => ({
tag: "option",
properties: {
innerHTML: getString(`prefs-rule-event-${key}`),
value: ActionEventTypes[key as keyof typeof ActionEventTypes],
},
})),
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-operation"),
},
},
{
tag: "select",
properties: {
value: action.operation,
},
attributes: {
"data-bind": "operation",
"data-prop": "value",
},
children: getEnumKeys(ActionOperationTypes).map((key) => ({
tag: "option",
properties: {
innerHTML: getString(`prefs-rule-operation-${key}`),
value:
ActionOperationTypes[key as keyof typeof ActionOperationTypes],
},
})),
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-data"),
},
},
{
tag: "hbox",
children: [
{
tag: "textarea",
id: "data-input",
properties: {
value: action.data,
rows: 1,
},
styles: {
resize: "none",
height: "1.2em",
fontSize: "inherit",
},
attributes: {
"data-bind": "data",
"data-prop": "value",
},
},
{
tag: "button",
properties: {
textContent: "⤤",
},
listeners: [
{
type: "click",
listener: async () => {
const content = await openEditorWindow(dialogData.data);
(
dialog.window.document.querySelector(
"#data-input"
) as HTMLTextAreaElement
).value = content;
dialogData.data = content;
},
},
],
},
],
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-shortcut"),
},
},
{
tag: "button",
attributes: {
"data-bind": "shortcut",
"data-prop": "textContent",
},
styles: {
width: "fit-content",
},
listeners: [
{
type: "click",
listener: (ev) => {
// Record pressed key
const key = ev.target as HTMLElement;
const win = dialog.window;
key.textContent = `[${getString(
"prefs-rule-edit-shortcut-placeholder"
)}]`;
dialogData.shortcut = "";
const keyDownListener = (e: KeyboardEvent) => {
e.preventDefault();
e.stopPropagation();
const shortcut = new KeyModifier(dialogData.shortcut);
shortcut.control = e.ctrlKey;
shortcut.meta = e.metaKey;
shortcut.shift = e.shiftKey;
shortcut.alt = e.altKey;
if (
!["Shift", "Meta", "Ctrl", "Alt", "Control"].includes(e.key)
) {
shortcut.key = e.key;
}
dialogData.shortcut = shortcut.getLocalized();
key.textContent = shortcut.getLocalized();
};
const keyUpListener = (e: KeyboardEvent) => {
e.preventDefault();
e.stopPropagation();
win.removeEventListener("keydown", keyDownListener);
win.removeEventListener("keyup", keyUpListener);
};
win.addEventListener("keydown", keyDownListener);
win.addEventListener("keyup", keyUpListener);
},
},
],
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-menu"),
},
},
{
tag: "input",
attributes: {
"data-bind": "menu",
"data-prop": "value",
},
properties: {
placeholder: getString("prefs-rule-edit-menu-placeholder"),
},
styles: {
width: "fit-content",
},
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-enabled"),
},
},
{
tag: "input",
properties: {
type: "checkbox",
checked: action.enabled,
},
attributes: {
"data-bind": "enabled",
"data-prop": "checked",
},
styles: {
width: "fit-content",
},
},
],
})
.addButton(getString("prefs-rule-edit-save"), "save")
.addButton(getString("prefs-rule-edit-cancel"), "cancel")
.addButton(getString("prefs-rule-edit-delete"), "delete")
.open(getString("prefs-rule-edit-title"), {
centerscreen: true,
noDialogMode: true,
fitContent: true,
});
addon.data.prefs.dialogWindow = dialog.window;
await dialogData.unloadLock?.promise;
switch (dialogData._lastButtonId) {
case "save":
{
if (
Number(dialogData.operation) === ActionOperationTypes.script &&
!getPref("ruleWarningDisabled") &&
!win?.confirm(getString("prefs-script-warning"))
) {
break;
}
addon.api.actionManager.updateAction(
{
event: Number(dialogData.event),
operation: Number(dialogData.operation),
data:
addon.data.prefs.editorInstance?.getValue() || dialogData.data,
// Replace things inside []
shortcut: dialogData.shortcut.replace(/\[(.*?)\]/g, ""),
enabled: dialogData.enabled,
menu: dialogData.menu,
name: dialogData.name,
},
currentKey
);
edited = true;
}
break;
case "delete": {
addon.api.actionManager.deleteAction(currentKey);
edited = true;
break;
}
default:
break;
}
closeWindow(addon.data.prefs.editorWindow!);
return edited;
}

async function openEditorWindow(content: string) {
const unloadLock = Zotero.Promise.defer();
let modifiedContent = content;
const editorWin = addon.data.prefs.window?.openDialog(
"chrome://scaffold/content/monaco/monaco.html",
"monaco",
"chrome,centerscreen,dialog=no,resizable,scrollbars=yes,width=800,height=600"
) as
| (Window & {
loadMonaco: (options: Record<string, any>) => Promise<{ editor: any }>;
})
| undefined;
if (!editorWin) {
return content;
}
await waitUtilAsync(() => !!editorWin.loadMonaco);
const { editor } = await editorWin.loadMonaco({
language: "javascript",
theme: "vs-light",
});
addon.data.prefs.editorWindow = editorWin;
addon.data.prefs.editorInstance = editor;
editorWin.addEventListener("unload", () => {
modifiedContent = editor.getValue();
unloadLock.resolve();
});
editor.setValue(content);
await unloadLock.promise;
return modifiedContent;
}

function getEnumKeys(v: any): string[] {
return Object.values(v).filter((key) => typeof key === "string") as string[];
}
Loading

0 comments on commit 154f4fc

Please sign in to comment.