Skip to content

Commit 4fc0d31

Browse files
authored
Added initial support for GitHub integration.
2 parents 6eb3e15 + 4618c68 commit 4fc0d31

31 files changed

+1557
-399
lines changed

.github/workflows/cd.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ name: CD
33
on:
44
workflow_dispatch:
55

6+
env:
7+
VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }}
8+
VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }}
9+
VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }}
10+
VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }}
11+
VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }}
12+
VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }}
13+
VITE_FIREBASE_MEASUREMENT_ID: ${{ secrets.VITE_FIREBASE_MEASUREMENT_ID }}
14+
615
jobs:
716
web_app:
817
if: ${{ github.repository == 'opencor/webapp' }}

.github/workflows/cddev.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ on:
55
branches: [mvp]
66
workflow_dispatch:
77

8+
env:
9+
VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }}
10+
VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }}
11+
VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }}
12+
VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }}
13+
VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }}
14+
VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }}
15+
VITE_FIREBASE_MEASUREMENT_ID: ${{ secrets.VITE_FIREBASE_MEASUREMENT_ID }}
16+
817
jobs:
918
web_app:
1019
name: Web app

.github/workflows/ci.yml

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ on:
55
branches: [mvp]
66
workflow_dispatch:
77

8+
env:
9+
VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }}
10+
VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }}
11+
VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }}
12+
VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }}
13+
VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }}
14+
VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }}
15+
VITE_FIREBASE_MEASUREMENT_ID: ${{ secrets.VITE_FIREBASE_MEASUREMENT_ID }}
16+
817
jobs:
918
ci:
1019
name: ${{ matrix.name }}
@@ -25,7 +34,9 @@ jobs:
2534
os: macos-15-intel
2635
- name: macOS (ARM)
2736
os: macos-15
28-
- name: Code formatting and linting
37+
- name: Code formatting
38+
os: ubuntu-22.04
39+
- name: Linting
2940
os: ubuntu-22.04
3041
steps:
3142
- name: Set the timezone to New Zealand
@@ -52,32 +63,32 @@ jobs:
5263
unzip -j bun.zip -d $env:USERPROFILE\.bun
5364
del bun.zip
5465
echo "$env:USERPROFILE\.bun" >> $env:GITHUB_PATH
55-
- name: Install Biome
56-
if: ${{ matrix.name == 'Code formatting and linting' }}
57-
uses: biomejs/setup-biome@v2
5866
- name: OpenCOR dependencies
5967
if: ${{ matrix.name != 'Windows (ARM)' }}
6068
run: bun install
6169
- name: OpenCOR dependencies (Windows ARM only)
6270
if: ${{ matrix.name == 'Windows (ARM)' }}
6371
run: bun install --cpu=arm64
6472
- name: Build libOpenCOR
65-
if: ${{ matrix.name != 'Code formatting and linting' }}
73+
if: ${{ matrix.name != 'Code formatting' && matrix.name != 'Linting' }}
6674
run: bun libopencor
6775
- name: Build OpenCOR
68-
if: ${{ matrix.name != 'Code formatting and linting' }}
76+
if: ${{ matrix.name != 'Code formatting' && matrix.name != 'Linting' }}
6977
run: bun run build
7078
- name: Build OpenCOR's Web app
71-
if: ${{ matrix.name != 'Code formatting and linting' }}
79+
if: ${{ matrix.name != 'Code formatting' && matrix.name != 'Linting' }}
7280
run: bun build:web
7381
- name: Build OpenCOR's npm library
74-
if: ${{ matrix.name != 'Code formatting and linting' }}
82+
if: ${{ matrix.name != 'Code formatting' && matrix.name != 'Linting' }}
7583
run: |
7684
cd src/renderer
7785
bun install
7886
bun build:lib
79-
- name: Code formatting and linting
80-
if: ${{ matrix.name == 'Code formatting and linting' }}
81-
run: biome ci --error-on-warnings
87+
- name: Code formatting
88+
if: ${{ matrix.name == 'Code formatting' }}
89+
run: bun format
90+
- name: Linting
91+
if: ${{ matrix.name == 'Linting' }}
92+
run: bun lint
8293
- name: Clean OpenCOR
8394
run: bun clean

bun.lock

Lines changed: 348 additions & 154 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

electron.vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default electronVite.defineConfig({
2424
build: {
2525
target: 'esnext'
2626
},
27+
envDir: path.join(import.meta.dirname, 'src/renderer'),
2728
optimizeDeps: {
2829
esbuildOptions: {
2930
target: 'esnext'

package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"url": "git+https://github.com/opencor/webapp.git"
2424
},
2525
"type": "module",
26-
"version": "0.20251112.0",
26+
"version": "0.20251118.0",
2727
"scripts": {
2828
"archive:web": "bun src/renderer/scripts/archive.web.js",
2929
"build": "electron-vite build",
@@ -44,14 +44,18 @@
4444
},
4545
"peerDependencies": {
4646
"mathjs": "^14.9.1",
47-
"plotly.js-gl2d-dist-min": "^3.1.1",
47+
"plotly.js-gl2d-dist-min": "^3.2.0",
4848
"vue": "3.4.21"
4949
},
5050
"dependencies": {
51+
"@napi-rs/keyring": "^1.2.0",
5152
"@primeuix/themes": "^1.2.5",
5253
"@primevue/auto-import-resolver": "4.2.5",
5354
"@vueuse/core": "12.8.2",
55+
"firebase": "^12.6.0",
56+
"firebaseui": "^6.1.0",
5457
"jsonschema": "^1.5.0",
58+
"octokit": "^5.0.5",
5559
"primeicons": "^7.0.0",
5660
"primevue": "4.2.5",
5761
"quill": "^2.0.3",
@@ -60,19 +64,19 @@
6064
"vue": "3.4.21"
6165
},
6266
"devDependencies": {
63-
"@biomejs/biome": "^2.3.4",
67+
"@biomejs/biome": "^2.3.6",
6468
"@electron-toolkit/tsconfig": "^2.0.0",
6569
"@electron-toolkit/utils": "^4.0.0",
6670
"@tailwindcss/postcss": "^4.1.17",
6771
"@tailwindcss/vite": "^4.1.17",
68-
"@types/node": "^24.10.0",
72+
"@types/node": "^24.10.1",
6973
"@types/plotly.js": "3.0.3",
7074
"@vitejs/plugin-vue": "^6.0.1",
7175
"@vue/tsconfig": "^0.8.1",
7276
"@wasm-fmt/clang-format": "^21.1.5",
7377
"autoprefixer": "^10.4.22",
74-
"cmake-js": "^7.3.1",
75-
"electron": "^38.6.0",
78+
"cmake-js": "^7.4.0",
79+
"electron": "^38.7.0",
7680
"electron-builder": "^26.0.12",
7781
"electron-conf": "^1.3.0",
7882
"electron-updater": "^6.6.2",

src/main/MainMenu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function enableDisableMainMenu(enable: boolean): void {
3232

3333
if (isPackaged()) {
3434
checkForUpdatesMenuItem = {
35-
label: 'Check For Updates...',
35+
label: 'Check for Updates...',
3636
click: () => {
3737
mainWindow?.webContents.send('check-for-updates');
3838
}

src/main/MainWindow.ts

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import electron from 'electron';
22
import { autoUpdater, type ProgressInfo, type UpdateCheckResult } from 'electron-updater';
33
import path from 'node:path';
4-
import process from 'node:process';
54

6-
import type { ISettings } from '../renderer/src/common/common';
5+
import { isHttpUrl, type ISettings } from '../renderer/src/common/common';
76
import { FULL_URI_SCHEME, LONG_DELAY, SHORT_DELAY } from '../renderer/src/common/constants';
87
import { isLinux, isMacOs, isPackaged, isWindows } from '../renderer/src/common/electron';
8+
import { deleteGitHubAccessToken } from '../renderer/src/common/gitHubIntegration';
99

1010
import icon from './assets/icon.png?asset';
1111
import { ApplicationWindow } from './ApplicationWindow';
@@ -75,6 +75,8 @@ let _resetAll = false;
7575
export function resetAll(): void {
7676
_resetAll = true;
7777

78+
deleteGitHubAccessToken();
79+
7880
electron.app.relaunch();
7981
electron.app.quit();
8082
}
@@ -140,7 +142,7 @@ export class MainWindow extends ApplicationWindow {
140142

141143
// Constructor.
142144

143-
constructor(commandLine: string[], splashScreenWindow: SplashScreenWindow) {
145+
constructor(commandLine: string[], splashScreenWindow: SplashScreenWindow, rendererUrl: string) {
144146
// Initialise ourselves.
145147

146148
const state: IElectronConfState = electronConf.get('app.state');
@@ -212,7 +214,7 @@ export class MainWindow extends ApplicationWindow {
212214

213215
commandLine.shift();
214216

215-
if (!isPackaged()) {
217+
if (!isPackaged() && commandLine.length > 0) {
216218
commandLine.shift();
217219
}
218220
}
@@ -238,10 +240,18 @@ export class MainWindow extends ApplicationWindow {
238240
// Main window state.
239241

240242
if (!this.isMaximized() && !this.isMinimized() && !this.isFullScreen()) {
241-
state.x = this.getPosition()[0];
242-
state.y = this.getPosition()[1];
243-
state.width = this.getContentSize()[0];
244-
state.height = this.getContentSize()[1];
243+
const [stateX, stateY] = this.getPosition();
244+
const [stateWidth, stateHeight] = this.getContentSize();
245+
246+
if (typeof stateX === 'number' && typeof stateY === 'number') {
247+
state.x = stateX;
248+
state.y = stateY;
249+
}
250+
251+
if (typeof stateWidth === 'number' && typeof stateHeight === 'number') {
252+
state.width = stateWidth;
253+
state.height = stateHeight;
254+
}
245255
}
246256

247257
state.isMaximized = this.isMaximized();
@@ -269,30 +279,64 @@ export class MainWindow extends ApplicationWindow {
269279
this.setAutoHideMenuBar(false);
270280
this.setMenuBarVisibility(true);
271281

272-
// Open external links in the default browser.
282+
// Open links in the default browser, unless it is a Firebase OAuth popup in which case we open it in a new window
283+
// so that the OAuth flow can proceed.
273284

274285
this.webContents.setWindowOpenHandler((details) => {
275-
electron.shell.openExternal(details.url).catch((error: unknown) => {
276-
console.error('Failed to open external URL:', error);
277-
});
286+
function isFirebaseOauthPopup(url: string): boolean {
287+
try {
288+
const parsedUrl = new URL(url);
289+
290+
return (
291+
(parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') &&
292+
parsedUrl.host === 'opencorapp.firebaseapp.com' &&
293+
parsedUrl.pathname === '/__/auth/handler'
294+
);
295+
} catch {
296+
return false;
297+
}
298+
}
299+
300+
if (isFirebaseOauthPopup(details.url)) {
301+
return {
302+
action: 'allow',
303+
overrideBrowserWindowOptions: {
304+
parent: this,
305+
modal: false,
306+
autoHideMenuBar: true,
307+
width: 600,
308+
height: 700,
309+
backgroundColor: electron.nativeTheme.shouldUseDarkColors ? '#18181b' : '#ffffff',
310+
// Note: use the same colours as the ones used by --p-content-background in PrimeVue, i.e. what we are using
311+
// as a background for our app (see src/renderer/src/assets/app.css).
312+
webPreferences: {
313+
preload: undefined,
314+
sandbox: true,
315+
contextIsolation: true,
316+
nodeIntegration: false
317+
}
318+
}
319+
};
320+
}
321+
322+
if (isHttpUrl(details.url)) {
323+
electron.shell.openExternal(details.url).catch((error: unknown) => {
324+
console.error(`Failed to open external URL (${details.url}):`, error);
325+
});
326+
} else {
327+
console.warn(`Blocked attempt to open unsupported URL (${details.url}).`);
328+
}
278329

279330
return {
280331
action: 'deny'
281332
};
282333
});
283334

284-
// Load the remote URL when ELECTRON_RENDERER_URL is provided (i.e. if we are not packaged), otherwise load the
285-
// local HTML file.
335+
// Load the renderer URL.
286336

287-
if (process.env.ELECTRON_RENDERER_URL) {
288-
this.loadURL(process.env.ELECTRON_RENDERER_URL).catch((error: unknown) => {
289-
console.error('Failed to load URL:', error);
290-
});
291-
} else {
292-
this.loadFile('./out/renderer/index.html').catch((error: unknown) => {
293-
console.error('Failed to load file:', error);
294-
});
295-
}
337+
this.loadURL(rendererUrl).catch((error: unknown) => {
338+
console.error(`Failed to load URL (${rendererUrl}):`, error);
339+
});
296340
}
297341

298342
// Reopen previously opened files, if any, and select the previously selected file.
@@ -321,7 +365,11 @@ export class MainWindow extends ApplicationWindow {
321365

322366
// Handle our command line arguments.
323367

324-
isAction(argument: string): boolean {
368+
isAction(argument: string | undefined): boolean {
369+
if (argument === undefined) {
370+
return false;
371+
}
372+
325373
return argument.startsWith(FULL_URI_SCHEME);
326374
}
327375

0 commit comments

Comments
 (0)