From ed8a22d5bc21192dd34c044d001010e88286642f Mon Sep 17 00:00:00 2001 From: Cody Tseng Date: Sat, 24 Jan 2026 00:09:10 +0800 Subject: [PATCH] feat: community mode (#738) Co-authored-by: CXPLAY <62034099+cxplay@users.noreply.github.com> --- README.md | 16 ++ .../BottomNavigationBar/FollowingButton.tsx | 16 ++ src/components/BottomNavigationBar/index.tsx | 5 +- .../FavoriteRelaysSetting/RelayItem.tsx | 3 +- src/components/FeedSwitcher/index.tsx | 42 ++++- .../FollowingFeed/index.tsx} | 0 src/components/RelayInfo/index.tsx | 7 +- src/components/RelaySimpleInfo/index.tsx | 3 +- src/components/Sidebar/FollowingButton.tsx | 20 +++ src/components/Sidebar/index.tsx | 5 +- src/constants.ts | 6 + src/pages/primary/FollowingPage/index.tsx | 32 ++++ src/pages/primary/NoteListPage/FeedButton.tsx | 21 ++- src/pages/primary/NoteListPage/index.tsx | 2 +- .../secondary/RelaySettingsPage/index.tsx | 13 +- src/providers/FavoriteRelaysProvider.tsx | 5 + src/providers/FeedProvider.tsx | 56 ++++--- src/routes/primary.tsx | 2 + src/types/index.d.ts | 2 +- src/vite-env.d.ts | 4 + vite.config.ts | 144 ++++++++++-------- 21 files changed, 303 insertions(+), 101 deletions(-) create mode 100644 src/components/BottomNavigationBar/FollowingButton.tsx rename src/{pages/primary/NoteListPage/FollowingFeed.tsx => components/FollowingFeed/index.tsx} (100%) create mode 100644 src/components/Sidebar/FollowingButton.tsx create mode 100644 src/pages/primary/FollowingPage/index.tsx diff --git a/README.md b/README.md index d652acb..c23365e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,22 @@ docker compose up --build -d After finishing, access: http://localhost:8089 +## Community mode (Optional) + +If you want to run Jumble in community mode (with pre-configured relay sets and relays), you can set the following environment variables in a `.env` file at the root of the project: + +- `VITE_COMMUNITY_RELAY_SETS`: Environment variable. Set the default relay sets for Jumble. Multiple relay sets can be configured. If configured, the first preset group will be displayed to visitors by default upon opening. Visitors cannot delete relay sets preset by administrators. This is ideal for communities wishing to host their own Jumble instances or for setting default feeds for family members. Examples: + +``` +VITE_COMMUNITY_RELAY_SETS=[{"id": "example.com", "name": "The Example Feed", "relayUrls": ["wss://relay.example.com/", "wss://relay.example.org/"]},{"id": "dailynews", "name": "News", "relayUrls": ["wss://news.example.com/", "wss://news.example.org/"]}] +``` + +- `VITE_COMMUNITY_RELAYS`: Environment variable. Set additional default relays for Jumble. Multiple relays can be configured, separated by commas. These relays will be added to the preset relay sets and cannot be removed by visitors. Examples: + +``` +VITE_COMMUNITY_RELAYS="wss://relay.example.com/,wss://relay.example.org/" +``` + ## Sponsors diff --git a/src/components/BottomNavigationBar/FollowingButton.tsx b/src/components/BottomNavigationBar/FollowingButton.tsx new file mode 100644 index 0000000..d0c88fc --- /dev/null +++ b/src/components/BottomNavigationBar/FollowingButton.tsx @@ -0,0 +1,16 @@ +import { usePrimaryPage } from '@/PageManager' +import { UsersRound } from 'lucide-react' +import BottomNavigationBarItem from './BottomNavigationBarItem' + +export default function FollowingButton() { + const { navigate, current, display } = usePrimaryPage() + + return ( + navigate('following')} + > + + + ) +} diff --git a/src/components/BottomNavigationBar/index.tsx b/src/components/BottomNavigationBar/index.tsx index c3bc87c..df418dc 100644 --- a/src/components/BottomNavigationBar/index.tsx +++ b/src/components/BottomNavigationBar/index.tsx @@ -1,7 +1,9 @@ +import { IS_COMMUNITY_MODE } from '@/constants' import { cn } from '@/lib/utils' import BackgroundAudio from '../BackgroundAudio' import AccountButton from './AccountButton' import ExploreButton from './ExploreButton' +import FollowingButton from './FollowingButton' import HomeButton from './HomeButton' import NotificationsButton from './NotificationsButton' @@ -16,7 +18,8 @@ export default function BottomNavigationBar() {
- + {!IS_COMMUNITY_MODE && } + {IS_COMMUNITY_MODE && }
diff --git a/src/components/FavoriteRelaysSetting/RelayItem.tsx b/src/components/FavoriteRelaysSetting/RelayItem.tsx index 7e8dc40..832e914 100644 --- a/src/components/FavoriteRelaysSetting/RelayItem.tsx +++ b/src/components/FavoriteRelaysSetting/RelayItem.tsx @@ -1,3 +1,4 @@ +import { IS_COMMUNITY_MODE } from '@/constants' import { toRelay } from '@/lib/link' import { useSecondaryPage } from '@/PageManager' import { useSortable } from '@dnd-kit/sortable' @@ -38,7 +39,7 @@ export default function RelayItem({ relay }: { relay: string }) {
{relay}
- + {!IS_COMMUNITY_MODE && } ) } diff --git a/src/components/FeedSwitcher/index.tsx b/src/components/FeedSwitcher/index.tsx index 1eb4883..8db4ee7 100644 --- a/src/components/FeedSwitcher/index.tsx +++ b/src/components/FeedSwitcher/index.tsx @@ -1,3 +1,4 @@ +import { IS_COMMUNITY_MODE, COMMUNITY_RELAY_SETS, COMMUNITY_RELAYS } from '@/constants' import { toRelaySettings } from '@/lib/link' import { simplifyUrl } from '@/lib/url' import { cn } from '@/lib/utils' @@ -18,12 +19,45 @@ export default function FeedSwitcher({ close }: { close?: () => void }) { const { relaySets, favoriteRelays } = useFavoriteRelays() const { feedInfo, switchFeed } = useFeed() const { pinnedPubkeySet } = usePinnedUsers() - const filteredRelaySets = useMemo( - () => relaySets.filter((set) => set.relayUrls.length > 0), - [relaySets] - ) + const filteredRelaySets = useMemo(() => { + return relaySets.filter((set) => set.relayUrls.length > 0) + }, [relaySets]) const hasRelays = filteredRelaySets.length > 0 || favoriteRelays.length > 0 + if (IS_COMMUNITY_MODE) { + return ( +
+ {COMMUNITY_RELAY_SETS.map((set) => ( + { + if (!select) return + switchFeed('relays', { activeRelaySetId: set.id }) + close?.() + }} + /> + ))} + {COMMUNITY_RELAYS.map((relay) => ( + { + switchFeed('relay', { relay }) + close?.() + }} + > +
+ +
{simplifyUrl(relay)}
+
+
+ ))} +
+ ) + } + return (
{/* Personal Feeds Section */} diff --git a/src/pages/primary/NoteListPage/FollowingFeed.tsx b/src/components/FollowingFeed/index.tsx similarity index 100% rename from src/pages/primary/NoteListPage/FollowingFeed.tsx rename to src/components/FollowingFeed/index.tsx diff --git a/src/components/RelayInfo/index.tsx b/src/components/RelayInfo/index.tsx index 49e010f..b939ec5 100644 --- a/src/components/RelayInfo/index.tsx +++ b/src/components/RelayInfo/index.tsx @@ -1,7 +1,9 @@ import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area' +import { IS_COMMUNITY_MODE } from '@/constants' import { useFetchRelayInfo } from '@/hooks' +import { createFakeEvent } from '@/lib/event' import { checkNip43Support } from '@/lib/relay' import { normalizeHttpUrl } from '@/lib/url' import { cn } from '@/lib/utils' @@ -10,6 +12,7 @@ import { Check, Copy, GitBranch, Mail, Share2, SquareCode } from 'lucide-react' import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' +import Content from '../Content' import PostEditor from '../PostEditor' import RelayIcon from '../RelayIcon' import RelayMembershipControl from '../RelayMembershipControl' @@ -17,8 +20,6 @@ import SaveRelayDropdownMenu from '../SaveRelayDropdownMenu' import UserAvatar from '../UserAvatar' import Username from '../Username' import RelayReviewsPreview from './RelayReviewsPreview' -import Content from '../Content' -import { createFakeEvent } from '@/lib/event' export default function RelayInfo({ url, className }: { url: string; className?: string }) { const { t } = useTranslation() @@ -161,7 +162,7 @@ function RelayControls({ url }: { url: string }) { - + {!IS_COMMUNITY_MODE && }
) } diff --git a/src/components/RelaySimpleInfo/index.tsx b/src/components/RelaySimpleInfo/index.tsx index 8ce4dd2..cce8ad5 100644 --- a/src/components/RelaySimpleInfo/index.tsx +++ b/src/components/RelaySimpleInfo/index.tsx @@ -1,4 +1,5 @@ import { Skeleton } from '@/components/ui/skeleton' +import { IS_COMMUNITY_MODE } from '@/constants' import { cn } from '@/lib/utils' import { TRelayInfo } from '@/types' import { HTMLProps } from 'react' @@ -30,7 +31,7 @@ export default function RelaySimpleInfo({ )} - {relayInfo && } + {relayInfo && !IS_COMMUNITY_MODE && } {!!relayInfo?.description && (
navigate('following')} + active={display && current === 'following'} + collapse={collapse} + > + + + ) +} diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index d97c4bd..d8e01d8 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -1,5 +1,6 @@ import Icon from '@/assets/Icon' import Logo from '@/assets/Logo' +import { IS_COMMUNITY_MODE } from '@/constants' import { cn } from '@/lib/utils' import { usePrimaryPage } from '@/PageManager' import { useNostr } from '@/providers/NostrProvider' @@ -10,6 +11,7 @@ import { ChevronsLeft, ChevronsRight } from 'lucide-react' import AccountButton from './AccountButton' import BookmarkButton from './BookmarkButton' import RelaysButton from './ExploreButton' +import FollowingButton from './FollowingButton' import HomeButton from './HomeButton' import LayoutSwitcher from './LayoutSwitcher' import NotificationsButton from './NotificationButton' @@ -53,7 +55,8 @@ export default function PrimaryPageSidebar() { )} - + {!IS_COMMUNITY_MODE && } + {IS_COMMUNITY_MODE && } diff --git a/src/constants.ts b/src/constants.ts index 1f48158..6a0784e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,5 @@ import { kinds } from 'nostr-tools' +import { TRelaySet } from './types' export const JUMBLE_API_BASE_URL = 'https://api.jumble.social' @@ -483,3 +484,8 @@ export const SPECIAL_TRUST_SCORE_FILTER_ID = { NAK: 'nak', TRENDING: 'trending' } + +export const COMMUNITY_RELAY_SETS = import.meta.env.VITE_COMMUNITY_RELAY_SETS as TRelaySet[] +export const COMMUNITY_RELAYS = import.meta.env.VITE_COMMUNITY_RELAYS as string[] + +export const IS_COMMUNITY_MODE = COMMUNITY_RELAY_SETS.length > 0 || COMMUNITY_RELAYS.length > 0 diff --git a/src/pages/primary/FollowingPage/index.tsx b/src/pages/primary/FollowingPage/index.tsx new file mode 100644 index 0000000..7da9aa1 --- /dev/null +++ b/src/pages/primary/FollowingPage/index.tsx @@ -0,0 +1,32 @@ +import FollowingFeed from '@/components/FollowingFeed' +import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' +import { TPageRef } from '@/types' +import { UsersRound } from 'lucide-react' +import { forwardRef } from 'react' +import { useTranslation } from 'react-i18next' + +const FollowingPage = forwardRef((_, ref) => { + return ( + } + displayScrollToTopButton + ref={ref} + > + + + ) +}) +FollowingPage.displayName = 'FollowingPage' +export default FollowingPage + +function FollowingPageTitlebar() { + const { t } = useTranslation() + + return ( +
+ +
{t('Following')}
+
+ ) +} diff --git a/src/pages/primary/NoteListPage/FeedButton.tsx b/src/pages/primary/NoteListPage/FeedButton.tsx index 7c491df..42dfa76 100644 --- a/src/pages/primary/NoteListPage/FeedButton.tsx +++ b/src/pages/primary/NoteListPage/FeedButton.tsx @@ -2,6 +2,7 @@ import FeedSwitcher from '@/components/FeedSwitcher' import RelayIcon from '@/components/RelayIcon' import { Drawer, DrawerContent } from '@/components/ui/drawer' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' +import { IS_COMMUNITY_MODE, COMMUNITY_RELAY_SETS, COMMUNITY_RELAYS } from '@/constants' import { simplifyUrl } from '@/lib/url' import { cn } from '@/lib/utils' import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' @@ -15,6 +16,10 @@ export default function FeedButton({ className }: { className?: string }) { const { isSmallScreen } = useScreenSize() const [open, setOpen] = useState(false) + if (IS_COMMUNITY_MODE && COMMUNITY_RELAY_SETS.length + COMMUNITY_RELAYS.length <= 1) { + return + } + if (isSmallScreen) { return ( <> @@ -61,7 +66,8 @@ const FeedSwitcherTrigger = forwardRef { return feedInfo?.feedType === 'relays' && feedInfo.id - ? relaySets.find((set) => set.id === feedInfo.id) + ? (relaySets.find((set) => set.id === feedInfo.id) ?? + COMMUNITY_RELAY_SETS.find((set) => set.id === feedInfo.id)) : undefined }, [feedInfo, relaySets]) const title = useMemo(() => { @@ -78,7 +84,7 @@ const FeedSwitcherTrigger = forwardRef }, [feedInfo]) + const clickable = + !IS_COMMUNITY_MODE || COMMUNITY_RELAY_SETS.length + COMMUNITY_RELAYS.length > 1 + return (
{icon}
{title}
- + {clickable && }
) } diff --git a/src/pages/primary/NoteListPage/index.tsx b/src/pages/primary/NoteListPage/index.tsx index babc432..e0d0fd6 100644 --- a/src/pages/primary/NoteListPage/index.tsx +++ b/src/pages/primary/NoteListPage/index.tsx @@ -1,4 +1,5 @@ import { usePrimaryPage, useSecondaryPage } from '@/PageManager' +import FollowingFeed from '@/components/FollowingFeed' import PostEditor from '@/components/PostEditor' import RelayInfo from '@/components/RelayInfo' import { Button } from '@/components/ui/button' @@ -21,7 +22,6 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import FeedButton from './FeedButton' -import FollowingFeed from './FollowingFeed' import PinnedFeed from './PinnedFeed' import RelaysFeed from './RelaysFeed' diff --git a/src/pages/secondary/RelaySettingsPage/index.tsx b/src/pages/secondary/RelaySettingsPage/index.tsx index 7ea7096..726a209 100644 --- a/src/pages/secondary/RelaySettingsPage/index.tsx +++ b/src/pages/secondary/RelaySettingsPage/index.tsx @@ -1,6 +1,7 @@ -import MailboxSetting from '@/components/MailboxSetting' import FavoriteRelaysSetting from '@/components/FavoriteRelaysSetting' +import MailboxSetting from '@/components/MailboxSetting' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { IS_COMMUNITY_MODE } from '@/constants' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import { forwardRef, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -20,6 +21,16 @@ const RelaySettingsPage = forwardRef(({ index }: { index?: number }, ref) => { } }, []) + if (IS_COMMUNITY_MODE) { + return ( + +
+ +
+
+ ) + } + return ( diff --git a/src/providers/FavoriteRelaysProvider.tsx b/src/providers/FavoriteRelaysProvider.tsx index fcf4d79..d8282e1 100644 --- a/src/providers/FavoriteRelaysProvider.tsx +++ b/src/providers/FavoriteRelaysProvider.tsx @@ -1,3 +1,4 @@ +import { IS_COMMUNITY_MODE, COMMUNITY_RELAY_SETS } from '@/constants' import { createFavoriteRelaysDraftEvent, createRelaySetDraftEvent } from '@/lib/draft-event' import { formatError } from '@/lib/error' import { getReplaceableEventIdentifier } from '@/lib/event' @@ -44,6 +45,10 @@ export function FavoriteRelaysProvider({ children }: { children: React.ReactNode const [relaySets, setRelaySets] = useState([]) useEffect(() => { + if (IS_COMMUNITY_MODE) { + setRelaySets(COMMUNITY_RELAY_SETS) + return + } if (!favoriteRelaysEvent) { const favoriteRelays: string[] = [] const storedRelaySets = storage.getRelaySets() diff --git a/src/providers/FeedProvider.tsx b/src/providers/FeedProvider.tsx index 1369b52..970e9b0 100644 --- a/src/providers/FeedProvider.tsx +++ b/src/providers/FeedProvider.tsx @@ -1,8 +1,9 @@ +import { IS_COMMUNITY_MODE, COMMUNITY_RELAY_SETS, COMMUNITY_RELAYS } from '@/constants' import { getRelaySetFromEvent } from '@/lib/event-metadata' import { isWebsocketUrl, normalizeUrl } from '@/lib/url' import indexedDb from '@/services/indexed-db.service' import storage from '@/services/local-storage.service' -import { TFeedInfo, TFeedType } from '@/types' +import { TFeedInfo, TFeedType, TRelaySet } from '@/types' import { kinds } from 'nostr-tools' import { createContext, useContext, useEffect, useRef, useState } from 'react' import { useFavoriteRelays } from './FavoriteRelaysProvider' @@ -48,9 +49,21 @@ export function FeedProvider({ children }: { children: React.ReactNode }) { if (storedFeedInfo) { feedInfo = storedFeedInfo } else { - feedInfo = { feedType: 'following' } + if (!IS_COMMUNITY_MODE) { + feedInfo = { feedType: 'following' } + } } } + if (!feedInfo && IS_COMMUNITY_MODE) { + feedInfo = + COMMUNITY_RELAY_SETS.length > 0 + ? { + feedType: 'relays', + id: COMMUNITY_RELAY_SETS[0].id, + name: COMMUNITY_RELAY_SETS[0].name + } + : { feedType: 'relay', id: COMMUNITY_RELAYS[0] } + } if (feedInfo?.feedType === 'relays') { return await switchFeed('relays', { activeRelaySetId: feedInfo.id }) @@ -109,24 +122,33 @@ export function FeedProvider({ children }: { children: React.ReactNode }) { } if (feedType === 'relays') { const relaySetId = options.activeRelaySetId ?? (relaySets.length > 0 ? relaySets[0].id : null) - if (!relaySetId || !pubkey) { - setIsReady(true) - return - } + let relaySet: TRelaySet | null = null + if (IS_COMMUNITY_MODE) { + relaySet = + COMMUNITY_RELAY_SETS.find((set) => set.id === relaySetId) ?? + (COMMUNITY_RELAY_SETS.length > 0 ? COMMUNITY_RELAY_SETS[0] : null) + } else { + if (!relaySetId || !pubkey) { + setIsReady(true) + return + } - let relaySet = - relaySets.find((set) => set.id === relaySetId) ?? - (relaySets.length > 0 ? relaySets[0] : null) - if (!relaySet) { - const storedRelaySetEvent = await indexedDb.getReplaceableEvent( - pubkey, - kinds.Relaysets, - relaySetId - ) - if (storedRelaySetEvent) { - relaySet = getRelaySetFromEvent(storedRelaySetEvent) + relaySet = + relaySets.find((set) => set.id === relaySetId) ?? + (relaySets.length > 0 ? relaySets[0] : null) + + if (!relaySet) { + const storedRelaySetEvent = await indexedDb.getReplaceableEvent( + pubkey, + kinds.Relaysets, + relaySetId + ) + if (storedRelaySetEvent) { + relaySet = getRelaySetFromEvent(storedRelaySetEvent) + } } } + if (relaySet) { const newFeedInfo = { feedType, id: relaySet.id } setFeedInfo(newFeedInfo) diff --git a/src/routes/primary.tsx b/src/routes/primary.tsx index d598635..87dff30 100644 --- a/src/routes/primary.tsx +++ b/src/routes/primary.tsx @@ -1,5 +1,6 @@ import BookmarkPage from '@/pages/primary/BookmarkPage' import ExplorePage from '@/pages/primary/ExplorePage' +import FollowingPage from '@/pages/primary/FollowingPage' import MePage from '@/pages/primary/MePage' import NoteListPage from '@/pages/primary/NoteListPage' import NotificationListPage from '@/pages/primary/NotificationListPage' @@ -13,6 +14,7 @@ import { createRef } from 'react' const PRIMARY_ROUTE_CONFIGS = [ { key: 'home', component: NoteListPage }, { key: 'explore', component: ExplorePage }, + { key: 'following', component: FollowingPage }, { key: 'notifications', component: NotificationListPage }, { key: 'me', component: MePage }, { key: 'profile', component: ProfilePage }, diff --git a/src/types/index.d.ts b/src/types/index.d.ts index a9b6f31..f2f2d9d 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -114,7 +114,7 @@ export type TAccount = { export type TAccountPointer = Pick export type TFeedType = 'following' | 'pinned' | 'relays' | 'relay' -export type TFeedInfo = { feedType: TFeedType; id?: string } | null +export type TFeedInfo = { feedType: TFeedType; id?: string; name?: string } | null export type TLanguage = 'en' | 'zh' | 'pl' diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index c921d75..6edf098 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -6,3 +6,7 @@ declare global { nostr?: TNip07 } } + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/vite.config.ts b/vite.config.ts index 5e5ace2..18130c3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,10 @@ import react from '@vitejs/plugin-react' import { execSync } from 'child_process' import path from 'path' -import { defineConfig } from 'vite' +import { defineConfig, loadEnv } from 'vite' import { VitePWA } from 'vite-plugin-pwa' import packageJson from './package.json' +import { normalizeUrl } from './src/lib/url' const getGitHash = () => { try { @@ -24,70 +25,81 @@ const getAppVersion = () => { } // https://vite.dev/config/ -export default defineConfig({ - define: { - 'import.meta.env.GIT_COMMIT': getGitHash(), - 'import.meta.env.APP_VERSION': getAppVersion() - }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src') - } - }, - plugins: [ - react(), - VitePWA({ - registerType: 'autoUpdate', - workbox: { - globPatterns: ['**/*.{js,css,html,png,jpg,svg}'], - globDirectory: 'dist/', - maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, - cleanupOutdatedCaches: true - }, - devOptions: { - enabled: true - }, - manifest: { - name: 'Jumble', - short_name: 'Jumble', - icons: [ - { - src: '/pwa-512x512.png', - sizes: '512x512', - type: 'image/png', - purpose: 'any' - }, - { - src: '/pwa-192x192.png', - sizes: '192x192', - type: 'image/png', - purpose: 'any' - }, - { - src: '/pwa-512x512.png', - sizes: '512x512', - type: 'image/png', - purpose: 'maskable' - }, - { - src: '/pwa-192x192.png', - sizes: '192x192', - type: 'image/png', - purpose: 'maskable' - }, - { - src: '/pwa-monochrome.svg', - sizes: '512x512', - type: 'image/svg+xml', - purpose: 'monochrome' - } - ], - start_url: '/', - display: 'standalone', - background_color: '#FFFFFF', - theme_color: '#FFFFFF', - description: packageJson.description +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + + return { + define: { + 'import.meta.env.GIT_COMMIT': getGitHash(), + 'import.meta.env.APP_VERSION': getAppVersion(), + 'import.meta.env.VITE_COMMUNITY_RELAY_SETS': JSON.parse( + JSON.stringify(env.VITE_COMMUNITY_RELAY_SETS ?? '[]') + ), + 'import.meta.env.VITE_COMMUNITY_RELAYS': (env.VITE_COMMUNITY_RELAYS ?? '') + .split(',') + .map((url) => normalizeUrl(url)) + .filter(Boolean) + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './src') } - }) - ] + }, + plugins: [ + react(), + VitePWA({ + registerType: 'autoUpdate', + workbox: { + globPatterns: ['**/*.{js,css,html,png,jpg,svg}'], + globDirectory: 'dist/', + maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, + cleanupOutdatedCaches: true + }, + devOptions: { + enabled: true + }, + manifest: { + name: 'Jumble', + short_name: 'Jumble', + icons: [ + { + src: '/pwa-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any' + }, + { + src: '/pwa-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'any' + }, + { + src: '/pwa-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable' + }, + { + src: '/pwa-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'maskable' + }, + { + src: '/pwa-monochrome.svg', + sizes: '512x512', + type: 'image/svg+xml', + purpose: 'monochrome' + } + ], + start_url: '/', + display: 'standalone', + background_color: '#FFFFFF', + theme_color: '#FFFFFF', + description: packageJson.description + } + }) + ] + } })