Skip to content

Commit

Permalink
make preload script bearable
Browse files Browse the repository at this point in the history
  • Loading branch information
astudentinearth committed Dec 20, 2024
1 parent 4e1f7e3 commit 497a4bc
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 179 deletions.
131 changes: 0 additions & 131 deletions packages/app-desktop/src/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,131 +0,0 @@
import {
DarkwriteDesktopClientInfo,
FileImportResult,
Note,
NoteExportType,
NotePartial,
ResolvedEmbed,
Theme,
} from "@darkwrite/common";
import { UserSettings } from "@darkwrite/common";

export interface DarkwriteElectronAPI {
/**
* Displays the app menu
*/
showAppMenu: () => void;
note: {
/** Creates a new note.
* @returns the Note object if successful, null if unsuccessful
*/
create: (title: string, parent?: string) => Promise<Note | null>;
/**
* Updates the stored JSON contents of the note.
* @param id ID of the note
* @param content stringified JSON to overwrite with
* @returns
*/
setContents: (id: string, content: string) => Promise<void>;
/**
* Reads the stored JSON contents of the note.
* @param id - ID of the note
* @returns JSON content in string form
*/
getContents: (id: string) => Promise<string>;
/**
* Deletes a note **permanently**.
* @param id ID of the note to delete
*/
delete: (id: string) => Promise<void>;
/**
* Moves a note below another note, or to the top level
* @param sourceID - ID of the note to move
* @param destID - ID of the note to move into, or `undefined` if the top level is desired
* @returns
*/
move: (sourceID: string, destID: string | undefined) => Promise<void>;
/**
* Performs a database update for the given note. All fields are updated with the passed parameter.
* @param note The partial note object to update with. An ID must be provided.
*/
update: (note: NotePartial) => Promise<void>;
/**
* Gets all notes from the database.
* @returns an array of notes.
*/
getAll: () => Promise<Note[]>;
/**
* Moves a note in or out of trash.
* @param id - ID of the note
* @param state - true moves to trash, false moves out of trash
*/
setTrashStatus: (id: string, state: boolean) => Promise<void>;
/**
* Finds a single note
* @param id - ID of the note to look for
* @returns the note if it exists, null if it doesn't exist or something goes wrong
*/
getNote: (id: string) => Promise<Note | null>;
/**
* Updates multiple notes at once
* @param notes array of notes to update
*/
saveAll: (notes: Note[]) => Promise<void>;
/**
* Shows a save file dialog to export a note
* @param title note title to use as file name
* @param content contents of the note
* @param exportType format to export notes in.
* @returns
*/
export: (
title: string,
content: string,
exportType: NoteExportType,
) => Promise<void>;
/**
* @deprecated Use `importFile` instead
* Shows a dialog to open an HTML file.
* @returns the contents to import later.
*/
importHTML: () => Promise<string>;
/** Imports a file and returns its contents along with its content type. */
importFile: ()=> Promise<FileImportResult | null>;
};
settings: {
load: () => Promise<UserSettings | null>;
save: (data: UserSettings) => Promise<void>;
};
exporter: {
init: () => Promise<void>;
push: (filename: string, content: string) => Promise<void>;
finish: () => Promise<void>;
};
backup: {
backupUserData: () => Promise<void>;
openBackup: () => Promise<string | null>;
restore: (archivePath: string) => Promise<void>;
};
theme: {
/** Prompts the user to choose a theme file and imports it if the theme is valid. */
import: () => Promise<void>;
/** Reads the contents of themes folder and loads all of them.
* @returns all themes installed in current profile.
*/
load: () => Promise<Theme[]>;
/** @returns the system accent color on Windows and macOS. If invoked on Linux, returns #000000
* @platform win32,darwin
*/
getAccentColor: () => Promise<string>;
};
embed: {
create: (filePath: string) => Promise<Embed>;
createFromBuffer: (buffer: ArrayBuffer, fileExt: string) => Promise<Embed>;
resolve: (id: string) => Promise<ResolvedEmbed>;
};
getClientInfo: () => Promise<DarkwriteDesktopClientInfo>;
setTitlebarSymbolColor: (
symbolColor: string,
themeMode: "light" | "dark" | "system" = "system",
) => Promise<void>;
}
164 changes: 122 additions & 42 deletions packages/app-desktop/src/electron/preload/preload.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,153 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Note, NoteExportType, NotePartial } from "@darkwrite/common";
import {
Embed,
FileImportResult,
Note,
NoteExportType,
NotePartial,
ResolvedEmbed,
Theme,
} from "@darkwrite/common";
import { UserSettings } from "@darkwrite/common";
import { contextBridge, ipcRenderer } from "electron";
import { ChannelNames } from "../channels";
import { DarkwriteElectronAPI } from "../../api";
import { webUtils } from "electron";

const darkwriteAPI: DarkwriteElectronAPI = {
showAppMenu: () => ipcRenderer.invoke("show-app-menu"),
/**
* Wraps around ipcRenderer.invoke() to type APIs
* @template T Expected return type of invocation. This method's return type will be a Promise of that type.
* @param channel IPC channel which will be handled
* @param args Every other parameter which will be passed into ipcRenderer.invoke()
* @returns
*/
const invoke = <T = void>(channel: ChannelNames, ...args): Promise<T> =>
<Promise<T>>ipcRenderer.invoke(channel, ...args);

export const darkwriteAPI = {
/**
* Displays the app menu
*/
showAppMenu: () => invoke(ChannelNames.SHOW_APP_MENU),
note: {
/** Creates a new note.
* @returns the Note object if successful, null if unsuccessful
*/
create: (title: string, parent?: string) =>
ipcRenderer.invoke(ChannelNames.CREATE_NOTE, title, parent),
invoke<Note | null>(ChannelNames.CREATE_NOTE, title, parent),
/**
* Updates the stored JSON contents of the note.
* @param id ID of the note
* @param content stringified JSON to overwrite with
* @returns
*/
setContents: (id: string, content: string) =>
ipcRenderer.invoke(ChannelNames.SET_JSON_CONTENT, id, content),
invoke(ChannelNames.SET_JSON_CONTENT, id, content),
/**
* Reads the stored JSON contents of the note.
* @param id - ID of the note
* @returns JSON content in string form
*/
getContents: (id: string) =>
ipcRenderer.invoke(ChannelNames.GET_JSON_CONTENT, id),
delete: (id: string) => ipcRenderer.invoke(ChannelNames.DELETE_NOTE, id),
invoke<string>(ChannelNames.GET_JSON_CONTENT, id),
/**
* Deletes a note **permanently**.
* @param id ID of the note to delete
*/
delete: (id: string) => invoke(ChannelNames.DELETE_NOTE, id),
/**
* Moves a note below another note, or to the top level
* @param sourceID - ID of the note to move
* @param destID - ID of the note to move into, or `undefined` if the top level is desired
* @returns
*/
move: (sourceID: string, destID: string | undefined) =>
ipcRenderer.invoke(ChannelNames.MOVE_NOTE, sourceID, destID),
update: (note: NotePartial) =>
ipcRenderer.invoke(ChannelNames.UPDATE_NOTE, note),
getAll: () => ipcRenderer.invoke(ChannelNames.GET_ALL_NOTES),
invoke(ChannelNames.MOVE_NOTE, sourceID, destID),
/**
* Performs a database update for the given note. All fields are updated with the passed parameter.
* @param note The partial note object to update with. An ID must be provided.
*/
update: (note: NotePartial) => invoke(ChannelNames.UPDATE_NOTE, note),
/**
* Gets all notes from the database.
* @returns an array of notes.
*/
getAll: () => invoke<Note[]>(ChannelNames.GET_ALL_NOTES),
/**
* Moves a note in or out of trash.
* @param id - ID of the note
* @param state - true moves to trash, false moves out of trash
*/
setTrashStatus: (id: string, state: boolean) =>
ipcRenderer.invoke(ChannelNames.SET_TRASH_STATUS, id, state),
getNote: (id: string) => ipcRenderer.invoke(ChannelNames.GET_NOTE, id),
saveAll: (notes: Note[]) =>
ipcRenderer.invoke(ChannelNames.SAVE_NOTES, notes),
invoke(ChannelNames.SET_TRASH_STATUS, id, state),
/**
* Finds a single note
* @param id - ID of the note to look for
* @returns the note if it exists, null if it doesn't exist or something goes wrong
*/
getNote: (id: string) => invoke<Note | null>(ChannelNames.GET_NOTE, id),
/**
* Updates multiple notes at once
* @param notes array of notes to update
*/
saveAll: (notes: Note[]) => invoke(ChannelNames.SAVE_NOTES, notes),
/**
* Shows a save file dialog to export a note
* @param title note title to use as file name
* @param content contents of the note
* @param exportType format to export notes in.
* @returns
*/
export: (title: string, content: string, exportType: NoteExportType) =>
ipcRenderer.invoke(ChannelNames.EXPORT_NOTE, title, content, exportType),
importHTML: () => ipcRenderer.invoke(ChannelNames.IMPORT_HTML),
importFile: ()=>ipcRenderer.invoke(ChannelNames.IMPORT_NOTE)
invoke(ChannelNames.EXPORT_NOTE, title, content, exportType),
/**
* @deprecated Use `importFile` instead
* Shows a dialog to open an HTML file.
* @returns the contents to import later.
*/
importHTML: () => invoke<string>(ChannelNames.IMPORT_HTML),
/** Imports a file and returns its contents along with its content type. */
importFile: () => invoke<FileImportResult | null>(ChannelNames.IMPORT_NOTE),
},
settings: {
load: () => ipcRenderer.invoke(ChannelNames.LOAD_USER_PREFS),
save: (data: UserSettings) =>
ipcRenderer.invoke(ChannelNames.SAVE_USER_PREFS, data),
load: () => invoke<UserSettings | null>(ChannelNames.LOAD_USER_PREFS),
save: (data: UserSettings) => invoke(ChannelNames.SAVE_USER_PREFS, data),
},
exporter: {
init: () => ipcRenderer.invoke(ChannelNames.INIT_EXPORT_CACHE),
init: () => invoke(ChannelNames.INIT_EXPORT_CACHE),
push: (filename: string, content: string) =>
ipcRenderer.invoke(ChannelNames.PUSH_EXPORT_DOCUMENT, filename, content),
finish: () => ipcRenderer.invoke(ChannelNames.FINISH_EXPORT),
invoke(ChannelNames.PUSH_EXPORT_DOCUMENT, filename, content),
finish: () => invoke(ChannelNames.FINISH_EXPORT),
},
getClientInfo: () => ipcRenderer.invoke(ChannelNames.GET_APP_INFO),
backup: {
backupUserData: () => ipcRenderer.invoke(ChannelNames.BACKUP_USER_DATA),
openBackup: () => ipcRenderer.invoke(ChannelNames.OPEN_BACKUP),
restore: (path: string) =>
ipcRenderer.invoke(ChannelNames.RESTORE_BACKUP, path),
backupUserData: () => invoke(ChannelNames.BACKUP_USER_DATA),
openBackup: () => invoke<string | null>(ChannelNames.OPEN_BACKUP),
restore: (path: string) => invoke(ChannelNames.RESTORE_BACKUP, path),
},
theme: {
import: () => ipcRenderer.invoke(ChannelNames.IMPORT_THEME),
load: () => ipcRenderer.invoke(ChannelNames.LOAD_THEMES),
getAccentColor: ()=>ipcRenderer.invoke(ChannelNames.GET_ACCENT_COLOR)
/** Prompts the user to choose a theme file and imports it if the theme is valid. */

import: () => invoke(ChannelNames.IMPORT_THEME),
/** Reads the contents of themes folder and loads all of them.
* @returns all themes installed in current profile.
*/
load: () => invoke<Theme[]>(ChannelNames.LOAD_THEMES),
/** @returns the system accent color on Windows and macOS. If invoked on Linux, returns #000000
* @platform win32,darwin
*/
getAccentColor: () => invoke<string>(ChannelNames.GET_ACCENT_COLOR),
},
setTitlebarSymbolColor: (
symbolColor: string,
themeMode: "light" | "dark" | "system" = "system",
) =>
ipcRenderer.invoke(
ChannelNames.SET_TITLEBAR_SYMBOL_COLOR,
symbolColor,
themeMode,
),
) => invoke(ChannelNames.SET_TITLEBAR_SYMBOL_COLOR, symbolColor, themeMode),
embed: {
create: (path) => ipcRenderer.invoke(ChannelNames.UPLOAD_EMBED, path),
createFromBuffer: (buf, ext) =>
ipcRenderer.invoke(ChannelNames.UPLOAD_EMBED_WITH_BUFFER, buf, ext),
resolve: (id) => ipcRenderer.invoke(ChannelNames.RESOLVE_EMBED, id),
create: (filePath: string) =>
invoke<Embed>(ChannelNames.UPLOAD_EMBED, filePath),
createFromBuffer: (buf: ArrayBuffer, fileExt: string) =>
invoke<Embed>(ChannelNames.UPLOAD_EMBED_WITH_BUFFER, buf, fileExt),
resolve: (id: string) =>
invoke<ResolvedEmbed>(ChannelNames.RESOLVE_EMBED, id),
},
};

Expand Down
2 changes: 2 additions & 0 deletions packages/app-desktop/src/electron/preload/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { darkwriteAPI } from "./preload"
export type DarkrwiteElectronAPI = typeof darkwriteAPI;
5 changes: 3 additions & 2 deletions packages/app-desktop/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/// <reference types="vite/client" />
/// <reference types="vitest/globals" />
/// <reference types="./electron/preload/types.d.ts"/>

import { DarkwriteElectronAPI } from "./api";
import { type DarkrwiteElectronAPI as DarkwriteAPI } from "./electron/preload/types";
import { type WebUtils } from "electron";

/**
Expand All @@ -20,7 +21,7 @@ declare global {
| { value?: undefined; error: E };

interface Window {
api: DarkwriteElectronAPI;
api: DarkwriteAPI;
webUtils: WebUtils;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/app-desktop/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"src/**/*.tsx",
"src/**/*.d.ts",
"src/**/*.ts",
"src/api.d.ts",
"src/global.d.ts"
"src/global.d.ts",
"src/electron/preload/types.d.ts"
],
"exclude": ["src/electron/**/*"],
"exclude": ["src/electron/**/*", "!src/electron/preload/*"],
"compilerOptions": {
"composite": true,
"jsx": "react-jsx",
Expand Down
2 changes: 1 addition & 1 deletion packages/app-desktop/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
"include": ["src/electron/**/*", "src/electron/**/*.d.ts", "src/api.d.ts"],
"include": ["src/electron/**/*", "src/electron/**/*.d.ts"],
"compilerOptions": {
"types": ["vitest/globals"],
"target": "ESNext",
Expand Down

0 comments on commit 497a4bc

Please sign in to comment.