Browse Source

docs: move build instructions from README to BUILDING.md

- Move detailed setup and build instructions from README.md to BUILDING.md
- Add concise reference to BUILDING.md in README.md
- Keep testing and other documentation in README.md
- Improve organization of documentation by separating build-specific content
Matthew Raymer 1 week ago
parent
commit
4e0f9235cd
  1. 74
      README.md
  2. 54
      src/electron/main.js
  3. 37
      src/electron/preload.js
  4. 15
      src/registerServiceWorker.ts
  5. 2
      src/router/index.ts
  6. 13
      src/vite.config.utils.js

74
README.md

@ -8,79 +8,9 @@ and expand to crowd-fund with time & money, then record and see the impact of co
See [project.task.yaml](project.task.yaml) for current priorities. See [project.task.yaml](project.task.yaml) for current priorities.
(Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.) (Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.)
## Setup ## Setup & Building
We like pkgx: `sh <(curl https://pkgx.sh) +vite sh`
```
npm install
```
### Compile and hot-reloads for development
```
npm run dev
```
See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or use http://localhost:3000 for local endorser.ch
### Build the test & production app
```
npm run serve
```
### Lint and fix files
```
npm run lint
```
### Run all UI tests
Look below for the "test-all" instructions.
### Compile and minify for test & production
* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations.
* `npx prettier --write ./sw_scripts/`
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`.
* Commit everything (since the commit hash is used the app).
* Put the commit hash in the changelog (which will help you remember to bump the version later).
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.36` && `git push origin 0.3.36`.
* For test, build the app (because test server is not yet set up to build):
```
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_PASSKEYS_ENABLED=true npm run build
```
... and transfer to the test server: `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari`
(Let's replace that with a .env.development or .env.staging file.)
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)
* For prod, get on the server and run the correct build:
... and log onto the server:
* `pkgx +npm sh`
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.36 && npm install && npm run build && cd -`
(The plain `npm run build` uses the .env.production file.)
* Back up the time-safari/dist folder, then `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`
* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production.
See [BUILDING.md](BUILDING.md) for detailed build and setup instructions.
## Tests ## Tests

54
src/electron/main.js

@ -3,13 +3,13 @@ const path = require("path");
const fs = require("fs"); const fs = require("fs");
// Check if running in dev mode // Check if running in dev mode
const isDev = process.argv.includes('--inspect'); const isDev = process.argv.includes("--inspect");
function createWindow() { function createWindow() {
// Add before createWindow function // Add before createWindow function
const preloadPath = path.join(__dirname, 'preload.js'); const preloadPath = path.join(__dirname, "preload.js");
console.log('Checking preload path:', preloadPath); console.log("Checking preload path:", preloadPath);
console.log('Preload exists:', fs.existsSync(preloadPath)); console.log("Preload exists:", fs.existsSync(preloadPath));
// Create the browser window. // Create the browser window.
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
@ -20,7 +20,7 @@ function createWindow() {
contextIsolation: true, contextIsolation: true,
webSecurity: true, webSecurity: true,
allowRunningInsecureContent: false, allowRunningInsecureContent: false,
preload: path.join(__dirname, 'preload.js') preload: path.join(__dirname, "preload.js"),
}, },
}); });
@ -33,30 +33,32 @@ function createWindow() {
console.log("__dirname:", __dirname); console.log("__dirname:", __dirname);
console.log("process.cwd():", process.cwd()); console.log("process.cwd():", process.cwd());
const indexPath = path.join(__dirname, 'www', 'index.html'); const indexPath = path.join(__dirname, "www", "index.html");
console.log("www path:", path.join(__dirname, 'www')); console.log("www path:", path.join(__dirname, "www"));
console.log("www assets path:", path.join(__dirname, 'www', 'assets')); console.log("www assets path:", path.join(__dirname, "www", "assets"));
if (!fs.existsSync(indexPath)) { if (!fs.existsSync(indexPath)) {
console.error(`Index file not found at: ${indexPath}`); console.error(`Index file not found at: ${indexPath}`);
throw new Error('Index file not found'); throw new Error("Index file not found");
} }
// Set CSP headers // Set CSP headers
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { mainWindow.webContents.session.webRequest.onHeadersReceived(
(details, callback) => {
callback({ callback({
responseHeaders: { responseHeaders: {
...details.responseHeaders, ...details.responseHeaders,
'Content-Security-Policy': [ "Content-Security-Policy": [
"default-src 'self';" + "default-src 'self';" +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" +
"font-src 'self' https://fonts.gstatic.com;" + "font-src 'self' https://fonts.gstatic.com;" +
"script-src 'self' 'unsafe-eval' 'unsafe-inline';" + "script-src 'self' 'unsafe-eval' 'unsafe-inline';" +
"img-src 'self' data: https:;" "img-src 'self' data: https:;",
] ],
} },
});
}); });
},
);
// Load the index.html // Load the index.html
mainWindow mainWindow
@ -79,17 +81,23 @@ function createWindow() {
}); });
// Add right after creating the BrowserWindow // Add right after creating the BrowserWindow
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => { mainWindow.webContents.on(
console.error('Page failed to load:', errorCode, errorDescription); "did-fail-load",
}); (event, errorCode, errorDescription) => {
console.error("Page failed to load:", errorCode, errorDescription);
},
);
mainWindow.webContents.on('preload-error', (event, preloadPath, error) => { mainWindow.webContents.on("preload-error", (event, preloadPath, error) => {
console.error('Preload script error:', preloadPath, error); console.error("Preload script error:", preloadPath, error);
}); });
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => { mainWindow.webContents.on(
console.log('Renderer Console:', message); "console-message",
}); (event, level, message, line, sourceId) => {
console.log("Renderer Console:", line, sourceId, message);
},
);
// Enable remote debugging when in dev mode // Enable remote debugging when in dev mode
if (isDev) { if (isDev) {

37
src/electron/preload.js

@ -1,39 +1,40 @@
const { contextBridge, ipcRenderer } = require('electron'); const { contextBridge, ipcRenderer } = require("electron");
// Use a more direct path resolution approach // Use a more direct path resolution approach
const getPath = (pathType) => { const getPath = (pathType) => {
switch(pathType) { switch (pathType) {
case 'userData': case "userData":
return process.env.APPDATA || ( return (
process.platform === 'darwin' process.env.APPDATA ||
(process.platform === "darwin"
? `${process.env.HOME}/Library/Application Support` ? `${process.env.HOME}/Library/Application Support`
: `${process.env.HOME}/.local/share` : `${process.env.HOME}/.local/share`)
); );
case 'home': case "home":
return process.env.HOME; return process.env.HOME;
case 'appPath': case "appPath":
return process.resourcesPath; return process.resourcesPath;
default: default:
return ''; return "";
} }
}; };
console.log('Preload script starting...'); console.log("Preload script starting...");
try { try {
contextBridge.exposeInMainWorld('electronAPI', { contextBridge.exposeInMainWorld("electronAPI", {
// Path utilities // Path utilities
getPath, getPath,
// IPC functions // IPC functions
send: (channel, data) => { send: (channel, data) => {
const validChannels = ['toMain']; const validChannels = ["toMain"];
if (validChannels.includes(channel)) { if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data); ipcRenderer.send(channel, data);
} }
}, },
receive: (channel, func) => { receive: (channel, func) => {
const validChannels = ['fromMain']; const validChannels = ["fromMain"];
if (validChannels.includes(channel)) { if (validChannels.includes(channel)) {
ipcRenderer.on(channel, (event, ...args) => func(...args)); ipcRenderer.on(channel, (event, ...args) => func(...args));
} }
@ -41,15 +42,15 @@ try {
// Environment info // Environment info
env: { env: {
isElectron: true, isElectron: true,
isDev: process.env.NODE_ENV === 'development' isDev: process.env.NODE_ENV === "development",
}, },
// Path utilities // Path utilities
getBasePath: () => { getBasePath: () => {
return process.env.NODE_ENV === 'development' ? '/' : './'; return process.env.NODE_ENV === "development" ? "/" : "./";
} },
}); });
console.log('Preload script completed successfully'); console.log("Preload script completed successfully");
} catch (error) { } catch (error) {
console.error('Error in preload script:', error); console.error("Error in preload script:", error);
} }

15
src/registerServiceWorker.ts

@ -3,7 +3,10 @@
import { register } from "register-service-worker"; import { register } from "register-service-worker";
// Only register service worker if explicitly enabled and in production // Only register service worker if explicitly enabled and in production
if (process.env.VITE_PWA_ENABLED === 'true' && process.env.NODE_ENV === "production") { if (
process.env.VITE_PWA_ENABLED === "true" &&
process.env.NODE_ENV === "production"
) {
register(`${process.env.BASE_URL}sw.js`, { register(`${process.env.BASE_URL}sw.js`, {
ready() { ready() {
console.log("Service worker is active."); console.log("Service worker is active.");
@ -21,12 +24,16 @@ if (process.env.VITE_PWA_ENABLED === 'true' && process.env.NODE_ENV === "product
console.log("New content is available; please refresh."); console.log("New content is available; please refresh.");
}, },
offline() { offline() {
console.log("No internet connection found. App is running in offline mode."); console.log(
"No internet connection found. App is running in offline mode.",
);
}, },
error(error) { error(error) {
console.error("Error during service worker registration:", error); console.error("Error during service worker registration:", error);
} },
}); });
} else { } else {
console.log("Service worker registration skipped - not enabled or not in production"); console.log(
"Service worker registration skipped - not enabled or not in production",
);
} }

2
src/router/index.ts

@ -286,7 +286,7 @@ const routes: Array<RouteRecordRaw> = [
const isElectron = window.location.protocol === "file:"; const isElectron = window.location.protocol === "file:";
const initialPath = isElectron const initialPath = isElectron
? window.location.pathname.split('/dist-electron/www/')[1] || '/' ? window.location.pathname.split("/dist-electron/www/")[1] || "/"
: window.location.pathname; : window.location.pathname;
const history = isElectron const history = isElectron

13
src/vite.config.utils.js

@ -1,6 +1,6 @@
import * as path from "path"; import * as path from "path";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import { fileURLToPath } from 'url'; import { fileURLToPath } from "url";
export async function loadAppConfig() { export async function loadAppConfig() {
const packageJson = await loadPackageJson(); const packageJson = await loadPackageJson();
@ -34,15 +34,16 @@ export async function loadAppConfig() {
sizes: "512x512", sizes: "512x512",
type: "image/png", type: "image/png",
purpose: "maskable", purpose: "maskable",
} },
] ],
} },
}, },
aliasConfig: { aliasConfig: {
"@": path.resolve(path.dirname(__dirname), "src"), "@": path.resolve(path.dirname(__dirname), "src"),
buffer: path.resolve(path.dirname(__dirname), "node_modules", "buffer"), buffer: path.resolve(path.dirname(__dirname), "node_modules", "buffer"),
"dexie-export-import/dist/import": "dexie-export-import/dist/import/index.js", "dexie-export-import/dist/import":
} "dexie-export-import/dist/import/index.js",
},
}; };
} }

Loading…
Cancel
Save