added electron

This commit is contained in:
PhilReact 2024-10-31 15:50:55 +02:00
parent a449bc7497
commit 839dd30ee1
22 changed files with 7826 additions and 167 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
release-builds/

8
electron/.gitignore vendored Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
electron/assets/appIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
electron/assets/splash.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
electron/assets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View 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;

View 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
View 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

File diff suppressed because it is too large Load Diff

41
electron/package.json Normal file
View 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"
]
}

View 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
View 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
View File

@ -0,0 +1,4 @@
require('./rt/electron-rt');
//////////////////////////////
// User Defined Preload scripts below
console.log('User Preload!');

View File

@ -0,0 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
module.exports = {
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,13 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"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": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor/android": "^6.1.2",
"@capacitor/browser": "^6.0.3",
"@capacitor/cli": "^6.1.2",
@ -77,6 +81,7 @@
"tiptap-extension-resize-image": "^1.1.8"
},
"devDependencies": {
"@electron/packager": "^18.3.5",
"@testing-library/dom": "^10.3.0",
"@testing-library/react": "^16.0.0",
"@types/dompurify": "^3.0.5",

View File

@ -160,7 +160,7 @@ const defaultValues: MyContextInterface = {
message: "",
},
};
export let isMobile = true;
export let isMobile = false;
const isMobileDevice = () => {
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(() => {
if (!isMainWindow) return;

View File

@ -88,15 +88,15 @@ import {
versionCase,
} from "./background-cases";
import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage";
import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
import { LocalNotifications } from '@capacitor/local-notifications';
// import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch';
// import { LocalNotifications } from '@capacitor/local-notifications';
LocalNotifications.requestPermissions().then(permission => {
if (permission.display === 'granted') {
console.log("Notifications enabled");
}
});
// LocalNotifications.requestPermissions().then(permission => {
// if (permission.display === 'granted') {
// console.log("Notifications enabled");
// }
// });
export function cleanUrl(url) {
@ -396,18 +396,18 @@ const handleNotificationDirect = async (directs) => {
) {
LocalNotifications.schedule({
notifications: [
{
title: `New Direct message! ${
newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}`
}`,
body: "You have received a new direct message",
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: `New Direct message! ${
// newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}`
// }`,
// body: "You have received a new direct message",
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
}
} catch (error) {
@ -428,16 +428,16 @@ const handleNotificationDirect = async (directs) => {
const notificationId = "chat_notification_" + Date.now();
LocalNotifications.schedule({
notifications: [
{
title: `New Direct message!`,
body: "You have received a new direct message",
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: `New Direct message!`,
// body: "You have received a new direct message",
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
}
} finally {
setChatHeadsDirect(dataDirects);
@ -610,16 +610,16 @@ const handleNotification = async (groups) => {
"_type=group" +
`_from=${newestLatestTimestamp.groupId}`;
LocalNotifications.schedule({
notifications: [
{
title: "New Group Message!",
body: `You have received a new message from ${newestLatestTimestamp?.groupName}`,
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: "New Group Message!",
// body: `You have received a new message from ${newestLatestTimestamp?.groupName}`,
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
if (!isMobile) {
setTimeout(() => {
chrome.notifications.clear(notificationId);
@ -646,16 +646,16 @@ const handleNotification = async (groups) => {
const notificationId = "chat_notification_" + Date.now();
LocalNotifications.schedule({
notifications: [
{
title: "New Group Message!",
body: "You have received a new message from one of your groups",
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: "New Group Message!",
// body: "You have received a new message from one of your groups",
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
lastGroupNotification = Date.now();
@ -2917,16 +2917,16 @@ export const checkNewMessages = async () => {
`_from=${newAnnouncements[0]?.groupId}`;
LocalNotifications.schedule({
notifications: [
{
title: "New group announcement!",
body: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`,
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: "New group announcement!",
// body: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`,
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
}
const savedtimestampAfter = await getTimestampGroupAnnouncement();
window.postMessage({
@ -3063,16 +3063,16 @@ export const checkThreads = async (bringBack) => {
let isDisableNotifications = await getUserSettings({key: 'disable-push-notifications'}) || false
if(!isDisableNotifications){
LocalNotifications.schedule({
notifications: [
{
title: `New thread post!`,
body: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`,
id: notificationId,
schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
}
]
});
// LocalNotifications.schedule({
// notifications: [
// {
// title: `New thread post!`,
// body: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`,
// id: notificationId,
// schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now
// }
// ]
// });
}
}
@ -3087,56 +3087,56 @@ export const checkThreads = async (bringBack) => {
};
// Configure Background Fetch
BackgroundFetch.configure({
minimumFetchInterval: 15, // Minimum 15-minute interval
enableHeadless: true, // Enable headless mode for Android
}, async (taskId) => {
// This is where your background task logic goes
const wallet = await getSaveWallet();
const address = wallet.address0;
if (!address) return;
checkActiveChatsForNotifications();
checkNewMessages();
checkThreads();
// BackgroundFetch.configure({
// minimumFetchInterval: 15, // Minimum 15-minute interval
// enableHeadless: true, // Enable headless mode for Android
// }, async (taskId) => {
// // This is where your background task logic goes
// const wallet = await getSaveWallet();
// const address = wallet.address0;
// if (!address) return;
// checkActiveChatsForNotifications();
// checkNewMessages();
// checkThreads();
await new Promise((res)=> {
setTimeout(() => {
res()
}, 55000);
})
// Always finish the task when complete
BackgroundFetch.finish(taskId);
}, (taskId) => {
// Optional timeout callback
BackgroundFetch.finish(taskId);
});
// await new Promise((res)=> {
// setTimeout(() => {
// res()
// }, 55000);
// })
// // Always finish the task when complete
// BackgroundFetch.finish(taskId);
// }, (taskId) => {
// // Optional timeout callback
// BackgroundFetch.finish(taskId);
// });
LocalNotifications.addListener('localNotificationActionPerformed', async (notification) => {
const notificationId = notification.notification.id;
// LocalNotifications.addListener('localNotificationActionPerformed', async (notification) => {
// const notificationId = notification.notification.id;
// Check the type of notification by parsing notificationId
const isDirect = notificationId.includes('_type=direct_');
const isGroup = notificationId.includes('_type=group_');
const isGroupAnnouncement = notificationId.includes('_type=group-announcement_');
const isNewThreadPost = notificationId.includes('_type=thread-post_');
// // Check the type of notification by parsing notificationId
// const isDirect = notificationId.includes('_type=direct_');
// const isGroup = notificationId.includes('_type=group_');
// const isGroupAnnouncement = notificationId.includes('_type=group-announcement_');
// const isNewThreadPost = notificationId.includes('_type=thread-post_');
// Handle specific notification types
if (isDirect) {
const fromValue = notificationId.split('_from=')[1];
handleDirectNotification(fromValue);
} else if (isGroup) {
const fromValue = notificationId.split('_from=')[1];
handleGroupNotification(fromValue);
} else if (isGroupAnnouncement) {
const fromValue = notificationId.split('_from=')[1];
handleAnnouncementNotification(fromValue);
} else if (isNewThreadPost) {
const dataValue = notificationId.split('_data=')[1];
const dataParsed = JSON.parse(dataValue);
handleThreadPostNotification(dataParsed);
}
});
// // Handle specific notification types
// if (isDirect) {
// const fromValue = notificationId.split('_from=')[1];
// handleDirectNotification(fromValue);
// } else if (isGroup) {
// const fromValue = notificationId.split('_from=')[1];
// handleGroupNotification(fromValue);
// } else if (isGroupAnnouncement) {
// const fromValue = notificationId.split('_from=')[1];
// handleAnnouncementNotification(fromValue);
// } else if (isNewThreadPost) {
// const dataValue = notificationId.split('_data=')[1];
// const dataParsed = JSON.parse(dataValue);
// handleThreadPostNotification(dataParsed);
// }
// });