Multi-build support; tested successfully for Electron

This commit is contained in:
Matthew Raymer
2025-01-07 09:40:31 +00:00
parent be8ba12df6
commit a32c3c7765
80 changed files with 29072 additions and 24052 deletions

View File

@@ -319,107 +319,202 @@
<script lang="ts">
import { Vue, Component } from "vue-facing-decorator";
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "./db/index";
import { NotificationIface } from "./constants/app";
interface Settings {
notifyingNewActivityTime?: string;
notifyingReminderTime?: string;
}
@Component
export default class App extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
stopAsking = false;
async turnOffNotifications(notification: NotificationIface) {
let subscription: object | null = null;
created() {
console.log(
"Component created: Reactivity set up.",
window.location.pathname,
);
}
beforeCreate() {
console.log("Component beforeCreate: Instance initialized.");
}
beforeMount() {
console.log("Component beforeMount: Template is about to be rendered.");
}
mounted() {
console.log("Component mounted: Template is now rendered.");
}
beforeUpdate() {
console.log("Component beforeUpdate: DOM is about to be updated.");
}
updated() {
console.log("Component updated: DOM has been updated.");
}
beforeUnmount() {
console.log("Component beforeUnmount: Cleaning up before removal.");
}
unmounted() {
console.log("Component unmounted: Component removed from the DOM.");
}
async turnOffNotifications(
notification: NotificationIface,
): Promise<boolean> {
console.log("Starting turnOffNotifications...");
let subscription: PushSubscriptionJSON | null = null;
let allGoingOff = false;
const settings = await retrieveSettingsForActiveAccount();
const notifyingNewActivity = !!settings?.notifyingNewActivityTime;
const notifyingReminder = !!settings?.notifyingReminderTime;
if (!notifyingNewActivity || !notifyingReminder) {
// the other notification is already off, so fully unsubscribe now
allGoingOff = true;
}
await navigator.serviceWorker?.ready
.then((registration) => {
return registration.pushManager.getSubscription();
})
.then(async (subscript: PushSubscription | null) => {
if (subscript) {
subscription = subscript.toJSON();
if (allGoingOff) {
await subscript.unsubscribe();
try {
console.log("Retrieving settings for the active account...");
const settings: Settings = await retrieveSettingsForActiveAccount();
console.log("Retrieved settings:", settings);
const notifyingNewActivity = !!settings?.notifyingNewActivityTime;
const notifyingReminder = !!settings?.notifyingReminderTime;
if (!notifyingNewActivity || !notifyingReminder) {
allGoingOff = true;
console.log("Both notifications are being turned off.");
}
console.log("Checking service worker readiness...");
await navigator.serviceWorker?.ready
.then((registration) => {
console.log("Service worker is ready. Fetching subscription...");
return registration.pushManager.getSubscription();
})
.then(async (subscript: PushSubscription | null) => {
if (subscript) {
subscription = subscript.toJSON();
console.log("PushSubscription retrieved:", subscription);
if (allGoingOff) {
console.log("Unsubscribing from push notifications...");
await subscript.unsubscribe();
console.log("Successfully unsubscribed.");
}
} else {
logConsoleAndDb("Subscription object is not available.");
console.log("No subscription found.");
}
} else {
logConsoleAndDb("Subscription object is not available.");
}
})
.catch((error) => {
logConsoleAndDb(
"Push provider server communication failed: " + JSON.stringify(error),
true,
);
});
})
.catch((error) => {
logConsoleAndDb(
"Push provider server communication failed: " +
JSON.stringify(error),
true,
);
console.error("Error during subscription fetch:", error);
});
if (!subscription) {
console.log("No subscription available. Notifying user...");
this.$notify(
{
group: "alert",
type: "info",
title: "Finished",
text: "Notifications are off.",
},
5000,
);
console.log("Exiting as there is no subscription to process.");
return true;
}
const serverSubscription = {
...subscription,
};
if (!allGoingOff) {
serverSubscription["notifyType"] = notification.title;
console.log(
`Server subscription updated with notifyType: ${notification.title}`,
);
}
console.log("Sending unsubscribe request to the server...");
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(serverSubscription),
})
.then(async (response) => {
if (!response.ok) {
const errorBody = await response.text();
logConsoleAndDb(
`Push server failed: ${response.status} ${errorBody}`,
true,
);
console.error("Push server error response:", errorBody);
}
console.log(`Server response status: ${response.status}`);
return response.ok;
})
.catch((error) => {
logConsoleAndDb(
"Push server communication failed: " + JSON.stringify(error),
true,
);
console.error("Error during server communication:", error);
return false;
});
const message = pushServerSuccess
? "Notification is off."
: "Notification is still on. Try to turn it off again.";
console.log("Server response processed. Message:", message);
if (!subscription) {
// there is no endpoint or auth for the server to compare, so we're done
this.$notify(
{
group: "alert",
type: "info",
title: "Finished",
text: "Notifications are off.", // a different message so I know there are none stored
text: message,
},
5000,
);
return true;
}
// clone in order to get only the properties and allow stringify to work
const serverSubscription = {
...subscription,
};
if (!allGoingOff) {
serverSubscription["notifyType"] = notification.title;
}
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(serverSubscription),
})
.then((response) => {
return response.ok;
})
.catch((error) => {
logConsoleAndDb(
"Push server communication failed: " + JSON.stringify(error),
true,
);
return false;
});
if (notification.callback) {
console.log("Executing notification callback...");
notification.callback(pushServerSuccess);
}
let message;
if (pushServerSuccess) {
message = "Notification is off.";
} else {
message = "Notification is still on. Try to turn it off again.";
}
this.$notify(
{
group: "alert",
type: "info",
title: "Finished",
text: message,
},
5000,
);
console.log(
"Completed turnOffNotifications with success:",
pushServerSuccess,
);
return pushServerSuccess;
} catch (error) {
logConsoleAndDb(
"Error turning off notifications: " + JSON.stringify(error),
true,
);
console.error("Critical error in turnOffNotifications:", error);
if (notification.callback) {
// it's OK if the local notifications are still on (especially if the other notification is on)
notification.callback(pushServerSuccess);
this.$notify(
{
group: "alert",
type: "error",
title: "Error",
text: "Failed to turn off notifications. Please try again.",
},
5000,
);
return false;
}
}
}

View File

@@ -5,7 +5,7 @@
import { createAvatar, StyleOptions } from "@dicebear/core";
import { avataaars } from "@dicebear/collection";
import { Vue, Component, Prop } from "vue-facing-decorator";
import { Contact } from "@/db/tables/contacts";
import { Contact } from "../db/tables/contacts";
@Component
export default class EntityIcon extends Vue {

View File

@@ -48,7 +48,7 @@
<script lang="ts">
import { Vue, Component } from "vue-facing-decorator";
import { NotificationIface } from "@/constants/app";
import { NotificationIface } from "../constants/app";
@Component
export default class InviteDialog extends Vue {

51
src/electron/main.js Normal file
View File

@@ -0,0 +1,51 @@
const { app, BrowserWindow } = require("electron");
const path = require("path");
let mainWindow;
app.on("ready", () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
//preload: path.join(__dirname, "preload.js"),
contextIsolation: true, // Security setting
},
});
const indexPath = path.join(
__dirname,
"../../",
"dist-electron",
"index.html",
);
console.log("Loading Vue app from:", indexPath);
mainWindow.webContents.openDevTools();
mainWindow.webContents.on(
"did-fail-load",
(event, errorCode, errorDescription, validatedURL) => {
console.error(
"Failed to load:",
validatedURL,
"Error:",
errorDescription,
);
},
);
mainWindow.webContents.on("console-message", (event, level, message) => {
console.log(`[Renderer] ${message}`);
});
mainWindow.loadFile(indexPath).catch((err) => {
console.error("Failed to load index.html:", err);
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});

5
src/electron/preload.js Normal file
View File

@@ -0,0 +1,5 @@
const { contextBridge } = require("electron");
contextBridge.exposeInMainWorld("api", {
logMessage: (message) => console.log(`[Electron]: ${message}`),
});

View File

@@ -191,7 +191,7 @@ function setupGlobalErrorHandler(app: VueApp) {
);
};
}
console.log("Bootstrapping Vue app...");
const app = createApp(App)
.component("fa", FontAwesomeIcon)
.component("camera", Camera)
@@ -203,3 +203,4 @@ const app = createApp(App)
setupGlobalErrorHandler(app);
app.mount("#app");
console.log("Vue app mounted.");

View File

@@ -1,6 +1,7 @@
import {
createRouter,
createWebHistory,
createMemoryHistory,
NavigationGuardNext,
RouteLocationNormalized,
RouteRecordRaw,
@@ -21,6 +22,9 @@ const enterOrStart = async (
// one of the few times we use accountsDBPromise directly; try to avoid more usage
const accountsDB = await accountsDBPromise;
const num_accounts = await accountsDB.accounts.count();
console.log("Number of accounts: ", num_accounts);
if (num_accounts > 0) {
next();
} else {
@@ -255,12 +259,26 @@ const routes: Array<RouteRecordRaw> = [
},
];
const isElectron = window.location.protocol === "file:"; // Check if running in Electron
const initialPath = isElectron
? window.location.pathname.replace("/dist-electron/index.html", "/")
: window.location.pathname;
const history = isElectron
? createMemoryHistory() // Memory history for Electron
: createWebHistory("/"); // Add base path for web apps
/** @type {*} */
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
history,
routes,
});
console.log("Initial URL:", initialPath);
// Replace initial URL to start at `/` if necessary
router.replace(initialPath || "/");
const errorHandler = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any,
@@ -278,4 +296,12 @@ const errorHandler = (
router.onError(errorHandler); // Assign the error handler to the router instance
router.beforeEach((to, from, next) => {
console.log("Navigating to view:", to.name);
console.log("From view:", from.name);
next();
});
console.log("Initial URL:", window.location.pathname);
export default router;