import constants from "./constants"; import {createRouter, createWebHashHistory} from "vue-router"; import SignIn from "../components/Windows/SignIn.vue"; import Connect from "../components/Windows/Connect.vue"; import Settings from "../components/Windows/Settings.vue"; import Help from "../components/Windows/Help.vue"; import Changelog from "../components/Windows/Changelog.vue"; import NetworkEdit from "../components/Windows/NetworkEdit.vue"; import SearchResults from "../components/Windows/SearchResults.vue"; import RoutedChat from "../components/RoutedChat.vue"; import {store} from "./store"; import AppearanceSettings from "../components/Settings/Appearance.vue"; import GeneralSettings from "../components/Settings/General.vue"; import AccountSettings from "../components/Settings/Account.vue"; import NotificationSettings from "../components/Settings/Notifications.vue"; import {ClientChan} from "./types"; const router = createRouter({ history: createWebHashHistory(), routes: [ { name: "SignIn", path: "/sign-in", component: SignIn, beforeEnter(to, from, next) { // Prevent navigating to sign-in when already signed in if (store.state.appLoaded) { next(false); return; } next(); }, }, { name: "Connect", path: "/connect", component: Connect, props: (route) => ({queryParams: route.query}), }, { path: "/settings", component: Settings, children: [ { name: "General", path: "", component: GeneralSettings, }, { name: "Appearance", path: "appearance", component: AppearanceSettings, }, { name: "Account", path: "account", component: AccountSettings, props: true, }, { name: "Notifications", path: "notifications", component: NotificationSettings, }, ], }, { name: "Help", path: "/help", component: Help, }, { name: "Changelog", path: "/changelog", component: Changelog, }, { name: "NetworkEdit", path: "/edit-network/:uuid", component: NetworkEdit, }, { name: "RoutedChat", path: "/chan-:id", component: RoutedChat, }, { name: "SearchResults", path: "/chan-:id/search", component: SearchResults, }, ], }); router.beforeEach((to, from, next) => { // If user is not yet signed in, wait for appLoaded state to change // unless they are trying to open SignIn (which can be triggered in auth.js) if (!store.state.appLoaded && to.name !== "SignIn") { store.watch( (state) => state.appLoaded, () => next() ); return; } next(); }); router.beforeEach((to, from, next) => { // Disallow navigating to non-existing routes if (!to.matched.length) { next(false); return; } // Disallow navigating to invalid channels if (to.name === "RoutedChat" && !store.getters.findChannel(Number(to.params.id))) { next(false); return; } // Disallow navigating to invalid networks if (to.name === "NetworkEdit" && !store.getters.findNetwork(String(to.params.uuid))) { next(false); return; } next(); }); router.afterEach((to) => { if (store.state.appLoaded) { if (window.innerWidth <= constants.mobileViewportPixels) { store.commit("sidebarOpen", false); } } if (store.state.activeChannel) { const channel = store.state.activeChannel.channel; if (to.name !== "RoutedChat") { store.commit("activeChannel", undefined); } // When switching out of a channel, mark everything as read if (channel.messages?.length > 0) { channel.firstUnread = channel.messages[channel.messages.length - 1].id; } if (channel.messages?.length > 100) { channel.messages.splice(0, channel.messages.length - 100); channel.moreHistoryAvailable = true; } } }); async function navigate(routeName: string, params: any = {}) { if (router.currentRoute.value.name) { await router.push({name: routeName, params}); } else { // If current route is null, replace the history entry // This prevents invalid entries from lingering in history, // and then the route guard preventing proper navigation // eslint-disable-next-line @typescript-eslint/no-empty-function await router.replace({name: routeName, params}).catch(() => {}); } } function switchToChannel(channel: ClientChan) { void navigate("RoutedChat", {id: channel.id}); } if ("serviceWorker" in navigator) { navigator.serviceWorker.addEventListener("message", (event) => { if (event.data && event.data.type === "open") { const id = parseInt(event.data.channel.substring(5), 10); // remove "chan-" prefix const channelTarget = store.getters.findChannel(id); if (channelTarget) { switchToChannel(channelTarget.channel); } } }); } export {router, navigate, switchToChannel};