/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ import {observer} from 'mobx-react-lite'; import React from 'react'; import {ChannelTypes, ME} from '~/Constants'; import {AppBadge} from '~/components/AppBadge'; import {ChannelIndexPage} from '~/components/channel/ChannelIndexPage'; import {ChannelLayout} from '~/components/channel/ChannelLayout'; import {DMLayout} from '~/components/channel/dm/DMLayout'; import {AppLayout} from '~/components/layout/AppLayout'; import {FavoritesLayout} from '~/components/layout/FavoritesLayout'; import {GuildsLayout} from '~/components/layout/GuildsLayout'; import {BookmarksBottomSheet} from '~/components/modals/BookmarksBottomSheet'; import {StatusChangeBottomSheet} from '~/components/modals/StatusChangeBottomSheet'; import {NotificationsPage} from '~/components/pages/NotificationsPage'; import PremiumCallbackPage from '~/components/pages/PremiumCallbackPage'; import {YouPage} from '~/components/pages/YouPage'; import {createRoute, Redirect, useParams} from '~/lib/router'; import SessionManager from '~/lib/SessionManager'; import {Routes} from '~/Routes'; import {GuildChannelRouter} from '~/router/components/GuildChannelRouter'; import {rootRoute} from '~/router/routes/rootRoutes'; import AuthenticationStore from '~/stores/AuthenticationStore'; import ChannelStore from '~/stores/ChannelStore'; import MobileLayoutStore from '~/stores/MobileLayoutStore'; import SelectedChannelStore from '~/stores/SelectedChannelStore'; const appLayoutRoute = createRoute({ getParentRoute: () => rootRoute, id: 'appLayout', onEnter: () => { if (!SessionManager.isInitialized) { return undefined; } if (!AuthenticationStore.isAuthenticated) { const current = window.location.pathname + window.location.search; return new Redirect(`${Routes.LOGIN}?redirect_to=${encodeURIComponent(current)}`); } return undefined; }, layout: ({children}) => ( <> {children} ), }); const guildsLayoutRoute = createRoute({ getParentRoute: () => appLayoutRoute, id: 'guildsLayout', layout: ({children}) => {children}, }); const notificationsRoute = createRoute({ getParentRoute: () => appLayoutRoute, id: 'notifications', path: Routes.NOTIFICATIONS, component: () => { const [bookmarksSheetOpen, setBookmarksSheetOpen] = React.useState(false); return ( <> setBookmarksSheetOpen(true)} /> setBookmarksSheetOpen(false)} /> ); }, }); const youRoute = createRoute({ getParentRoute: () => appLayoutRoute, id: 'you', path: Routes.YOU, component: () => { const [statusSheetOpen, setStatusSheetOpen] = React.useState(false); return ( <> setStatusSheetOpen(true)} /> setStatusSheetOpen(false)} /> ); }, }); const premiumCallbackRoute = createRoute({ getParentRoute: () => appLayoutRoute, id: 'premiumCallback', path: Routes.PREMIUM_CALLBACK, component: () => , }); const bookmarksRoute = createRoute({ getParentRoute: () => guildsLayoutRoute, id: 'bookmarks', path: Routes.BOOKMARKS, component: () => , }); const mentionsRoute = createRoute({ getParentRoute: () => guildsLayoutRoute, id: 'mentions', path: Routes.MENTIONS, component: () => , }); const meRoute = createRoute({ getParentRoute: () => guildsLayoutRoute, id: 'me', path: '/channels/@me', component: observer(() => { const isMobileLayout = MobileLayoutStore.enabled; React.useEffect(() => { if (!isMobileLayout && SelectedChannelStore.selectedChannelIds.has(ME)) { SelectedChannelStore.clearGuildSelection(ME); } }, [isMobileLayout]); return ; }), }); const favoritesRoute = createRoute({ getParentRoute: () => guildsLayoutRoute, id: 'favorites', path: '/channels/@favorites', layout: ({children}) => {children}, }); const favoritesChannelRoute = createRoute({ getParentRoute: () => favoritesRoute, id: 'favoritesChannel', path: '/channels/@favorites/:channelId', component: () => ( ), }); const channelsRoute = createRoute({ getParentRoute: () => guildsLayoutRoute, id: 'channels', path: '/channels/:guildId', layout: ({children}) => { const params = useParams() as {guildId: string}; const {guildId} = params; if (guildId === ME) { return {children}; } return guildId ? {children} : null; }, }); const channelRoute = createRoute({ getParentRoute: () => channelsRoute, id: 'channel', path: '/channels/:guildId/:channelId', onEnter: (ctx) => { const {guildId, channelId} = ctx.params; const channel = ChannelStore.getChannel(channelId); if (channel && (channel.type === ChannelTypes.GUILD_CATEGORY || channel.type === ChannelTypes.GUILD_LINK)) { return new Redirect(Routes.guildChannel(guildId)); } return undefined; }, component: () => ( ), }); const messageRoute = createRoute({ getParentRoute: () => channelRoute, id: 'message', path: '/channels/:guildId/:channelId/:messageId', onEnter: (ctx) => { const {guildId, channelId} = ctx.params; const channel = ChannelStore.getChannel(channelId); if (channel && (channel.type === ChannelTypes.GUILD_CATEGORY || channel.type === ChannelTypes.GUILD_LINK)) { return new Redirect(Routes.guildChannel(guildId)); } return undefined; }, component: () => ( ), }); export const appRouteTree = appLayoutRoute.addChildren([ notificationsRoute, youRoute, premiumCallbackRoute, guildsLayoutRoute.addChildren([ bookmarksRoute, mentionsRoute, meRoute, favoritesRoute.addChildren([favoritesChannelRoute]), channelsRoute.addChildren([channelRoute.addChildren([messageRoute])]), ]), ]);