DarkGuy10 / NextJS-Electron-Boilerplate

A boilerplate for building desktop apps with Electron and NextJS.
MIT License
50 stars 12 forks source link

Pages routing doesnt work? #95

Open ShiNxz opened 6 months ago

ShiNxz commented 6 months ago

After building the app and starting the built exe file and clicking on a link component, the app goes into a white blank screen.

DarkGuy10 commented 4 months ago

Alright so, this pushed me into an unexpectedly deep rabbit hole.

I've tried a lot of workarounds in the past 7 hours and here are the conclusions:

Im keeping this issue open, any help is appreciated.

quyle1222 commented 4 months ago

I have same issue

huzixuanH commented 4 months ago

I tried using https://github.com/HaNdTriX/next-electron-server or https://github.com/sindresorhus/electron-serve and it works fine.

electron-serve

  1. yarn add electron-serve
  2. update main.ts
    
    import serve from 'electron-serve';
    const loadURL = serve({directory: 'frontend/build'});
    // other...

if (electronIsDev) { appWindow.loadURL("http://localhost:3000"); } else { loadURL(appWindow); }

## next-electron-server
1. ` yarn add  next-electron-server`
2. update main.ts
```ts
import serveNextAt from "next-electron-server";
 serveNextAt("next://app", {"outputDir": path.join("frontend/build")});
// other...

// appWindow.loadURL(
//  electronIsDev
//      ? "http://localhost:3000"
//      : `file://${path.join(__dirname, "../../frontend/build/index.html")}`
// );
appWindow.loadURL("next://app");

Of course I only did a simple test.

ppipada commented 2 months ago

@ShiNxz @DarkGuy10 I went through this recently and below are my findings and solution in Next 14.2.5 :

Issues:

Solution (tested on Ubuntu 24.04 only):

import { BrowserWindow, CallbackResponse, OnBeforeRequestListenerDetails, app, ipcMain, session } from 'electron';
import electronIsDev from 'electron-is-dev';
import log from 'electron-log';
import electronUpdater from 'electron-updater';
import path from 'node:path';
import { dirname } from 'path';
import { fileURLToPath, format as urlformat } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const FRONTEND_PATH_PREFIX = '/frontend/build';
const PUBLIC_FILES_PATHS = ['/icon.png', '/favicon.ico'];
const HANDLE_FILES_PREFIXES = [`file://${FRONTEND_PATH_PREFIX}`, ...PUBLIC_FILES_PATHS.map(path => `file://${path}`)];

const getActualURL = (origurl: string) => {
    let callurl = origurl;

    if (HANDLE_FILES_PREFIXES.some(prefix => callurl.startsWith(prefix))) {
        // Remove the file://
        let actualURL = callurl.substring(7);
        // For public files add the frontend prefix
        if (PUBLIC_FILES_PATHS.some(pfile => actualURL === pfile)) {
            actualURL = FRONTEND_PATH_PREFIX + actualURL;
        }
        // Handle if its a page request
        if (actualURL.endsWith('/')) {
            actualURL += 'index.html';
        }
        // Create a absolute url from the actual url
        callurl = urlformat({
            pathname: path.join(__dirname, `../..${actualURL}`),
            protocol: 'file:',
            slashes: true,
        });
    }
    // console.log(`Input URL: ${origurl} Callpath: ${callurl}`);
    return callurl;
};

const handleAccessRequest = (
    details: OnBeforeRequestListenerDetails,
    callback: (response: CallbackResponse) => void
) => {
    const callurl = getActualURL(details.url);
    if (callurl !== details.url) {
        callback({ redirectURL: callurl });
    } else {
        callback({});
    }
};

app.on('ready', async () => {
    // new AppUpdater();
    spawnAppWindow();
    session.defaultSession.webRequest.onBeforeRequest(handleAccessRequest);
});
const interceptFileProtocol = () => {
    protocol.interceptFileProtocol('file', (request, callback) => {
        const callurl = getActualURL(request.url);
        callback({ path: callurl.substring(8) });
    });
};

const handleFileProtocol = () => {
    protocol.handle('file', (request: Request) => {
        const callurl = getActualURL(request.url);
        // fetch the new path, without reinvoking this handler
        return net.fetch(callurl, { bypassCustomProtocolHandlers: true });
    });
};