Files
eranos/src/App.tsx
T
Alex Gleason b62da321f7 Restore Messages sidebar entry, add WhiteNoise logo to /messages page
- Re-add the Messages item to the left sidebar with its previous
  MessageSquareMore lucide icon. Drop requiresAuth so logged-out users
  also see the entry — the page is a static recommendation.
- Restore 'messages' to the default sidebarOrder in App.tsx.
- Add WhiteNoiseIcon (the logomark from whitenoise.chat, recolored to
  currentColor so it adapts to theme) and use it on the /messages
  install-CTA card in place of the generic Lock glyph.
2026-05-17 14:41:34 -05:00

225 lines
6.9 KiB
TypeScript

// NOTE: This file should normally not be modified unless you are adding a new provider.
// To add new routes, edit the AppRouter.tsx file.
import { NostrLoginProvider } from "@nostrify/react/login";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { InferSeoMetaPlugin } from "@unhead/addons";
import { createHead, UnheadProvider } from "@unhead/react/client";
import { AppProvider } from "@/components/AppProvider";
import { InitialSyncGate } from "@/components/InitialSyncGate";
import { NativeNotifications } from "@/components/NativeNotifications";
import NostrProvider from "@/components/NostrProvider";
import { NostrSync } from "@/components/NostrSync";
import { PlausibleProvider } from "@/components/PlausibleProvider";
import { SentryProvider } from "@/components/SentryProvider";
import { TooltipProvider } from "@/components/ui/tooltip";
import { useNsecPasteGuard } from "@/hooks/useNsecPasteGuard";
import type { AppConfig } from "@/contexts/AppContext";
import { NWCProvider } from "@/contexts/NWCContext";
import { SparkWalletProvider } from "@/contexts/SparkWalletContext";
import { BuildConfigSchema, type BuildConfig } from "@/lib/schemas";
import { secureStorage } from "@/lib/secureStorage";
import AppRouter from "./AppRouter";
const head = createHead({
plugins: [InferSeoMetaPlugin()],
});
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60000, // 1 minute
gcTime: 300000, // 5 minutes
},
},
});
/** Hardcoded fallback values. Always provides every required field. */
const hardcodedConfig: AppConfig = {
appName: "Agora",
appId: "agora",
shareOrigin: import.meta.env.VITE_SHARE_ORIGIN || undefined,
homePage: "feed",
client: "naddr1qvzqqqru7cpzq7q6z5ns2hm5c8msyv83qwzxpxe52j8c4d4q5m92wsp9sflelkh9qqzkg6t5w3hswjl4yp",
magicMouse: false,
theme: "system",
useAppRelays: true,
useUserRelays: false,
relayMetadata: {
relays: [],
updatedAt: 0,
},
feedSettings: {
feedIncludePosts: true,
feedIncludeComments: true,
feedIncludeReposts: true,
feedIncludeGenericReposts: true,
feedIncludeReactions: false,
feedIncludeZaps: false,
feedIncludeArticles: true,
showArticles: true,
showHighlights: true,
feedIncludeHighlights: false,
showEvents: true,
feedIncludeEvents: true,
showVines: true,
showPolls: true,
showTreasures: true,
showTreasureGeocaches: true,
showTreasureFoundLogs: true,
showColors: true,
showPeopleLists: true,
feedIncludeVines: true,
feedIncludePolls: true,
feedIncludeTreasureGeocaches: true,
feedIncludeTreasureFoundLogs: true,
feedIncludeColors: true,
feedIncludePeopleLists: true,
showDecks: true,
feedIncludeDecks: true,
showWebxdc: true,
feedIncludeWebxdc: true,
showPhotos: true,
feedIncludePhotos: true,
showVideos: true,
feedIncludeNormalVideos: true,
feedIncludeShortVideos: true,
feedIncludeVoiceMessages: true,
showEmojiPacks: true,
feedIncludeEmojiPacks: true,
showCustomEmojis: true,
showUserStatuses: true,
showMusic: true,
feedIncludeMusicTracks: true,
feedIncludeMusicPlaylists: true,
showPodcasts: true,
feedIncludePodcastEpisodes: true,
feedIncludePodcastTrailers: true,
showDevelopment: true,
feedIncludeDevelopment: true,
showCommunities: true,
feedIncludeCommunities: true,
showBadges: true,
showBadgeDefinitions: true,
showProfileBadges: true,
showBadgeAwards: true,
feedIncludeBadgeDefinitions: true,
feedIncludeProfileBadges: true,
feedIncludeBadgeAwards: true,
feedIncludeVanish: true,
showBirdstar: true,
feedIncludeBirdDetections: true,
feedIncludeBirdex: true,
feedIncludeConstellations: true,
followsFeedShowReplies: true,
},
sidebarOrder: [
"feed",
"communities",
"world",
"wallet",
"agent",
"messages",
"profile",
"notifications",
"search",
"settings",
],
nip85StatsPubkey:
"5f68e85ee174102ca8978eef302129f081f03456c884185d5ec1c1224ab633ea",
blossomServerMetadata: {
servers: [],
updatedAt: 0,
},
useAppBlossomServers: true,
faviconUrl: "https://ditto.pub/api/favicon/{hostname}",
linkPreviewUrl: "https://ditto.pub/api/link-preview/{url}",
corsProxy: "https://proxy.shakespeare.diy/?url={href}",
contentWarningPolicy: "blur",
sentryDsn: import.meta.env.VITE_SENTRY_DSN || "",
sentryEnabled: true,
plausibleDomain: import.meta.env.VITE_PLAUSIBLE_DOMAIN || "",
plausibleEndpoint: import.meta.env.VITE_PLAUSIBLE_ENDPOINT || "",
savedFeeds: [],
autoplayVideos: false,
imageQuality: 'compressed',
curatorPubkey: '932614571afcbad4d17a191ee281e39eebbb41b93fac8fd87829622aeb112f4d',
sandboxDomain: 'iframe.diy',
esploraBaseUrl: 'https://mempool.space/api',
sidebarWidgets: [
{ id: 'trends' },
{ id: 'hot-posts' },
{ id: 'ai-chat' },
],
aiBaseURL: 'https://ai.shakespeare.diy/v1',
aiApiKey: '',
aiModel: 'grok-4.1-fast',
aiSystemPrompt: '',
};
/**
* Parse and validate build-time app config overrides from the env string.
* Returns an empty object when no config file was provided or validation fails.
*/
function parseBuildConfig(): BuildConfig {
try {
const encodedConfig = import.meta.env.APP_CONFIG ?? import.meta.env.DITTO_CONFIG;
const json = JSON.parse(encodedConfig);
if (!json) return {};
return BuildConfigSchema.parse(json);
} catch {
return {};
}
}
/**
* Merge hardcoded defaults with build-time config overrides.
* Deep-merges feedSettings so a partial override doesn't erase defaults.
* Precedence (handled by AppProvider): user localStorage > build-time > hardcoded.
*/
const buildConfig = parseBuildConfig();
const defaultConfig: AppConfig = {
...hardcodedConfig,
...buildConfig,
feedSettings: { ...hardcodedConfig.feedSettings, ...buildConfig.feedSettings },
};
export function App() {
useNsecPasteGuard();
return (
<UnheadProvider head={head}>
<AppProvider storageKey="nostr:app-config" defaultConfig={defaultConfig}>
<SentryProvider>
<PlausibleProvider>
<QueryClientProvider client={queryClient}>
<NostrLoginProvider storageKey="nostr:login" storage={secureStorage}>
<NostrProvider>
<NostrSync />
<NativeNotifications />
<NWCProvider>
<SparkWalletProvider>
<TooltipProvider>
<InitialSyncGate>
<AppRouter />
</InitialSyncGate>
</TooltipProvider>
</SparkWalletProvider>
</NWCProvider>
</NostrProvider>
</NostrLoginProvider>
</QueryClientProvider>
</PlausibleProvider>
</SentryProvider>
</AppProvider>
</UnheadProvider>
);
}
export default App;