Skip to content

Commit

Permalink
fix: production build not launching (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
teofanis authored Oct 14, 2022
1 parent 5187123 commit 41cea1d
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 33 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
Decided to have some fun and learn a bit of electron.
The use case was to turn my youtube downloader script into a usable desktop app.

In use by family members and close friends.

## Demo

![electronV005](https://user-images.githubusercontent.com/47872542/195666060-e0da23ad-dd08-4892-9ff8-ed8e5c18ca42.gif)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@
"classnames": "^2.3.2",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.8",
"electron-shared-state": "^1.0.0",
"electron-updater": "^5.2.1",
"immer": "^9.0.15",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
Expand Down
14 changes: 0 additions & 14 deletions release/app/package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ElectronYoutubeDownloader",
"version": "0.0.5",
"version": "0.0.6",
"description": "Electron Youtube Downloader",
"license": "MIT",
"author": {
Expand Down
185 changes: 185 additions & 0 deletions src/libs/electron-shared-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/* eslint-disable global-require */
/* eslint-disable no-empty */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable no-plusplus */
/* eslint-disable @typescript-eslint/no-unused-expressions */
/* eslint-disable prefer-destructuring */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable no-unneeded-ternary */
/* eslint-disable prefer-const */
/* eslint-disable import/prefer-default-export */
import {
ipcMain,
IpcMainInvokeEvent,
ipcRenderer,
IpcRenderer,
IpcRendererEvent,
webContents,
} from 'electron';
import produce, { applyPatches, enablePatches, Patch } from 'immer';
import process from 'process';

enablePatches();

interface IChangePack {
patches: Patch[];
description?: string;
senderId?: number;
}

export function createSharedStore<T>(state: T) {
let innerState = state;
let lastChange: IChangePack = { patches: [] };
let listeners: ((state: T, description?: string) => void)[] = [];

const connected = new Set<number>(); // this is only for main process
const isRenderer = process?.type === 'renderer'; // @ts-ignore
const isMain = process?.type === 'browser' || process?.type === 'main';
const ipcModule = isMain ? ipcMain : ipcRenderer;
const INTERNAL_CHANNEL = '@@ELECTRON_SHARED_STORE_IPC_CHANNEL';
const INTERNAL_CHANNEL_INIT = '@@ELECTRON_SHARED_STORE_IPC_CHANNEL_INIT';

let isUpdating = false;

if (isRenderer) {
// during creation, we need to sync state in renderer with state in main (workaround for this issue - https://github.com/zoubingwu/electron-shared-state/issues/3 )
try {
const initState = (ipcModule as IpcRenderer).sendSync(
INTERNAL_CHANNEL_INIT
);
innerState = initState ? initState : innerState;
} catch (error) {
console.log(error);
}
}

if (isMain) {
ipcMain.on(INTERNAL_CHANNEL_INIT, (event) => {
event.returnValue = getState();
});
}
ipcModule.on(
INTERNAL_CHANNEL,
(event: IpcMainInvokeEvent | IpcRendererEvent, change: IChangePack) => {
if (isMain) {
const id = (event as IpcMainInvokeEvent).sender.id; // webContent's id
connected.add(id);
}

if (change.patches.length === 0) {
return;
}

isUpdating = true;
// @ts-ignore
const nextState = applyPatches(innerState, change.patches);
lastChange = {
...change,
senderId: isMain ? (event as IpcMainInvokeEvent).sender.id : -1, // renderer always receives from main so let's say id is -1
};

broadcastChange();
// @ts-ignore
innerState = nextState;

isUpdating = false;

for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener(innerState, change.description);
}
}
);

function broadcastChange() {
if (lastChange.patches.length === 0) {
return;
}

if (isRenderer) {
// if lastChange was from main, we don't send it to main again
lastChange.senderId !== -1 &&
(ipcModule as IpcRenderer).send(INTERNAL_CHANNEL, lastChange);
} else if (isMain) {
connected.forEach((id) => {
// do not broadcast to sender process
if (id === lastChange.senderId) {
return;
}

const wc = webContents.fromId(id);
if (wc) {
wc.send(INTERNAL_CHANNEL, lastChange);
}
});
}
}

function setState(recipe: (draft: T) => void, description?: string) {
isUpdating = true;

const nextState = produce(innerState, recipe, (patches) => {
lastChange = { patches, description };
});

broadcastChange();

innerState = nextState;
isUpdating = false;

for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener(innerState, lastChange.description);
}

return nextState;
}

function getState(): T {
if (isUpdating) {
throw new Error(
'You may not call store.getState() inside setState method. ' +
'It has already received the state as an argument. '
);
}

return innerState;
}

function subscribe(listener: (state: T, description?: string) => void) {
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.');
}

if (isUpdating) {
throw new Error(
'You may not call store.subscribe() inside store.setState(). '
);
}

listeners.push(listener);

// run once for the first time for every one who just subscribed
listener(innerState, lastChange.description);

return function unsubscribe() {
if (isUpdating) {
throw new Error(
'You may not unsubscribe from a store listener while the state is updating. '
);
}

const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}

if (isRenderer) {
// send empty change to main, so main process can save the senderId
(ipcModule as IpcRenderer).send(INTERNAL_CHANNEL, {
patches: [],
});
}

return { setState, getState, subscribe };
}
18 changes: 9 additions & 9 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ const installExtensions = async () => {
)
.catch(console.log);
};
const registerIpcChannels = (ipcChannels: IpcChannelInterface[]) => {
ipcChannels.forEach((channel) =>
ipcMain.on(channel.getName(), (event, request) =>
channel.handle(event, request)
)
);
};

const createWindow = async () => {
if (isDebug) {
Expand Down Expand Up @@ -121,28 +128,21 @@ app.on('window-all-closed', () => {
app.quit();
}
});
const registerIpcChannels = (ipcChannels: IpcChannelInterface[]) => {
ipcChannels.forEach((channel) =>
ipcMain.on(channel.getName(), (event, request) =>
channel.handle(event, request)
)
);
};

app
.whenReady()
.then(() => {
createWindow();

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) createWindow();
});
registerIpcChannels([new DownloaderChannel()]);
})
.catch(console.log);

registerIpcChannels([new DownloaderChannel()]);

// store.subscribe((state) => {
// // console.log(state);
// });
2 changes: 1 addition & 1 deletion src/main/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSharedStore } from 'electron-shared-state';
import { DownloadQueue } from 'interfaces';
import { createSharedStore } from '../libs/electron-shared-state';

const initialState = {
downloadQueue: [] as DownloadQueue,
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3690,11 +3690,6 @@ electron-rebuild@^3.2.9:
tar "^6.0.5"
yargs "^17.0.1"

electron-shared-state@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/electron-shared-state/-/electron-shared-state-1.0.0.tgz#67dd15d0061414888c3e30e81dbd739ccdac7a10"
integrity sha512-MTrniWxrWUek9FfBNgS+wJHBtTtnx7qd3gPlL+0JEwJ9KMDSSDNV5JjDfGDPAJt5/c9Jpsvd80/GxvWA2txpZw==

electron-to-chromium@^1.4.251:
version "1.4.261"
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.261.tgz#31f14ad60c6f95bec404a77a2fd5e1962248e112"
Expand Down Expand Up @@ -5316,6 +5311,11 @@ immediate@~3.0.5:
resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==

immer@^9.0.15:
version "9.0.15"
resolved "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==

immutable@^4.0.0:
version "4.1.0"
resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
Expand Down

0 comments on commit 41cea1d

Please sign in to comment.