Skip to content

Commit 397bd84

Browse files
🆕 feat: add 'components.json' file and update several modules in 'electron/main.ts'
This commit introduces a new 'components.json' configuration file, specifying UI components' settings such as Tailwind CSS configuration and component aliases. In 'electron/main.ts', various imports are updated, including the addition of new utility classes and the enhancement of functionality for user account and preference management. The update enhances the modularization and readability of the code, paving the way for more streamlined and efficient development processes. Additionally, Sentry error logging and preference management functionalities are refined, offering improved debugging and user customization capabilities. This contributes to a more robust and user-friendly application. 📝 chore: update 'index.css' with new Tailwind CSS classes and configurations The modification of 'index.css' reflects an overhaul of the CSS styling strategy, adopting Tailwind CSS classes and configurations. This change streamlines the styling process, providing a more organized and maintainable codebase. The updated styling strategy ensures consistency across the application, enhancing the user interface's aesthetic appeal and usability. 📈 feat: implement new components and utilities in 'src' directory Significant enhancements are made in the 'src' directory, where new components and utility functions are introduced. These additions include 'NavBar', 'Recorder', and various UI components like 'avatar', 'button', and 'dialog'. These components enrich the application's UI, offering more features and interactivity. The utility functions aid in code reusability and maintainability, facilitating smoother application development and functionality enhancements.
1 parent 9b82f4c commit 397bd84

24 files changed

Lines changed: 1767 additions & 243 deletions

components.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "default",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.js",
8+
"css": "src/index.css",
9+
"baseColor": "slate",
10+
"cssVariables": true
11+
},
12+
"aliases": {
13+
"components": "@/components",
14+
"utils": "@/lib/utils"
15+
}
16+
}

electron/main.ts

Lines changed: 173 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,61 @@ import { writeFile as writeFilePromise, readFile as readFilePromise, unlink as u
1010
import path from 'node:path'
1111
import got from "got";
1212
import cp from 'child_process';
13-
import { MacWindow, UploadLink, baseUrl } from '../src/utils';
13+
import { MacWindow, UploadLink, baseUrl, Account, Preference } from '../src/utils';
1414
import { platform } from 'process';
1515

16+
const sessionDataPath = app.getPath('sessionData');
17+
const deviceCodeFilePath = path.join(sessionDataPath, 'deviceCode.txt');
18+
const userAccountFilePath = path.join(sessionDataPath, 'userAccount.txt');
19+
const userPreferencesFilePath = path.join(sessionDataPath, 'userPreferences.txt');
20+
21+
1622
autoUpdater.logger = logger
17-
console.log(`dsn: ${process.env.SENTRY_DSN}`)
1823
Sentry.init({
1924
dsn: 'https://d9c49d59e5554239ac977e3a7c409cda@glitchtip.dermot.email/2'
2025
});
26+
27+
const getPreferences = async (): Promise<Preference[] | null> => {
28+
try {
29+
const preferences = await readFilePromise(userPreferencesFilePath, 'utf-8');
30+
let parsed = JSON.parse(preferences) as Preference[] ?? [];
31+
if (!Array.isArray(parsed)) return [];
32+
33+
// Remove duplicates based on Preference.name
34+
const uniquePreferences = Array.from(new Map(parsed.map(preference => [preference.name, preference])).values());
35+
return uniquePreferences;
36+
} catch (error: any) {
37+
console.log(error)
38+
Sentry.captureException(new Error(`Failed to get preferences: ${error?.message}`), {
39+
tags: { module: "getPreferences" },
40+
extra: { error }
41+
});
42+
return null;
43+
}
44+
};
45+
46+
const getPreference = async (key: string): Promise<Preference['value'] | null> => {
47+
try {
48+
const preferences = await getPreferences();
49+
console.log({ preferences })
50+
return preferences?.find(preference => preference.name === key)?.value ?? null
51+
} catch (error: any) {
52+
console.log(error);
53+
return null
54+
}
55+
};
56+
57+
getPreference('errorLoggingEnabled').then((errorLoggingEnabled: string | boolean | null) => {
58+
// Disable error logging if the preference is set to false
59+
if (errorLoggingEnabled !== null && errorLoggingEnabled === false) {
60+
const client = Sentry.getCurrentHub().getClient();
61+
const options = client?.getOptions();
62+
if (options) {
63+
options.enabled = false;
64+
}
65+
}
66+
});
67+
2168
logger.info('App starting...');
2269

2370
import ffmpeg from 'fluent-ffmpeg';
@@ -35,11 +82,6 @@ let tray: Tray | null = null
3582
// recording state
3683
let showCameraWindow = false;
3784

38-
const sessionDataPath = app.getPath('sessionData');
39-
const deviceCodeFilePath = path.join(sessionDataPath, 'deviceCode.txt');
40-
41-
// myUndefinedFunction();
42-
4385
function getComputerName() {
4486
switch (process.platform) {
4587
case "win32":
@@ -54,6 +96,20 @@ function getComputerName() {
5496
}
5597
}
5698

99+
const refreshWindows = () => {
100+
try {
101+
if (mainWindow) mainWindow?.webContents.send('set-window', 'main')
102+
if (floatingWindow) floatingWindow?.webContents.send('set-window', 'floating')
103+
if (webcamWindow) webcamWindow?.webContents.send('set-window', 'webcam')
104+
} catch (error: any) {
105+
console.log(error)
106+
Sentry.captureException(new Error(`Failed to refresh: ${error?.message}`), {
107+
tags: { module: "refreshWindows" },
108+
extra: { error }
109+
});
110+
}
111+
};
112+
57113
ipcMain.handle('get-desktop-capturer-sources', async () => {
58114
const screenSources = await desktopCapturer.getSources({ types: ['screen'], fetchWindowIcons: true, thumbnailSize: { width: 1920, height: 1080 } })
59115
const windowSources = await desktopCapturer.getSources({ types: ['window',], fetchWindowIcons: true, thumbnailSize: { width: 1920, height: 1080 } })
@@ -164,6 +220,7 @@ ipcMain.handle('stop-recording', async (_) => {
164220
if (webcamWindow) {
165221
toggleCameraWindow(true);
166222
webcamWindow.setAlwaysOnTop(false);
223+
webcamWindow.minimize();
167224
webcamWindow.hide();
168225
}
169226
if (mainWindow) {
@@ -412,18 +469,12 @@ const toggleCameraWindow = (show: boolean) => {
412469
const verifyDeviceCode = async (deviceCode: string) => {
413470
try {
414471
const url = `${baseUrl}/api/devices/verify`;
415-
const response = await fetch(url, {
416-
method: 'POST',
417-
headers: {
418-
'Content-Type': 'application/json'
419-
},
420-
body: JSON.stringify({ deviceCode })
421-
});
472+
const response = await axios.post(url, { deviceCode });
422473
if (response.status !== 200) {
423474
console.log(JSON.stringify({ e: "failed to verify device code", url }))
424475
throw new Error(response.statusText);
425476
}
426-
const data = await response.json();
477+
const data = response.data;
427478
Sentry.setUser({ userId: data?.id, deviceName: data?.name, projectId: data?.user?.currentProjectId, name: data?.user?.name, email: data?.user?.email })
428479

429480
if (data.error) {
@@ -466,58 +517,115 @@ const getDeviceCode = async () => {
466517
}
467518
}
468519

469-
const log = async (data: object) => {
470-
const webhookUrl = 'https://webhook.site/ce05f4c4-7d74-4013-a954-356b11d11873';
471-
try {
472-
await fetch(webhookUrl, {
473-
method: 'POST',
474-
headers: {
475-
'Content-Type': 'application/json'
476-
},
477-
body: JSON.stringify(data)
478-
});
479-
} catch (error: any) {
480-
console.error('Failed to send log:', error);
481-
Sentry.captureException(new Error(`Failed to send log: ${error?.message}`), {
482-
tags: { module: "log" },
483-
extra: { error }
484-
});
485-
}
486-
}
487-
488-
const getAccount = async () => {
520+
const getAccount = async (): Promise<Account | null> => {
489521
try {
522+
// Check if user has a device code (login status)
490523
const deviceCode = await getDeviceCode();
491524
if (deviceCode === '' || !deviceCode) {
492525
if (mainWindow) {
493526
mainWindow.webContents.send('device-code', '');
494527
}
495-
return;
528+
return null;
529+
}
530+
531+
// Auth cache stored for 5 minutes
532+
const userAccountFileBuffer = await readFilePromise(userAccountFilePath);
533+
const userAccount: Account = JSON.parse(userAccountFileBuffer.toString());
534+
// Check if the user account was updated less than 5 minutes ago
535+
const currentTime = new Date().getTime();
536+
const lastUpdatedTime = new Date(userAccount.lastUpdated).getTime();
537+
const timeDifferenceInMinutes = (currentTime - lastUpdatedTime) / (1000 * 60);
538+
if (timeDifferenceInMinutes < 5) {
539+
return userAccount;
496540
}
497541

542+
498543
// Verify the device code
499-
const device = await verifyDeviceCode(deviceCode);
544+
const device: Account = await verifyDeviceCode(deviceCode);
500545
if (!device) {
501546
if (mainWindow) {
502547
mainWindow.webContents.send('device-code', '');
503548
Sentry.captureException(new Error(`Failed to get account: device not found`), {
504-
tags: { module: "getAccount" },
505-
extra: { deviceCode }
549+
tags: { module: "getAccount", deviceCode },
506550
});
507551
}
508-
return;
552+
return null;
509553
}
554+
555+
// Write the response to userAccountFilePath
556+
await writeFilePromise(userAccountFilePath, JSON.stringify({ ...device, lastUpdated: new Date() }));
557+
510558
return device;
511559
} catch (error: any) {
512560
console.log(error)
513561
Sentry.captureException(new Error(`Failed to get account: ${error?.message}`), {
514562
tags: { module: "getAccount" },
515563
extra: { error }
516564
});
517-
return;
565+
return null;
518566
}
519567
}
520568

569+
ipcMain.handle('get-account', async (_) => {
570+
try {
571+
const account = await getAccount();
572+
return account;
573+
} catch (error: any) {
574+
console.log(error)
575+
Sentry.captureException(new Error(`Failed to get account: ${error?.message}`), {
576+
tags: { module: "getAccount" },
577+
extra: { error }
578+
});
579+
return null;
580+
}
581+
});
582+
583+
584+
const updatePreferences = async (newPreferences: Preference[]): Promise<boolean> => {
585+
try {
586+
// Write upserted preferences back to file
587+
await writeFilePromise(userPreferencesFilePath, JSON.stringify(newPreferences));
588+
return true;
589+
} catch (error: any) {
590+
console.log(error)
591+
Sentry.captureException(new Error(`Failed to upsert preferences: ${error?.message}`), {
592+
tags: { module: "upsertPreferences" },
593+
extra: { error }
594+
});
595+
return false;
596+
}
597+
};
598+
599+
ipcMain.handle('get-preferences', async (_) => {
600+
try {
601+
const preferences = await getPreferences();
602+
return preferences;
603+
} catch (error: any) {
604+
console.log(error)
605+
Sentry.captureException(new Error(`Failed to get preferences: ${error?.message}`), {
606+
tags: { module: "getPreferences" },
607+
extra: { error }
608+
});
609+
return null;
610+
}
611+
});
612+
613+
ipcMain.handle('update-preferences', async (_event, newPreferences: Preference[]) => {
614+
try {
615+
await updatePreferences(newPreferences);
616+
return true;
617+
} catch (error: any) {
618+
console.log(error)
619+
Sentry.captureException(new Error(`Failed to set preferences: ${error?.message}`), {
620+
tags: { module: "setPreferences" },
621+
extra: { error }
622+
});
623+
return false;
624+
}
625+
});
626+
627+
628+
521629
ipcMain.handle('get-device-code', async (_) => {
522630
try {
523631
const deviceCode = await getDeviceCode();
@@ -543,6 +651,25 @@ ipcMain.handle('get-device-code', async (_) => {
543651
}
544652
});
545653

654+
ipcMain.handle('logout', async (_) => {
655+
try {
656+
// Clear the user account and device code
657+
await writeFilePromise(userAccountFilePath, JSON.stringify({}));
658+
await writeFilePromise(deviceCodeFilePath, JSON.stringify({}));
659+
// Send a logout event to the renderer process
660+
if (mainWindow) mainWindow.webContents.send('device-code', '');
661+
if (floatingWindow) floatingWindow.hide();
662+
if (webcamWindow) webcamWindow.hide();
663+
} catch (error: any) {
664+
console.log(error)
665+
Sentry.captureException(new Error(`Failed to logout: ${error?.message}`), {
666+
tags: { module: "logout" },
667+
extra: { error }
668+
});
669+
}
670+
});
671+
672+
546673
ipcMain.handle('permissions-missing', async (_) => {
547674
try {
548675
if (mainWindow) {
@@ -586,6 +713,10 @@ const missingPermissions = async () => {
586713
return missingList;
587714
}
588715

716+
setTimeout(() => {
717+
Sentry.captureException(new Error(`Testing sentry`));
718+
});
719+
589720
const requestPermissions = async (permission: string): Promise<boolean> => {
590721
try {
591722
if (permission === 'camera' || permission === 'microphone') {
@@ -822,6 +953,7 @@ function createWindow() {
822953
console.log('Device code on ready: ', deviceCode);
823954
if (mainWindow) {
824955
mainWindow.webContents.send('device-code', deviceCode);
956+
refreshWindows();
825957
} else {
826958
console.log('No window to send device code to');
827959
}

electron/preload.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { contextBridge, ipcRenderer } from 'electron'
2-
import { UploadLink } from '../src/utils';
2+
import { Preference, UploadLink } from '../src/utils';
33

44

55
// --------- Expose some API to the Renderer process ---------
@@ -14,6 +14,18 @@ contextBridge.exposeInMainWorld('electron', {
1414
getDeviceCode: async (): Promise<string> => {
1515
return await ipcRenderer.invoke('get-device-code')
1616
},
17+
logout: async (): Promise<void> => {
18+
return await ipcRenderer.invoke('logout')
19+
},
20+
getAccount: async (): Promise<any> => {
21+
return await ipcRenderer.invoke('get-account')
22+
},
23+
getPreferences: async (): Promise<Preference[]> => {
24+
return await ipcRenderer.invoke('get-preferences')
25+
},
26+
updatePreferences: async (preferences: Preference[]): Promise<void> => {
27+
return await ipcRenderer.invoke('update-preferences', preferences)
28+
},
1729
getDesktopCapturerSources: async (): Promise<Electron.DesktopCapturerSource> => {
1830
return await ipcRenderer.invoke('get-desktop-capturer-sources')
1931
},

0 commit comments

Comments
 (0)