mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
added electron
This commit is contained in:
parent
a449bc7497
commit
839dd30ee1
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
release-builds/
|
||||||
|
8
electron/.gitignore
vendored
Normal file
8
electron/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# NPM renames .gitignore to .npmignore
|
||||||
|
# In order to prevent that, we remove the initial "."
|
||||||
|
# And the CLI then renames it
|
||||||
|
app
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
logs
|
BIN
electron/assets/appIcon.ico
Normal file
BIN
electron/assets/appIcon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
BIN
electron/assets/appIcon.png
Normal file
BIN
electron/assets/appIcon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
BIN
electron/assets/splash.gif
Normal file
BIN
electron/assets/splash.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 159 KiB |
BIN
electron/assets/splash.png
Normal file
BIN
electron/assets/splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
15
electron/capacitor.config.ts
Normal file
15
electron/capacitor.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { CapacitorConfig } from '@capacitor/cli';
|
||||||
|
|
||||||
|
const config: CapacitorConfig = {
|
||||||
|
appId: 'com.example.app',
|
||||||
|
appName: 'Qortal ',
|
||||||
|
webDir: 'dist',
|
||||||
|
"plugins": {
|
||||||
|
"LocalNotifications": {
|
||||||
|
"smallIcon": "qort",
|
||||||
|
"iconColor": "#09b6e8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
28
electron/electron-builder.config.json
Normal file
28
electron/electron-builder.config.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"appId": "com.yourdoamnin.yourapp",
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "resources"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"assets/**/*",
|
||||||
|
"build/**/*",
|
||||||
|
"capacitor.config.*",
|
||||||
|
"app/**/*"
|
||||||
|
],
|
||||||
|
"publish": {
|
||||||
|
"provider": "github"
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"allowElevation": true,
|
||||||
|
"oneClick": false,
|
||||||
|
"allowToChangeInstallationDirectory": true
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"target": "nsis",
|
||||||
|
"icon": "assets/appIcon.ico"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"category": "your.app.category.type",
|
||||||
|
"target": "dmg"
|
||||||
|
}
|
||||||
|
}
|
75
electron/live-runner.js
Normal file
75
electron/live-runner.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const cp = require('child_process');
|
||||||
|
const chokidar = require('chokidar');
|
||||||
|
const electron = require('electron');
|
||||||
|
|
||||||
|
let child = null;
|
||||||
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||||
|
const reloadWatcher = {
|
||||||
|
debouncer: null,
|
||||||
|
ready: false,
|
||||||
|
watcher: null,
|
||||||
|
restarting: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
///*
|
||||||
|
function runBuild() {
|
||||||
|
return new Promise((resolve, _reject) => {
|
||||||
|
let tempChild = cp.spawn(npmCmd, ['run', 'build']);
|
||||||
|
tempChild.once('exit', () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
tempChild.stdout.pipe(process.stdout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
|
||||||
|
async function spawnElectron() {
|
||||||
|
if (child !== null) {
|
||||||
|
child.stdin.pause();
|
||||||
|
child.kill();
|
||||||
|
child = null;
|
||||||
|
await runBuild();
|
||||||
|
}
|
||||||
|
child = cp.spawn(electron, ['--inspect=5858', './']);
|
||||||
|
child.on('exit', () => {
|
||||||
|
if (!reloadWatcher.restarting) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
child.stdout.pipe(process.stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupReloadWatcher() {
|
||||||
|
reloadWatcher.watcher = chokidar
|
||||||
|
.watch('./src/**/*', {
|
||||||
|
ignored: /[/\\]\./,
|
||||||
|
persistent: true,
|
||||||
|
})
|
||||||
|
.on('ready', () => {
|
||||||
|
reloadWatcher.ready = true;
|
||||||
|
})
|
||||||
|
.on('all', (_event, _path) => {
|
||||||
|
if (reloadWatcher.ready) {
|
||||||
|
clearTimeout(reloadWatcher.debouncer);
|
||||||
|
reloadWatcher.debouncer = setTimeout(async () => {
|
||||||
|
console.log('Restarting');
|
||||||
|
reloadWatcher.restarting = true;
|
||||||
|
await spawnElectron();
|
||||||
|
reloadWatcher.restarting = false;
|
||||||
|
reloadWatcher.ready = false;
|
||||||
|
clearTimeout(reloadWatcher.debouncer);
|
||||||
|
reloadWatcher.debouncer = null;
|
||||||
|
reloadWatcher.watcher = null;
|
||||||
|
setupReloadWatcher();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await runBuild();
|
||||||
|
await spawnElectron();
|
||||||
|
setupReloadWatcher();
|
||||||
|
})();
|
5272
electron/package-lock.json
generated
Normal file
5272
electron/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
electron/package.json
Normal file
41
electron/package.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "Qortal ",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "An Amazing Capacitor App",
|
||||||
|
"author": {
|
||||||
|
"name": "",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": ""
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "build/src/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && electron-rebuild",
|
||||||
|
"electron:start-live": "node ./live-runner.js",
|
||||||
|
"electron:start": "npm run build && electron --inspect=5858 ./",
|
||||||
|
"electron:pack": "npm run build && electron-builder build --dir -c ./electron-builder.config.json",
|
||||||
|
"electron:make": "npm run build && electron-builder build -c ./electron-builder.config.json -p always"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@capacitor-community/electron": "^5.0.0",
|
||||||
|
"chokidar": "~3.5.3",
|
||||||
|
"electron-is-dev": "~2.0.0",
|
||||||
|
"electron-serve": "~1.1.0",
|
||||||
|
"electron-unhandled": "~4.0.1",
|
||||||
|
"electron-updater": "^5.3.0",
|
||||||
|
"electron-window-state": "^5.0.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "^26.2.2",
|
||||||
|
"electron-builder": "~23.6.0",
|
||||||
|
"electron-rebuild": "^3.2.9",
|
||||||
|
"typescript": "^5.0.4"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"capacitor",
|
||||||
|
"electron"
|
||||||
|
]
|
||||||
|
}
|
10
electron/resources/electron-publisher-custom.js
Normal file
10
electron/resources/electron-publisher-custom.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const electronPublish = require('electron-publish');
|
||||||
|
|
||||||
|
class Publisher extends electronPublish.Publisher {
|
||||||
|
async upload(task) {
|
||||||
|
console.log('electron-publisher-custom', task.file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = Publisher;
|
70
electron/src/index.ts
Normal file
70
electron/src/index.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
|
||||||
|
import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron';
|
||||||
|
import type { MenuItemConstructorOptions } from 'electron';
|
||||||
|
import { app, MenuItem } from 'electron';
|
||||||
|
import electronIsDev from 'electron-is-dev';
|
||||||
|
import unhandled from 'electron-unhandled';
|
||||||
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
|
||||||
|
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
|
||||||
|
|
||||||
|
// Graceful handling of unhandled errors.
|
||||||
|
unhandled();
|
||||||
|
|
||||||
|
// Define our menu templates (these are optional)
|
||||||
|
const trayMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [new MenuItem({ label: 'Quit App', role: 'quit' })];
|
||||||
|
const appMenuBarMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [
|
||||||
|
{ role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' },
|
||||||
|
{ role: 'viewMenu' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get Config options from capacitor.config
|
||||||
|
const capacitorFileConfig: CapacitorElectronConfig = getCapacitorElectronConfig();
|
||||||
|
|
||||||
|
// Initialize our app. You can pass menu templates into the app here.
|
||||||
|
// const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig);
|
||||||
|
const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig, trayMenuTemplate, appMenuBarMenuTemplate);
|
||||||
|
|
||||||
|
// If deeplinking is enabled then we will set it up here.
|
||||||
|
if (capacitorFileConfig.electron?.deepLinkingEnabled) {
|
||||||
|
setupElectronDeepLinking(myCapacitorApp, {
|
||||||
|
customProtocol: capacitorFileConfig.electron.deepLinkingCustomProtocol ?? 'mycapacitorapp',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in Dev mode, use the file watcher components.
|
||||||
|
if (electronIsDev) {
|
||||||
|
setupReloadWatcher(myCapacitorApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run Application
|
||||||
|
(async () => {
|
||||||
|
// Wait for electron app to be ready.
|
||||||
|
await app.whenReady();
|
||||||
|
// Security - Set Content-Security-Policy based on whether or not we are in dev mode.
|
||||||
|
setupContentSecurityPolicy(myCapacitorApp.getCustomURLScheme());
|
||||||
|
// Initialize our app, build windows, and load content.
|
||||||
|
await myCapacitorApp.init();
|
||||||
|
// Check for updates if we are in a packaged app.
|
||||||
|
autoUpdater.checkForUpdatesAndNotify();
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Handle when all of our windows are close (platforms have their own expectations).
|
||||||
|
app.on('window-all-closed', function () {
|
||||||
|
// On OS X it is common for applications and their menu bar
|
||||||
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the dock icon is clicked.
|
||||||
|
app.on('activate', async function () {
|
||||||
|
// On OS X 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 (myCapacitorApp.getMainWindow().isDestroyed()) {
|
||||||
|
await myCapacitorApp.init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Place all ipc or other electron api calls and custom functionality under this line
|
4
electron/src/preload.ts
Normal file
4
electron/src/preload.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
require('./rt/electron-rt');
|
||||||
|
//////////////////////////////
|
||||||
|
// User Defined Preload scripts below
|
||||||
|
console.log('User Preload!');
|
4
electron/src/rt/electron-plugins.js
Normal file
4
electron/src/rt/electron-plugins.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
}
|
88
electron/src/rt/electron-rt.ts
Normal file
88
electron/src/rt/electron-rt.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
import { ipcRenderer, contextBridge } from 'electron';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const plugins = require('./electron-plugins');
|
||||||
|
|
||||||
|
const randomId = (length = 5) => randomBytes(length).toString('hex');
|
||||||
|
|
||||||
|
const contextApi: {
|
||||||
|
[plugin: string]: { [functionName: string]: () => Promise<any> };
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
Object.keys(plugins).forEach((pluginKey) => {
|
||||||
|
Object.keys(plugins[pluginKey])
|
||||||
|
.filter((className) => className !== 'default')
|
||||||
|
.forEach((classKey) => {
|
||||||
|
const functionList = Object.getOwnPropertyNames(plugins[pluginKey][classKey].prototype).filter(
|
||||||
|
(v) => v !== 'constructor'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!contextApi[classKey]) {
|
||||||
|
contextApi[classKey] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
functionList.forEach((functionName) => {
|
||||||
|
if (!contextApi[classKey][functionName]) {
|
||||||
|
contextApi[classKey][functionName] = (...args) => ipcRenderer.invoke(`${classKey}-${functionName}`, ...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Events
|
||||||
|
if (plugins[pluginKey][classKey].prototype instanceof EventEmitter) {
|
||||||
|
const listeners: { [key: string]: { type: string; listener: (...args: any[]) => void } } = {};
|
||||||
|
const listenersOfTypeExist = (type) =>
|
||||||
|
!!Object.values(listeners).find((listenerObj) => listenerObj.type === type);
|
||||||
|
|
||||||
|
Object.assign(contextApi[classKey], {
|
||||||
|
addListener(type: string, callback: (...args) => void) {
|
||||||
|
const id = randomId();
|
||||||
|
|
||||||
|
// Deduplicate events
|
||||||
|
if (!listenersOfTypeExist(type)) {
|
||||||
|
ipcRenderer.send(`event-add-${classKey}`, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventHandler = (_, ...args) => callback(...args);
|
||||||
|
|
||||||
|
ipcRenderer.addListener(`event-${classKey}-${type}`, eventHandler);
|
||||||
|
listeners[id] = { type, listener: eventHandler };
|
||||||
|
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
removeListener(id: string) {
|
||||||
|
if (!listeners[id]) {
|
||||||
|
throw new Error('Invalid id');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, listener } = listeners[id];
|
||||||
|
|
||||||
|
ipcRenderer.removeListener(`event-${classKey}-${type}`, listener);
|
||||||
|
|
||||||
|
delete listeners[id];
|
||||||
|
|
||||||
|
if (!listenersOfTypeExist(type)) {
|
||||||
|
ipcRenderer.send(`event-remove-${classKey}-${type}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeAllListeners(type: string) {
|
||||||
|
Object.entries(listeners).forEach(([id, listenerObj]) => {
|
||||||
|
if (!type || listenerObj.type === type) {
|
||||||
|
ipcRenderer.removeListener(`event-${classKey}-${listenerObj.type}`, listenerObj.listener);
|
||||||
|
ipcRenderer.send(`event-remove-${classKey}-${listenerObj.type}`);
|
||||||
|
delete listeners[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('CapacitorCustomPlatform', {
|
||||||
|
name: 'electron',
|
||||||
|
plugins: contextApi,
|
||||||
|
});
|
||||||
|
////////////////////////////////////////////////////////
|
231
electron/src/setup.ts
Normal file
231
electron/src/setup.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
|
||||||
|
import {
|
||||||
|
CapElectronEventEmitter,
|
||||||
|
CapacitorSplashScreen,
|
||||||
|
setupCapacitorElectronPlugins,
|
||||||
|
} from '@capacitor-community/electron';
|
||||||
|
import chokidar from 'chokidar';
|
||||||
|
import type { MenuItemConstructorOptions } from 'electron';
|
||||||
|
import { app, BrowserWindow, Menu, MenuItem, nativeImage, Tray, session } from 'electron';
|
||||||
|
import electronIsDev from 'electron-is-dev';
|
||||||
|
import electronServe from 'electron-serve';
|
||||||
|
import windowStateKeeper from 'electron-window-state';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
// Define components for a watcher to detect when the webapp is changed so we can reload in Dev mode.
|
||||||
|
const reloadWatcher = {
|
||||||
|
debouncer: null,
|
||||||
|
ready: false,
|
||||||
|
watcher: null,
|
||||||
|
};
|
||||||
|
export function setupReloadWatcher(electronCapacitorApp: ElectronCapacitorApp): void {
|
||||||
|
reloadWatcher.watcher = chokidar
|
||||||
|
.watch(join(app.getAppPath(), 'app'), {
|
||||||
|
ignored: /[/\\]\./,
|
||||||
|
persistent: true,
|
||||||
|
})
|
||||||
|
.on('ready', () => {
|
||||||
|
reloadWatcher.ready = true;
|
||||||
|
})
|
||||||
|
.on('all', (_event, _path) => {
|
||||||
|
if (reloadWatcher.ready) {
|
||||||
|
clearTimeout(reloadWatcher.debouncer);
|
||||||
|
reloadWatcher.debouncer = setTimeout(async () => {
|
||||||
|
electronCapacitorApp.getMainWindow().webContents.reload();
|
||||||
|
reloadWatcher.ready = false;
|
||||||
|
clearTimeout(reloadWatcher.debouncer);
|
||||||
|
reloadWatcher.debouncer = null;
|
||||||
|
reloadWatcher.watcher = null;
|
||||||
|
setupReloadWatcher(electronCapacitorApp);
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define our class to manage our app.
|
||||||
|
export class ElectronCapacitorApp {
|
||||||
|
private MainWindow: BrowserWindow | null = null;
|
||||||
|
private SplashScreen: CapacitorSplashScreen | null = null;
|
||||||
|
private TrayIcon: Tray | null = null;
|
||||||
|
private CapacitorFileConfig: CapacitorElectronConfig;
|
||||||
|
private TrayMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
|
||||||
|
new MenuItem({ label: 'Quit App', role: 'quit' }),
|
||||||
|
];
|
||||||
|
private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
|
||||||
|
{ role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' },
|
||||||
|
{ role: 'viewMenu' },
|
||||||
|
];
|
||||||
|
private mainWindowState;
|
||||||
|
private loadWebApp;
|
||||||
|
private customScheme: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
capacitorFileConfig: CapacitorElectronConfig,
|
||||||
|
trayMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[],
|
||||||
|
appMenuBarMenuTemplate?: (MenuItemConstructorOptions | MenuItem)[]
|
||||||
|
) {
|
||||||
|
this.CapacitorFileConfig = capacitorFileConfig;
|
||||||
|
|
||||||
|
this.customScheme = this.CapacitorFileConfig.electron?.customUrlScheme ?? 'capacitor-electron';
|
||||||
|
|
||||||
|
if (trayMenuTemplate) {
|
||||||
|
this.TrayMenuTemplate = trayMenuTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appMenuBarMenuTemplate) {
|
||||||
|
this.AppMenuBarMenuTemplate = appMenuBarMenuTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup our web app loader, this lets us load apps like react, vue, and angular without changing their build chains.
|
||||||
|
this.loadWebApp = electronServe({
|
||||||
|
directory: join(app.getAppPath(), 'app'),
|
||||||
|
scheme: this.customScheme,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to load in the app.
|
||||||
|
private async loadMainWindow(thisRef: any) {
|
||||||
|
await thisRef.loadWebApp(thisRef.MainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose the mainWindow ref for use outside of the class.
|
||||||
|
getMainWindow(): BrowserWindow {
|
||||||
|
return this.MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomURLScheme(): string {
|
||||||
|
return this.customScheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(): Promise<void> {
|
||||||
|
const icon = nativeImage.createFromPath(
|
||||||
|
join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')
|
||||||
|
);
|
||||||
|
this.mainWindowState = windowStateKeeper({
|
||||||
|
defaultWidth: 1000,
|
||||||
|
defaultHeight: 800,
|
||||||
|
});
|
||||||
|
// Setup preload script path and construct our main window.
|
||||||
|
const preloadPath = join(app.getAppPath(), 'build', 'src', 'preload.js');
|
||||||
|
this.MainWindow = new BrowserWindow({
|
||||||
|
icon,
|
||||||
|
show: false,
|
||||||
|
x: this.mainWindowState.x,
|
||||||
|
y: this.mainWindowState.y,
|
||||||
|
width: this.mainWindowState.width,
|
||||||
|
height: this.mainWindowState.height,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: true,
|
||||||
|
// Use preload to inject the electron varriant overrides for capacitor plugins.
|
||||||
|
// preload: join(app.getAppPath(), "node_modules", "@capacitor-community", "electron", "dist", "runtime", "electron-rt.js"),
|
||||||
|
preload: preloadPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.mainWindowState.manage(this.MainWindow);
|
||||||
|
|
||||||
|
if (this.CapacitorFileConfig.backgroundColor) {
|
||||||
|
this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we close the main window with the splashscreen enabled we need to destory the ref.
|
||||||
|
this.MainWindow.on('closed', () => {
|
||||||
|
if (this.SplashScreen?.getSplashWindow() && !this.SplashScreen.getSplashWindow().isDestroyed()) {
|
||||||
|
this.SplashScreen.getSplashWindow().close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the tray icon is enabled, setup the options.
|
||||||
|
if (this.CapacitorFileConfig.electron?.trayIconAndMenuEnabled) {
|
||||||
|
this.TrayIcon = new Tray(icon);
|
||||||
|
this.TrayIcon.on('double-click', () => {
|
||||||
|
if (this.MainWindow) {
|
||||||
|
if (this.MainWindow.isVisible()) {
|
||||||
|
this.MainWindow.hide();
|
||||||
|
} else {
|
||||||
|
this.MainWindow.show();
|
||||||
|
this.MainWindow.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.TrayIcon.on('click', () => {
|
||||||
|
if (this.MainWindow) {
|
||||||
|
if (this.MainWindow.isVisible()) {
|
||||||
|
this.MainWindow.hide();
|
||||||
|
} else {
|
||||||
|
this.MainWindow.show();
|
||||||
|
this.MainWindow.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.TrayIcon.setToolTip(app.getName());
|
||||||
|
this.TrayIcon.setContextMenu(Menu.buildFromTemplate(this.TrayMenuTemplate));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the main manu bar at the top of our window.
|
||||||
|
Menu.setApplicationMenu(Menu.buildFromTemplate(this.AppMenuBarMenuTemplate));
|
||||||
|
|
||||||
|
// If the splashscreen is enabled, show it first while the main window loads then switch it out for the main window, or just load the main window from the start.
|
||||||
|
if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
|
||||||
|
this.SplashScreen = new CapacitorSplashScreen({
|
||||||
|
imageFilePath: join(
|
||||||
|
app.getAppPath(),
|
||||||
|
'assets',
|
||||||
|
this.CapacitorFileConfig.electron?.splashScreenImageName ?? 'splash.png'
|
||||||
|
),
|
||||||
|
windowWidth: 400,
|
||||||
|
windowHeight: 400,
|
||||||
|
});
|
||||||
|
this.SplashScreen.init(this.loadMainWindow, this);
|
||||||
|
} else {
|
||||||
|
this.loadMainWindow(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security
|
||||||
|
this.MainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
|
if (!details.url.includes(this.customScheme)) {
|
||||||
|
return { action: 'deny' };
|
||||||
|
} else {
|
||||||
|
return { action: 'allow' };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.MainWindow.webContents.on('will-navigate', (event, _newURL) => {
|
||||||
|
if (!this.MainWindow.webContents.getURL().includes(this.customScheme)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Link electron plugins into the system.
|
||||||
|
setupCapacitorElectronPlugins();
|
||||||
|
|
||||||
|
// When the web app is loaded we hide the splashscreen if needed and show the mainwindow.
|
||||||
|
this.MainWindow.webContents.on('dom-ready', () => {
|
||||||
|
if (this.CapacitorFileConfig.electron?.splashScreenEnabled) {
|
||||||
|
this.SplashScreen.getSplashWindow().hide();
|
||||||
|
}
|
||||||
|
if (!this.CapacitorFileConfig.electron?.hideMainWindowOnLaunch) {
|
||||||
|
this.MainWindow.show();
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
if (electronIsDev) {
|
||||||
|
this.MainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
|
CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', '');
|
||||||
|
}, 400);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a CSP up for our application based on the custom scheme
|
||||||
|
export function setupContentSecurityPolicy(customScheme: string): void {
|
||||||
|
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||||
|
callback({
|
||||||
|
responseHeaders: {
|
||||||
|
...details.responseHeaders,
|
||||||
|
'Content-Security-Policy': [
|
||||||
|
"script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval'; object-src 'self'; connect-src 'self' https://*:* http://*:* wss://*:* ws://*:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
15
electron/tsconfig.json
Normal file
15
electron/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
|
"include": ["./src/**/*", "./capacitor.config.ts", "./capacitor.config.js"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./build",
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2017",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"typeRoots": ["./node_modules/@types"],
|
||||||
|
"allowJs": true,
|
||||||
|
"rootDir": "."
|
||||||
|
}
|
||||||
|
}
|
1857
package-lock.json
generated
1857
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,9 +9,13 @@
|
|||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"coverage": "vitest run --coverage"
|
"coverage": "vitest run --coverage",
|
||||||
|
"electron:mac": "electron-packager ./electron Qortal --overwrite --platform=darwin --arch=x64 --prune=true --out=release-builds",
|
||||||
|
"electron:win": "electron-packager ./electron Qortal --overwrite --asar=true --platform=win32 --arch=ia32 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName='Qortal Electron App'",
|
||||||
|
"electron:linux": "electron-packager ./electron Qortal --overwrite --platform=linux --arch=x64 --prune=true --out=release-builds --executable-name=qortal-app --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName='Qortal Electron App'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@capacitor-community/electron": "^5.0.1",
|
||||||
"@capacitor/android": "^6.1.2",
|
"@capacitor/android": "^6.1.2",
|
||||||
"@capacitor/browser": "^6.0.3",
|
"@capacitor/browser": "^6.0.3",
|
||||||
"@capacitor/cli": "^6.1.2",
|
"@capacitor/cli": "^6.1.2",
|
||||||
@ -77,6 +81,7 @@
|
|||||||
"tiptap-extension-resize-image": "^1.1.8"
|
"tiptap-extension-resize-image": "^1.1.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@electron/packager": "^18.3.5",
|
||||||
"@testing-library/dom": "^10.3.0",
|
"@testing-library/dom": "^10.3.0",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@types/dompurify": "^3.0.5",
|
"@types/dompurify": "^3.0.5",
|
||||||
|
39
src/App.tsx
39
src/App.tsx
@ -160,7 +160,7 @@ const defaultValues: MyContextInterface = {
|
|||||||
message: "",
|
message: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export let isMobile = true;
|
export let isMobile = false;
|
||||||
|
|
||||||
const isMobileDevice = () => {
|
const isMobileDevice = () => {
|
||||||
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
||||||
@ -1064,43 +1064,8 @@ function App() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleBeforeUnload = (e)=> {
|
|
||||||
// const shouldClose = confirm('Are you sure you want to close this window? You may have unsaved changes.');
|
|
||||||
|
|
||||||
// if (!shouldClose) {
|
|
||||||
// // Prevent the window from closing
|
|
||||||
// e.preventDefault();
|
|
||||||
// e.returnValue = ''; // Required for Chrome
|
|
||||||
// } else {
|
|
||||||
// // Allow the window to close
|
|
||||||
// // No need to call preventDefault here; returnValue must be left empty
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// useEffect(()=> {
|
|
||||||
// window.addEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
|
|
||||||
// return ()=> {
|
|
||||||
// window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
// }
|
|
||||||
// }, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isMainWindow || isMobile) return;
|
|
||||||
const handleBeforeUnload = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = ""; // This is required for Chrome to display the confirmation dialog.
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the event listener when the component mounts
|
|
||||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
||||||
|
|
||||||
// Clean up the event listener when the component unmounts
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMainWindow) return;
|
if (!isMainWindow) return;
|
||||||
|
@ -88,15 +88,15 @@ import {
|
|||||||
versionCase,
|
versionCase,
|
||||||
} from "./background-cases";
|
} from "./background-cases";
|
||||||
import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage";
|
import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage";
|
||||||
import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
|
// import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
|
||||||
import { LocalNotifications } from '@capacitor/local-notifications';
|
// import { LocalNotifications } from '@capacitor/local-notifications';
|
||||||
|
|
||||||
|
|
||||||
LocalNotifications.requestPermissions().then(permission => {
|
// LocalNotifications.requestPermissions().then(permission => {
|
||||||
if (permission.display === 'granted') {
|
// if (permission.display === 'granted') {
|
||||||
console.log("Notifications enabled");
|
// console.log("Notifications enabled");
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
export function cleanUrl(url) {
|
export function cleanUrl(url) {
|
||||||
@ -396,18 +396,18 @@ const handleNotificationDirect = async (directs) => {
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: `New Direct message! ${
|
// title: `New Direct message! ${
|
||||||
newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}`
|
// newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}`
|
||||||
}`,
|
// }`,
|
||||||
body: "You have received a new direct message",
|
// body: "You have received a new direct message",
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -428,16 +428,16 @@ const handleNotificationDirect = async (directs) => {
|
|||||||
|
|
||||||
const notificationId = "chat_notification_" + Date.now();
|
const notificationId = "chat_notification_" + Date.now();
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: `New Direct message!`,
|
// title: `New Direct message!`,
|
||||||
body: "You have received a new direct message",
|
// body: "You have received a new direct message",
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setChatHeadsDirect(dataDirects);
|
setChatHeadsDirect(dataDirects);
|
||||||
@ -610,16 +610,16 @@ const handleNotification = async (groups) => {
|
|||||||
"_type=group" +
|
"_type=group" +
|
||||||
`_from=${newestLatestTimestamp.groupId}`;
|
`_from=${newestLatestTimestamp.groupId}`;
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: "New Group Message!",
|
// title: "New Group Message!",
|
||||||
body: `You have received a new message from ${newestLatestTimestamp?.groupName}`,
|
// body: `You have received a new message from ${newestLatestTimestamp?.groupName}`,
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
chrome.notifications.clear(notificationId);
|
chrome.notifications.clear(notificationId);
|
||||||
@ -646,16 +646,16 @@ const handleNotification = async (groups) => {
|
|||||||
|
|
||||||
const notificationId = "chat_notification_" + Date.now();
|
const notificationId = "chat_notification_" + Date.now();
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: "New Group Message!",
|
// title: "New Group Message!",
|
||||||
body: "You have received a new message from one of your groups",
|
// body: "You have received a new message from one of your groups",
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
lastGroupNotification = Date.now();
|
lastGroupNotification = Date.now();
|
||||||
@ -2917,16 +2917,16 @@ export const checkNewMessages = async () => {
|
|||||||
`_from=${newAnnouncements[0]?.groupId}`;
|
`_from=${newAnnouncements[0]?.groupId}`;
|
||||||
|
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: "New group announcement!",
|
// title: "New group announcement!",
|
||||||
body: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`,
|
// body: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`,
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
const savedtimestampAfter = await getTimestampGroupAnnouncement();
|
const savedtimestampAfter = await getTimestampGroupAnnouncement();
|
||||||
window.postMessage({
|
window.postMessage({
|
||||||
@ -3063,16 +3063,16 @@ export const checkThreads = async (bringBack) => {
|
|||||||
let isDisableNotifications = await getUserSettings({key: 'disable-push-notifications'}) || false
|
let isDisableNotifications = await getUserSettings({key: 'disable-push-notifications'}) || false
|
||||||
if(!isDisableNotifications){
|
if(!isDisableNotifications){
|
||||||
|
|
||||||
LocalNotifications.schedule({
|
// LocalNotifications.schedule({
|
||||||
notifications: [
|
// notifications: [
|
||||||
{
|
// {
|
||||||
title: `New thread post!`,
|
// title: `New thread post!`,
|
||||||
body: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`,
|
// body: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`,
|
||||||
id: notificationId,
|
// id: notificationId,
|
||||||
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -3087,56 +3087,56 @@ export const checkThreads = async (bringBack) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Configure Background Fetch
|
// Configure Background Fetch
|
||||||
BackgroundFetch.configure({
|
// BackgroundFetch.configure({
|
||||||
minimumFetchInterval: 15, // Minimum 15-minute interval
|
// minimumFetchInterval: 15, // Minimum 15-minute interval
|
||||||
enableHeadless: true, // Enable headless mode for Android
|
// enableHeadless: true, // Enable headless mode for Android
|
||||||
}, async (taskId) => {
|
// }, async (taskId) => {
|
||||||
// This is where your background task logic goes
|
// // This is where your background task logic goes
|
||||||
const wallet = await getSaveWallet();
|
// const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
// const address = wallet.address0;
|
||||||
if (!address) return;
|
// if (!address) return;
|
||||||
checkActiveChatsForNotifications();
|
// checkActiveChatsForNotifications();
|
||||||
checkNewMessages();
|
// checkNewMessages();
|
||||||
checkThreads();
|
// checkThreads();
|
||||||
|
|
||||||
await new Promise((res)=> {
|
// await new Promise((res)=> {
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
res()
|
// res()
|
||||||
}, 55000);
|
// }, 55000);
|
||||||
})
|
// })
|
||||||
// Always finish the task when complete
|
// // Always finish the task when complete
|
||||||
BackgroundFetch.finish(taskId);
|
// BackgroundFetch.finish(taskId);
|
||||||
}, (taskId) => {
|
// }, (taskId) => {
|
||||||
// Optional timeout callback
|
// // Optional timeout callback
|
||||||
BackgroundFetch.finish(taskId);
|
// BackgroundFetch.finish(taskId);
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LocalNotifications.addListener('localNotificationActionPerformed', async (notification) => {
|
// LocalNotifications.addListener('localNotificationActionPerformed', async (notification) => {
|
||||||
const notificationId = notification.notification.id;
|
// const notificationId = notification.notification.id;
|
||||||
|
|
||||||
// Check the type of notification by parsing notificationId
|
// // Check the type of notification by parsing notificationId
|
||||||
const isDirect = notificationId.includes('_type=direct_');
|
// const isDirect = notificationId.includes('_type=direct_');
|
||||||
const isGroup = notificationId.includes('_type=group_');
|
// const isGroup = notificationId.includes('_type=group_');
|
||||||
const isGroupAnnouncement = notificationId.includes('_type=group-announcement_');
|
// const isGroupAnnouncement = notificationId.includes('_type=group-announcement_');
|
||||||
const isNewThreadPost = notificationId.includes('_type=thread-post_');
|
// const isNewThreadPost = notificationId.includes('_type=thread-post_');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle specific notification types
|
// // Handle specific notification types
|
||||||
if (isDirect) {
|
// if (isDirect) {
|
||||||
const fromValue = notificationId.split('_from=')[1];
|
// const fromValue = notificationId.split('_from=')[1];
|
||||||
handleDirectNotification(fromValue);
|
// handleDirectNotification(fromValue);
|
||||||
} else if (isGroup) {
|
// } else if (isGroup) {
|
||||||
const fromValue = notificationId.split('_from=')[1];
|
// const fromValue = notificationId.split('_from=')[1];
|
||||||
handleGroupNotification(fromValue);
|
// handleGroupNotification(fromValue);
|
||||||
} else if (isGroupAnnouncement) {
|
// } else if (isGroupAnnouncement) {
|
||||||
const fromValue = notificationId.split('_from=')[1];
|
// const fromValue = notificationId.split('_from=')[1];
|
||||||
handleAnnouncementNotification(fromValue);
|
// handleAnnouncementNotification(fromValue);
|
||||||
} else if (isNewThreadPost) {
|
// } else if (isNewThreadPost) {
|
||||||
const dataValue = notificationId.split('_data=')[1];
|
// const dataValue = notificationId.split('_data=')[1];
|
||||||
const dataParsed = JSON.parse(dataValue);
|
// const dataParsed = JSON.parse(dataValue);
|
||||||
handleThreadPostNotification(dataParsed);
|
// handleThreadPostNotification(dataParsed);
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user