react-native-electron-platform 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/main.mjs DELETED
@@ -1,662 +0,0 @@
1
- import {
2
- app,
3
- BrowserWindow,
4
- session,
5
- screen,
6
- ipcMain,
7
- dialog,
8
- clipboard,
9
- shell
10
- } from "electron";
11
- import electronUpdater from "electron-updater";
12
- const { autoUpdater } = electronUpdater;
13
- import path from "path";
14
- import fs from "fs";
15
- import { Readable } from "stream";
16
- import os from "os";
17
- import { fileURLToPath } from 'url';
18
- import { dirname } from 'path';
19
- import packageJson from "../../../package.json" with { type: 'json' };
20
-
21
- const __dirname = dirname(fileURLToPath(import.meta.url));
22
-
23
- // ======================================================
24
- // AUTO SAFE MODE DETECTION
25
- // ======================================================
26
-
27
- function isRunningFromNetwork() {
28
- try {
29
- const exePath = app.getPath("exe");
30
- return exePath.startsWith("\\\\"); // UNC path
31
- } catch {
32
- return false;
33
- }
34
- }
35
-
36
- function shouldUseSafeMode() {
37
- const network = isRunningFromNetwork();
38
- const hostname = os.hostname().toLowerCase();
39
- const vmHint =
40
- hostname.includes("vm") ||
41
- hostname.includes("virtual") ||
42
- hostname.includes("vbox") ||
43
- hostname.includes("hyper");
44
-
45
- return network || vmHint;
46
- }
47
-
48
- // Apply BEFORE Electron ready
49
- if (shouldUseSafeMode()) {
50
- console.log("⚠ SAFE MODE ENABLED");
51
- app.disableHardwareAcceleration();
52
- app.commandLine.appendSwitch("disable-gpu");
53
- app.commandLine.appendSwitch("disable-software-rasterizer");
54
- app.commandLine.appendSwitch("no-sandbox");
55
- app.commandLine.appendSwitch("disable-dev-shm-usage");
56
- } else {
57
- console.log("✅ NORMAL MODE");
58
- }
59
-
60
- let mainWindow;
61
-
62
- // ======================================================
63
- // DEEP LINKING / URL HANDLING
64
- // ======================================================
65
-
66
- const appOpenURLTargets = new WeakSet();
67
-
68
- function sendOpenURL(url) {
69
- for (const window of BrowserWindow.getAllWindows()) {
70
- if (appOpenURLTargets.has(window.webContents)) {
71
- window.webContents.send('react-native-app-open-url', url);
72
- }
73
- }
74
- }
75
-
76
- // ======================================================
77
- // REGISTER ALL IPC HANDLERS
78
- // ======================================================
79
-
80
- function registerIpcHandlers() {
81
- console.log("📝 Registering IPC handlers...");
82
-
83
- // Deep Linking Handlers
84
- ipcMain.handle('react-native-add-app-open-url', (event) => {
85
- appOpenURLTargets.add(event.sender);
86
- });
87
-
88
- ipcMain.handle('react-native-get-initial-url', () => {
89
- return Promise.resolve(process.argv[1]);
90
- });
91
-
92
- ipcMain.handle('react-native-open-url', async (_event, url) => {
93
- await shell.openExternal(url);
94
- });
95
-
96
- // Clipboard Handlers
97
- ipcMain.handle('react-native-get-clipboard-text', async () => {
98
- return await clipboard.readText();
99
- });
100
-
101
- ipcMain.handle('react-native-set-clipboard-text', async (_event, text) => {
102
- await clipboard.writeText(text);
103
- });
104
-
105
- // Dialog Handlers
106
- ipcMain.handle('react-native-show-alert', async (event, options) => {
107
- const window = BrowserWindow.fromWebContents(event.sender);
108
- if (window != null) {
109
- const { response } = await dialog.showMessageBox(window, options);
110
- return response;
111
- }
112
- });
113
-
114
- // Internal support check
115
- ipcMain.on('react-native-supported', (event) => {
116
- event.returnValue = true;
117
- });
118
-
119
- // Auto-updater Handlers
120
- ipcMain.handle("check-for-updates", async () => {
121
- if (app.isPackaged) {
122
- autoUpdater.checkForUpdates();
123
- return { status: "checking" };
124
- }
125
- return { status: "disabled", message: "Auto-update disabled in development" };
126
- });
127
-
128
- ipcMain.handle("get-app-version", () => {
129
- return app.getVersion();
130
- });
131
-
132
- // Network Service Handlers
133
- ipcMain.handle("network-call", async (event, payload) => {
134
- try {
135
- const { method, url, params, headers } = payload;
136
- console.log("IPC network-call:", { method, url });
137
- return await NetworkServiceCall(method, url, params, headers);
138
- } catch (err) {
139
- console.error("IPC network-call error:", err);
140
- return {
141
- httpstatus: 500,
142
- data: { title: "ERROR", message: err.message },
143
- };
144
- }
145
- });
146
-
147
- // PDF Operations Handlers
148
- ipcMain.handle("save-pdf", async (event, html) => {
149
- try {
150
- console.log("IPC save-pdf called");
151
- const tempWin = new BrowserWindow({
152
- show: false,
153
- webPreferences: { offscreen: true },
154
- });
155
-
156
- await tempWin.loadURL(
157
- `data:text/html;charset=utf-8,${encodeURIComponent(html)}`
158
- );
159
-
160
- const pdfBuffer = await tempWin.webContents.printToPDF({});
161
- tempWin.destroy();
162
-
163
- const { filePath } = await dialog.showSaveDialog({
164
- title: "Save PDF",
165
- defaultPath: "document.pdf",
166
- filters: [{ name: "PDF", extensions: ["pdf"] }],
167
- });
168
-
169
- if (filePath) {
170
- fs.writeFileSync(filePath, pdfBuffer);
171
- console.log("PDF saved:", filePath);
172
- return { status: "saved", path: filePath };
173
- }
174
-
175
- return { status: "cancelled" };
176
- } catch (err) {
177
- console.error("IPC save-pdf error:", err);
178
- return { status: "error", message: err.message };
179
- }
180
- });
181
-
182
- ipcMain.handle("post-pdf-preview", async (event, payload) => {
183
- try {
184
- const { url, data, headers = {} } = payload;
185
- console.log("IPC post-pdf-preview:", { url });
186
-
187
- const res = await fetch(url, {
188
- method: "POST",
189
- headers: {
190
- "Content-Type": "application/json",
191
- Accept: "application/pdf",
192
- ...headers,
193
- },
194
- body: data,
195
- });
196
-
197
- if (!res.ok) {
198
- throw new Error(`HTTP ${res.status}`);
199
- }
200
-
201
- const fileName = `Report_${Date.now()}.pdf`;
202
- const tempPath = path.join(app.getPath("temp"), fileName);
203
-
204
- const nodeStream = Readable.fromWeb(res.body);
205
- await new Promise((resolve, reject) => {
206
- const fileStream = fs.createWriteStream(tempPath);
207
- nodeStream.pipe(fileStream);
208
- nodeStream.on("error", reject);
209
- fileStream.on("finish", resolve);
210
- });
211
-
212
- return {
213
- status: "ok",
214
- path: `file://${tempPath}`,
215
- };
216
- } catch (err) {
217
- console.error("post-pdf-preview error:", err);
218
- return {
219
- status: "error",
220
- message: err.message,
221
- };
222
- }
223
- });
224
-
225
- ipcMain.handle("open-pdf-preview", async (_, pdfUrl) => {
226
- try {
227
- const res = await fetch(pdfUrl);
228
- const buffer = Buffer.from(await res.arrayBuffer());
229
-
230
- const tempPath = path.join(app.getPath("temp"), `preview_${Date.now()}.pdf`);
231
- fs.writeFileSync(tempPath, buffer);
232
-
233
- return `file://${tempPath}`;
234
- } catch (err) {
235
- console.error("open-pdf-preview error:", err);
236
- throw err;
237
- }
238
- });
239
-
240
- // File Operations Handlers
241
- ipcMain.handle("select-file", async () => {
242
- try {
243
- const result = await dialog.showOpenDialog({
244
- title: "Select a file",
245
- properties: ["openFile"],
246
- filters: [
247
- { name: "All Files", extensions: ["*"] },
248
- ],
249
- });
250
-
251
- if (result.canceled) {
252
- return {
253
- status: "cancelled",
254
- filePath: null,
255
- };
256
- }
257
-
258
- return {
259
- status: "selected",
260
- filePath: result.filePaths[0],
261
- };
262
- } catch (err) {
263
- console.error("select-file error:", err);
264
- return {
265
- status: "error",
266
- message: err.message,
267
- };
268
- }
269
- });
270
-
271
- // HTML Preview Handlers
272
- ipcMain.handle("preview-html", async (event, htmlContent) => {
273
- try {
274
- const previewWin = new BrowserWindow({
275
- width: 800,
276
- height: 600,
277
- show: false,
278
- webPreferences: {
279
- contextIsolation: true,
280
- sandbox: false,
281
- nodeIntegration: false,
282
- },
283
- });
284
-
285
- await previewWin.loadURL(
286
- `data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`
287
- );
288
-
289
- previewWin.show();
290
- return { status: "ok" };
291
- } catch (err) {
292
- console.error("preview-html error:", err);
293
- return { status: "error", message: err.message };
294
- }
295
- });
296
-
297
- ipcMain.handle("html-to-pdf-preview", async (event, htmlContent) => {
298
- try {
299
- const pdfPath = await convertHtmlToPdfPreview(htmlContent);
300
- return { status: "ok", path: pdfPath };
301
- } catch (err) {
302
- console.error("html-to-pdf-preview error:", err);
303
- return { status: "error", message: err.message };
304
- }
305
- });
306
-
307
- // Utility Handlers
308
- ipcMain.handle("get-platform", () => {
309
- return process.platform;
310
- });
311
-
312
- ipcMain.handle("get-app-path", () => {
313
- return app.getAppPath();
314
- });
315
-
316
- ipcMain.handle("get-user-data-path", () => {
317
- return app.getPath("userData");
318
- });
319
-
320
- console.log("✅ All IPC handlers registered");
321
- }
322
-
323
- // ======================================================
324
- // NETWORK SERVICE
325
- // ======================================================
326
-
327
- async function NetworkServiceCall(method, url, params = {}, headers = {}) {
328
- try {
329
- const upperMethod = method.toUpperCase();
330
- const options = {
331
- method: upperMethod,
332
- headers: {
333
- "Content-Type": "application/json",
334
- ...headers,
335
- },
336
- };
337
-
338
- if (upperMethod !== "GET") {
339
- options.body = JSON.stringify(params);
340
- } else if (params && Object.keys(params).length > 0) {
341
- const query = new URLSearchParams(params).toString();
342
- url += `?${query}`;
343
- }
344
-
345
- const response = await fetch(url, options);
346
- const data = await response.json().catch(() => ({}));
347
-
348
- if (response.ok || data?.httpstatus === 200) {
349
- return { httpstatus: 200, data: data?.data || data };
350
- }
351
-
352
- return {
353
- httpstatus: response.status,
354
- data: { title: "ERROR", message: data?.message || "Network Error" },
355
- };
356
- } catch (err) {
357
- return {
358
- httpstatus: 404,
359
- data: { title: "ERROR", message: err.message },
360
- };
361
- }
362
- }
363
-
364
- // ======================================================
365
- // PDF CONVERSION HELPER
366
- // ======================================================
367
-
368
- async function convertHtmlToPdfPreview(htmlContent, options = {}) {
369
- try {
370
- const tempWin = new BrowserWindow({
371
- show: false,
372
- webPreferences: {
373
- offscreen: true,
374
- contextIsolation: true,
375
- nodeIntegration: false,
376
- },
377
- });
378
-
379
- await tempWin.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
380
-
381
- const pdfBuffer = await tempWin.webContents.printToPDF({
382
- printBackground: true,
383
- ...options,
384
- });
385
-
386
- tempWin.destroy();
387
-
388
- const pdfFileName = `Preview_${Date.now()}.pdf`;
389
- const pdfPath = path.join(app.getPath("temp"), pdfFileName);
390
- fs.writeFileSync(pdfPath, pdfBuffer);
391
-
392
- const previewWin = new BrowserWindow({
393
- width: 900,
394
- height: 700,
395
- show: true,
396
- webPreferences: {
397
- contextIsolation: true,
398
- nodeIntegration: false,
399
- },
400
- });
401
-
402
- await previewWin.loadURL(`file://${pdfPath}`);
403
- return pdfPath;
404
- } catch (err) {
405
- console.error("convertHtmlToPdfPreview error:", err);
406
- throw err;
407
- }
408
- }
409
-
410
- // ======================================================
411
- // AUTO UPDATER SETUP
412
- // ======================================================
413
-
414
- function setupAutoUpdater() {
415
- console.log("Current app version:", app.getVersion());
416
-
417
- if (!app.isPackaged) {
418
- console.log("Auto-update disabled in development.");
419
- return;
420
- }
421
-
422
- autoUpdater.autoDownload = true;
423
- autoUpdater.autoInstallOnAppQuit = true;
424
-
425
- const sendStatus = (data) => {
426
- if (mainWindow) {
427
- mainWindow.webContents.send("update-status", data);
428
- }
429
- };
430
-
431
- autoUpdater.on("checking-for-update", () => {
432
- console.log("Checking for updates...");
433
- sendStatus({ status: "checking" });
434
- });
435
-
436
- autoUpdater.on("update-available", (info) => {
437
- console.log("Update available:", info.version);
438
- sendStatus({ status: "available", version: info.version });
439
- });
440
-
441
- autoUpdater.on("update-not-available", () => {
442
- console.log("No updates available");
443
- sendStatus({ status: "not-available" });
444
- });
445
-
446
- autoUpdater.on("download-progress", (progress) => {
447
- console.log(`Download progress: ${Math.round(progress.percent)}%`);
448
- sendStatus({
449
- status: "downloading",
450
- percent: Math.round(progress.percent),
451
- });
452
- });
453
-
454
- autoUpdater.on("update-downloaded", () => {
455
- console.log("Update downloaded");
456
- sendStatus({ status: "downloaded" });
457
-
458
- dialog
459
- .showMessageBox(mainWindow, {
460
- type: "info",
461
- title: "Update Ready",
462
- message: "A new version has been downloaded. Restart now?",
463
- buttons: ["Restart", "Later"],
464
- })
465
- .then((result) => {
466
- if (result.response === 0) {
467
- autoUpdater.quitAndInstall();
468
- }
469
- });
470
- });
471
-
472
- autoUpdater.on("error", (err) => {
473
- console.error("Auto-updater error:", err);
474
- sendStatus({ status: "error", message: err.message });
475
- });
476
-
477
- // Check for updates after a short delay to ensure window is ready
478
- setTimeout(() => {
479
- autoUpdater.checkForUpdates();
480
- }, 3000);
481
- }
482
-
483
- // ======================================================
484
- // WINDOW CREATION
485
- // ======================================================
486
-
487
- function createWindow() {
488
- const primaryDisplay = screen.getPrimaryDisplay();
489
- const { x, y, width, height } = primaryDisplay.bounds;
490
-
491
- const iconPath = path.join(app.getAppPath(), "electron/icon.ico");
492
- const preloadPath = path.join(__dirname, "preload.mjs");
493
-
494
- mainWindow = new BrowserWindow({
495
- x,
496
- y,
497
- width,
498
- height,
499
- show: false,
500
- icon: iconPath,
501
- frame: true,
502
- webPreferences: {
503
- preload: preloadPath,
504
- nodeIntegration: false,
505
- contextIsolation: true,
506
- sandbox: false,
507
- webSecurity: false,
508
- disableBlinkFeatures: "AutoLoadIconsForPage",
509
- nativeWindowOpen: true,
510
- spellcheck: true,
511
- },
512
- });
513
-
514
- mainWindow.removeMenu();
515
- mainWindow.maximize();
516
- mainWindow.show();
517
-
518
- mainWindow.on("resize", () => {
519
- const bounds = mainWindow.getBounds();
520
- if (bounds?.width && bounds?.height) {
521
- console.log(`Window resized: ${bounds.width}x${bounds.height}`);
522
- }
523
- });
524
-
525
- // CORS handling
526
- session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
527
- const responseHeaders = { ...details.responseHeaders };
528
-
529
- delete responseHeaders["access-control-allow-origin"];
530
- delete responseHeaders["access-control-allow-headers"];
531
- delete responseHeaders["access-control-allow-methods"];
532
- delete responseHeaders["Access-Control-Allow-Origin"];
533
- delete responseHeaders["Access-Control-Allow-Headers"];
534
- delete responseHeaders["Access-Control-Allow-Methods"];
535
-
536
- callback({
537
- responseHeaders: {
538
- ...responseHeaders,
539
- "Access-Control-Allow-Origin": ["*"],
540
- "Access-Control-Allow-Headers": ["*"],
541
- "Access-Control-Allow-Methods": ["GET, POST, PUT, DELETE, OPTIONS"],
542
- },
543
- });
544
- });
545
-
546
- const isDev =
547
- process.argv.includes("--enable-remote-module") ||
548
- process.env.NODE_ENV === "development";
549
-
550
- if (isDev) {
551
- mainWindow.loadURL("http://localhost:5001");
552
- console.log("DEV MODE: http://localhost:5001");
553
-
554
- // Dev tools shortcuts
555
- mainWindow.webContents.on("before-input-event", (event, input) => {
556
- if (input.control && input.shift && input.key.toLowerCase() === "i") {
557
- mainWindow.webContents.toggleDevTools();
558
- event.preventDefault();
559
- } else if (input.key === "F12") {
560
- mainWindow.webContents.toggleDevTools();
561
- event.preventDefault();
562
- } else if (
563
- input.key === "F5" ||
564
- (input.control && input.key.toLowerCase() === "r")
565
- ) {
566
- mainWindow.webContents.reload();
567
- event.preventDefault();
568
- }
569
- });
570
- } else {
571
- const possiblePaths = [
572
- path.join(__dirname, "web-build/index.html"),
573
- path.join(__dirname, "../web-build/index.html"),
574
- path.join(__dirname, "../../web-build/index.html"),
575
- path.join(app.getAppPath(), "web-build/index.html"),
576
- ];
577
-
578
- let indexPath = null;
579
- for (const p of possiblePaths) {
580
- if (fs.existsSync(p)) {
581
- indexPath = p;
582
- break;
583
- }
584
- }
585
-
586
- console.log("Loading:", indexPath);
587
-
588
- if (!indexPath) {
589
- dialog.showErrorBox(
590
- "Application Error",
591
- `web-build/index.html not found. Tried: ${possiblePaths.join(", ")}`
592
- );
593
- app.quit();
594
- return;
595
- }
596
-
597
- mainWindow.loadFile(indexPath);
598
- }
599
-
600
- mainWindow.webContents.on("did-finish-load", () => {
601
- console.log("Page loaded successfully");
602
- });
603
- }
604
-
605
- // ======================================================
606
- // REGISTER IPC HANDLERS BEFORE APP READY
607
- // ======================================================
608
-
609
- registerIpcHandlers();
610
-
611
- // ======================================================
612
- // APP LIFECYCLE
613
- // ======================================================
614
-
615
- app.whenReady().then(() => {
616
- if (process.platform === "win32") {
617
- app.setAppUserModelId(`com.${packageJson.name}.desktop`);
618
- }
619
- createWindow();
620
- setupAutoUpdater();
621
-
622
- // Log all registered IPC handlers for debugging
623
- console.log("📊 IPC Handlers registered:",
624
- ipcMain.eventNames().filter(name =>
625
- typeof name === 'string' &&
626
- !name.startsWith('ELECTRON_')
627
- )
628
- );
629
- });
630
-
631
- app.on("window-all-closed", () => {
632
- if (process.platform !== "darwin") {
633
- app.quit();
634
- }
635
- });
636
-
637
- app.on("activate", () => {
638
- if (BrowserWindow.getAllWindows().length === 0) {
639
- createWindow();
640
- }
641
- });
642
-
643
- // Handle second instance (for single instance apps)
644
- const gotTheLock = app.requestSingleInstanceLock();
645
-
646
- if (!gotTheLock) {
647
- app.quit();
648
- } else {
649
- app.on('second-instance', (event, commandLine, workingDirectory) => {
650
- // Someone tried to run a second instance, focus our window instead
651
- if (mainWindow) {
652
- if (mainWindow.isMinimized()) mainWindow.restore();
653
- mainWindow.focus();
654
-
655
- // Handle deep link from second instance
656
- const url = commandLine.pop();
657
- if (url && (url.startsWith('myapp://') || url.startsWith('http'))) {
658
- sendOpenURL(url);
659
- }
660
- }
661
- });
662
- }