Skip to content

Commit

Permalink
Merge pull request #2 from pioneers/develop/ui
Browse files Browse the repository at this point in the history
Develop/UI
  • Loading branch information
snowNnik authored Jul 14, 2024
2 parents 494b650 + 07920ae commit 1d9d569
Show file tree
Hide file tree
Showing 27 changed files with 1,354 additions and 65 deletions.
347 changes: 326 additions & 21 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"version": "4.0.0",
"description": "A foundation for scalable desktop apps",
"keywords": [
"electron",
Expand Down Expand Up @@ -78,7 +79,8 @@
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
"\\.(css|less|sass|scss)$": "identity-obj-proxy",
"ace-builds": "<rootDir>/node_modules/ace-builds"
},
"setupFiles": [
"./.erb/scripts/check-build-exists.ts"
Expand All @@ -96,11 +98,15 @@
}
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.6",
"ace-builds": "^1.35.1",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
"electron-updater": "^6.1.4",
"react": "^18.2.0",
"react-ace": "^12.0.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.16.0"
},
"devDependencies": {
Expand Down Expand Up @@ -149,6 +155,7 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jsdoc": "^4.0.3",
"mini-css-extract-plugin": "^2.7.6",
"prettier": "^3.0.3",
"react-refresh": "^0.14.0",
Expand Down
10 changes: 10 additions & 0 deletions src/common/AppConsoleMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default class AppConsoleMessage {
#type: string;

#text: string;

constructor(type: string, text: string) {
this.#type = type;
this.#text = text;
}
}
164 changes: 164 additions & 0 deletions src/main/MainApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { dialog, ipcMain } from 'electron';
import type { BrowserWindow } from 'electron';
import fs from 'fs';
import { version as dawnVersion } from '../../package.json';
import AppConsoleMessage from '../common/AppConsoleMessage';

const WATCH_DEBOUNCE_MS = 3000;
const CODE_FILE_FILTERS = [
{ name: 'Python Files', extensions: ['py'] },
{ name: 'All Files', extensions: ['*'] },
];

export default class MainApp {
readonly #mainWindow: BrowserWindow;

#savePath: string | null;

#watcher: fs.FSWatcher | null;

#watchDebounce: boolean;

#preventQuit: boolean;

constructor(mainWindow: BrowserWindow) {
this.#mainWindow = mainWindow;
this.#savePath = null;
this.#watcher = null;
this.#watchDebounce = true;
this.#preventQuit = true;
mainWindow.on('close', (e) => {
if (this.#preventQuit) {
e.preventDefault();
this.#sendToRenderer('renderer-quit-request');
}
});
ipcMain.on('main-file-control', (_event, data) => {
if (data.type === 'save') {
this.#saveCodeFile(data.content as string, data.forceDialog as boolean);
} else if (data.type === 'load') {
this.#openCodeFile();
} else {
// eslint-disable-next-line no-console
console.error(`Unknown data.type for main-file-control ${data.type}`);
}
});
ipcMain.on('main-quit', () => {
this.#preventQuit = false;
this.#mainWindow.close();
});
}

#sendToRenderer(...args) {
this.#mainWindow.webContents.send(...args);
}

onPresent() {
this.#watcher?.close();
this.#savePath = null;
this.#sendToRenderer('renderer-init', { dawnVersion });
}

promptSaveCodeFile(forceDialog: boolean) {
this.#sendToRenderer('renderer-file-control', {
type: 'promptSave',
forceDialog,
});
}

promptLoadCodeFile() {
this.#sendToRenderer('renderer-file-control', { type: 'promptLoad' });
}

#saveCodeFile(code: string, forceDialog: boolean) {
let success = true;
if (this.#savePath === null || forceDialog) {
success = this.#showCodePathDialog('save');
}
if (success) {
// Temporarily disable watcher
this.#watcher?.close();
this.#watcher = null;
fs.writeFile(
this.#savePath,
code,
{ encoding: 'utf8', flag: 'w' },
(err) => {
if (err) {
this.#sendToRenderer(
'renderer-post-console',
new AppConsoleMessage(
'dawn-err',
`Failed to save code to ${this.#savePath}. ${err}`,
),
);
} else {
this.#sendToRenderer('renderer-file-control', { type: 'didSave' });
}
this.#watchCodeFile();
},
);
}
}

#openCodeFile() {
const success = this.#showCodePathDialog('load');
if (success) {
this.#sendToRenderer('renderer-file-control', {
type: 'didOpen',
content: fs.readFileSync(this.#savePath, {
encoding: 'utf8',
flag: 'r',
}),
});
}
}

#showCodePathDialog(mode: 'save' | 'load') {
let result: string | string[] | undefined;
if (mode === 'save') {
result = dialog.showSaveDialogSync(this.#mainWindow, {
filters: CODE_FILE_FILTERS,
...(this.#savePath === null ? {} : { defaultPath: this.#savePath }),
});
} else {
result = dialog.showOpenDialogSync(this.#mainWindow, {
filters: CODE_FILE_FILTERS,
properties: ['openFile'],
});
}
if (result && result.length) {
this.#savePath = typeof result === 'string' ? result : result[0];
this.#sendToRenderer('renderer-file-control', {
type: 'didChangePath',
path: this.#savePath,
});
if (mode === 'load') {
this.#watchCodeFile();
}
return true;
}
return false;
}

#watchCodeFile() {
this.#watcher?.close();
this.#watchDebounce = true;
this.#watcher = fs.watch(
this.#savePath,
{ persistent: false, encoding: 'utf8' },
() => {
// Don't care what the event type is
if (this.#watchDebounce) {
this.#watchDebounce = false;
setTimeout(() => {
this.#watchDebounce = true;
}, WATCH_DEBOUNCE_MS);
this.#sendToRenderer('renderer-file-control', {
type: 'didExternalChange',
});
}
},
);
}
}
14 changes: 5 additions & 9 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
* `./src/main.js` using webpack. This gives us some performance wins.
*/
import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import { app, BrowserWindow, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import MainApp from './MainApp';

class AppUpdater {
constructor() {
Expand All @@ -25,12 +26,6 @@ class AppUpdater {

let mainWindow: BrowserWindow | null = null;

ipcMain.on('ipc-example', async (event, arg) => {
const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
console.log(msgTemplate(arg));
event.reply('ipc-example', msgTemplate('pong'));
});

if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
Expand Down Expand Up @@ -80,13 +75,14 @@ const createWindow = async () => {
: path.join(__dirname, '../../.erb/dll/preload.js'),
},
});
const mainApp = new MainApp(mainWindow);

mainWindow.loadURL(resolveHtmlPath('index.html'));

mainWindow.on('ready-to-show', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
mainApp.onPresent();
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
Expand All @@ -98,7 +94,7 @@ const createWindow = async () => {
mainWindow = null;
});

const menuBuilder = new MenuBuilder(mainWindow);
const menuBuilder = new MenuBuilder(mainApp, mainWindow);
menuBuilder.buildMenu();

// Open urls in the user's browser
Expand Down
28 changes: 21 additions & 7 deletions src/main/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ import {
BrowserWindow,
MenuItemConstructorOptions,
} from 'electron';
import type MainApp from './MainApp';

interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
}

export default class MenuBuilder {
mainApp: MainApp;

mainWindow: BrowserWindow;

constructor(mainWindow: BrowserWindow) {
constructor(mainApp: MainApp, mainWindow: BrowserWindow) {
this.mainApp = mainApp;
this.mainWindow = mainWindow;
}

Expand Down Expand Up @@ -200,12 +204,22 @@ export default class MenuBuilder {
{
label: '&Open',
accelerator: 'Ctrl+O',
click: () => {
this.mainApp.promptLoadCodeFile();
},
},
{
label: '&Close',
accelerator: 'Ctrl+W',
label: '&Save',
accelerator: 'Ctrl+S',
click: () => {
this.mainWindow.close();
this.mainApp.promptSaveCodeFile(false);
},
},
{
label: 'Save &As',
accelerator: 'Ctrl+Shift+S',
click: () => {
this.mainApp.promptSaveCodeFile(true);
},
},
],
Expand Down Expand Up @@ -234,7 +248,7 @@ export default class MenuBuilder {
},
{
label: 'Toggle &Developer Tools',
accelerator: 'Alt+Ctrl+I',
accelerator: 'Ctrl+Shift+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
Expand All @@ -252,7 +266,7 @@ export default class MenuBuilder {
},
],
},
{
/* {
label: 'Help',
submenu: [
{
Expand Down Expand Up @@ -282,7 +296,7 @@ export default class MenuBuilder {
},
},
],
},
}, */
];

return templateDefault;
Expand Down
9 changes: 8 additions & 1 deletion src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
/* eslint no-unused-vars: off */
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';

export type Channels = 'ipc-example';
export type Channels =
| 'renderer-init'
| 'renderer-robot-update'
| 'renderer-post-console'
| 'renderer-file-control'
| 'renderer-quit-request'
| 'main-file-control'
| 'main-quit';

const electronHandler = {
ipcRenderer: {
Expand Down
31 changes: 27 additions & 4 deletions src/renderer/App.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
/*
* @NOTE: Prepend a `~` to css file paths that are in your node_modules
* See https://github.com/webpack-contrib/sass-loader#imports
*/
body {
margin: 0;
}

.App {
display: flex;
flex-direction: column;
height: 100vh;
}
.App-cols {
display: flex;
flex-direction: row;
flex-shrink: 0;
}
.App-modal-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgb(0 0 0 / 50%);
display: none;
z-index: 4;
}
.App-modal-container:has(.modal-active) {
display: block;
}
Loading

0 comments on commit 1d9d569

Please sign in to comment.