Skip to content

Commit

Permalink
Merge pull request #2 from dolthub/liuliu/app-menu
Browse files Browse the repository at this point in the history
liuliu/set up app menu
  • Loading branch information
liuliu-dev authored Sep 19, 2024
2 parents f88fd2f + 86d6ad8 commit d4aa9b1
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 41 deletions.
21 changes: 17 additions & 4 deletions main/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import {
app,
BrowserWindow,
ipcMain,
Menu,
utilityProcess,
UtilityProcess,
} from "electron";
import serve from "electron-serve";
import { createWindow } from "./helpers";
import { initMenu } from "./helpers/menu";

const isProd = process.env.NODE_ENV === "production";
const userDataPath = app.getPath("userData");
const schemaPath = isProd?path.join(userDataPath, "schema.gql"):"graphql-server/schema.gql";
const schemaPath = isProd
? path.join(userDataPath, "schema.gql")
: "graphql-server/schema.gql";
process.env.SCHEMA_PATH = schemaPath;
process.env.NEXT_PUBLIC_FOR_ELECTRON = "true";

Expand Down Expand Up @@ -60,23 +64,32 @@ app.on("ready", () => {
preload: path.join(__dirname, "preload.js"),
},
});
Menu.setApplicationMenu(initMenu(mainWindow, isProd));
createGraphqlSeverProcess();

if (isProd) {
setTimeout(async () => {
await mainWindow.loadURL("app://./");
mainWindow.webContents.openDevTools();

}, 3000);
} else {
setTimeout(async () => {
const port = process.argv[2];
await mainWindow.loadURL(`http://localhost:${port}`);
mainWindow.webContents.openDevTools();
}, 2500);
}
});

function updateMenu(databaseName?: string) {
const hasChosenDatabase = !!databaseName;
const menu = initMenu(mainWindow, isProd, hasChosenDatabase);

Menu.setApplicationMenu(menu);
}

ipcMain.on("update-menu", (_event, databaseName?: string) => {
updateMenu(databaseName);
});

app.on("before-quit", () => {
if (serverProcess) {
serverProcess.kill();
Expand Down
66 changes: 33 additions & 33 deletions main/helpers/create-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,72 @@ import {
BrowserWindow,
BrowserWindowConstructorOptions,
Rectangle,
} from 'electron'
import Store from 'electron-store'
} from "electron";
import Store from "electron-store";

export const createWindow = (
windowName: string,
options: BrowserWindowConstructorOptions
options: BrowserWindowConstructorOptions,
): BrowserWindow => {
const key = 'window-state'
const name = `window-state-${windowName}`
const store = new Store<Rectangle>({ name })
const key = "window-state";
const name = `window-state-${windowName}`;
const store = new Store<Rectangle>({ name });
const defaultSize = {
width: options.width,
height: options.height,
}
let state = {}
};
let state = {};

const restore = () => store.get(key, defaultSize)
const restore = () => store.get(key, defaultSize);

const getCurrentPosition = () => {
const position = win.getPosition()
const size = win.getSize()
const position = win.getPosition();
const size = win.getSize();
return {
x: position[0],
y: position[1],
width: size[0],
height: size[1],
}
}
};
};

const windowWithinBounds = (windowState, bounds) => {
return (
windowState.x >= bounds.x &&
windowState.y >= bounds.y &&
windowState.x + windowState.width <= bounds.x + bounds.width &&
windowState.y + windowState.height <= bounds.y + bounds.height
)
}
);
};

const resetToDefaults = () => {
const bounds = screen.getPrimaryDisplay().bounds
const bounds = screen.getPrimaryDisplay().bounds;
return Object.assign({}, defaultSize, {
x: (bounds.width - defaultSize.width) / 2,
y: (bounds.height - defaultSize.height) / 2,
})
}
});
};

const ensureVisibleOnSomeDisplay = (windowState) => {
const visible = screen.getAllDisplays().some((display) => {
return windowWithinBounds(windowState, display.bounds)
})
const ensureVisibleOnSomeDisplay = windowState => {
const visible = screen.getAllDisplays().some(display => {
return windowWithinBounds(windowState, display.bounds);
});
if (!visible) {
// Window is partially or fully not visible now.
// Reset it to safe defaults.
return resetToDefaults()
return resetToDefaults();
}
return windowState
}
return windowState;
};

const saveState = () => {
if (!win.isMinimized() && !win.isMaximized()) {
Object.assign(state, getCurrentPosition())
Object.assign(state, getCurrentPosition());
}
store.set(key, state)
}
store.set(key, state);
};

state = ensureVisibleOnSomeDisplay(restore())
state = ensureVisibleOnSomeDisplay(restore());

const win = new BrowserWindow({
...state,
Expand All @@ -78,9 +78,9 @@ export const createWindow = (
contextIsolation: true,
...options.webPreferences,
},
})
});

win.on('close', saveState)
win.on("close", saveState);

return win
}
return win;
};
2 changes: 1 addition & 1 deletion main/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './create-window'
export * from "./create-window";
165 changes: 165 additions & 0 deletions main/helpers/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {
shell,
BrowserWindow,
Menu,
MenuItemConstructorOptions,
} from "electron";

export function initMenu(
win: BrowserWindow,
isProd: boolean,
hasChosenDatabase?: boolean,
): Menu {
var applicationMenu: MenuItemConstructorOptions[] = [
{
label: "Edit",
submenu: [
{
type: "separator",
},
{
role: "cut",
},
{
role: "copy",
},
{
role: "paste",
},
{
role: "pasteAndMatchStyle",
},
{
role: "delete",
},
{
role: "selectAll",
},
],
},
{
label: "View",
submenu: [
isProd
? { label: "hidden", visible: false }
: {
label: "Reload",
click(_item, focusedWindow) {
if (focusedWindow) focusedWindow.reload();
},
},
isProd
? { label: "hidden", visible: false }
: {
type: "separator",
},
{
role: "resetZoom",
},
{
role: "zoomIn",
},
{
role: "zoomOut",
},
{
type: "separator",
},
{
role: "togglefullscreen",
},
],
},
{
label: "Tools",
submenu: [
{
label: "Toggle Developer Tools",
accelerator:
process.platform === "darwin" ? "Alt+Command+I" : "Ctrl+Shift+I",
click(_item, focusedWindow) {
if (focusedWindow) focusedWindow.webContents.toggleDevTools();
},
},
{
type: "separator",
},
{
label: "Import File",
accelerator: "CmdOrCtrl+I",
click: () => win.webContents.send("menu-clicked", "upload-file"),
enabled: hasChosenDatabase,
},
{
type: "separator",
},
{
label: "Commit Graph",
accelerator: "CmdOrCtrl+G",
click: () => win.webContents.send("menu-clicked", "commit-graph"),
enabled: hasChosenDatabase,
},
{
type: "separator",
},
{
label: "Schema Diagram",
accelerator: "CmdOrCtrl+D",
click: () => win.webContents.send("menu-clicked", "schema-diagram"),
enabled: hasChosenDatabase,
},
{
type: "separator",
},
{
label: "New...",
submenu: [
{
label: "Table",
accelerator: "CmdOrCtrl+T",
click: () => win.webContents.send("menu-clicked", "new-table"),
enabled: hasChosenDatabase,
},
{
type: "separator",
},
{
label: "Branch",
accelerator: "CmdOrCtrl+B",
click: () => win.webContents.send("menu-clicked", "new-branch"),
},
{
type: "separator",
},
{
label: "Release",
accelerator: "CmdOrCtrl+R",
click: () => win.webContents.send("menu-clicked", "new-release"),
},
],
enabled: hasChosenDatabase,
},
],
},
{
role: "window",
submenu: [
{
role: "minimize",
},
],
},
{
role: "help",
submenu: [
{
label: "Learn More",
click() {
shell.openExternal("https://docs.dolthub.com/");
},
},
],
},
];
return Menu.buildFromTemplate(applicationMenu);
}
6 changes: 3 additions & 3 deletions main/preload.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare const handler: {
send(channel: string, value: unknown): void;
on(channel: string, callback: (...args: unknown[]) => void): () => void;
invoke(channel: string, ...args: unknown[]): Promise<any>;
send(channel: string, value: unknown): void;
on(channel: string, callback: (...args: unknown[]) => void): () => void;
invoke(channel: string, ...args: unknown[]): Promise<any>;
};
export type IpcHandler = typeof handler;
export {};
5 changes: 5 additions & 0 deletions main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ const handler = {
invoke(channel: string, ...args: unknown[]) {
return ipcRenderer.invoke(channel, ...args);
},
onMenuClicked: (callback: (value: string) => {}) =>
ipcRenderer.on("menu-clicked", (_event, value) => callback(value)),
updateAppMenu: (databaseName?: string) => {
ipcRenderer.send("update-menu", databaseName);
},
};

contextBridge.exposeInMainWorld("ipc", handler);
Expand Down
3 changes: 3 additions & 0 deletions web/components/pageComponents/DatabasePage/component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DatabaseLayout from "@components/layouts/DatabaseLayout";
import { SqlEditorProvider } from "@contexts/sqleditor";
import useElectronMenu from "@hooks/useElectronMenu";
import { DatabasePageParams } from "@lib/params";
import { RefUrl } from "@lib/urls";
import { ReactNode } from "react";
Expand All @@ -21,6 +22,8 @@ type Props = {
};

export default function DatabasePage({ params, children, ...props }: Props) {
useElectronMenu(params);

return (
<SqlEditorProvider params={params}>
<DatabaseLayout
Expand Down
Loading

0 comments on commit d4aa9b1

Please sign in to comment.