-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathapp.tsx
More file actions
106 lines (96 loc) · 3.64 KB
/
Copy pathapp.tsx
File metadata and controls
106 lines (96 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { useMemo, useState } from 'react';
import { initializeApp } from 'firebase/app';
import { getFunctions, connectFunctionsEmulator } from 'firebase/functions';
import {
connectFirestoreEmulator,
initializeFirestore,
} from 'firebase/firestore';
import { Layer } from 'effect';
import { Client } from '@effect-firebase/client';
import { RegistryProvider, useAtomSet } from '@effect/atom-react';
import { TanStackDevtools } from '@tanstack/react-devtools';
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
import {
firestoreMockPlugin,
type TanStackDevtoolsReactPlugin,
} from '@effect-firebase/devtools';
import SideMenu from '../components/menu/side-menu.js';
import MenuItem from '../components/menu/menu-item.js';
import { firestoreLayerAtom, mockEpochAtom } from '../lib/atoms.js';
import { mockBackend } from '../lib/mock.js';
interface AppProps {
children: React.ReactNode;
}
/**
* Start the app with `VITE_MOCK_BACKEND=1` (e.g. `pnpm example:mock`) to run
* Firestore against the in-memory mock backend instead of the emulator.
*/
const useMockBackend = import.meta.env['VITE_MOCK_BACKEND'] === '1';
/**
* One TanStack Devtools shell hosting the router panel and, in mock mode,
* the Firestore Mock panel. Every state toggle bumps `mockEpochAtom`, which
* remounts the data views: their atoms are disposed and the fresh
* subscriptions start from `Initial` against the toggled state. A refresh
* would not be enough — atoms keep their previous value while re-running,
* and a `loading` stream never emits, so stale data would stay on screen.
*/
function Devtools() {
const bumpEpoch = useAtomSet(mockEpochAtom);
const plugins = useMemo(() => {
const all: Array<TanStackDevtoolsReactPlugin> = [
{
name: 'TanStack Router',
render: <TanStackRouterDevtoolsPanel />,
},
];
if (useMockBackend) {
all.push(
firestoreMockPlugin(mockBackend.controller, {
defaultOpen: true,
onStateChange: () => {
bumpEpoch((epoch) => epoch + 1);
},
}),
);
}
return all;
}, [bumpEpoch]);
return <TanStackDevtools plugins={plugins} />;
}
export function App({ children }: AppProps) {
// useState initializer: Firebase setup runs once per mount, and the layer
// keeps a stable identity without a memo the compiler can't verify.
const [layer] = useState(() => {
const app = initializeApp({ projectId: 'effect-firebase-example' });
const functions = getFunctions(app, 'europe-north1');
connectFunctionsEmulator(functions, 'localhost', 5001);
if (useMockBackend) {
// Fixture encoding errors are defects, not recoverable failures.
return Layer.orDie(mockBackend.layer);
}
const firestore = initializeFirestore(app, {
ignoreUndefinedProperties: true,
});
connectFirestoreEmulator(firestore, 'localhost', 8080);
return Client.layer({ firestore });
});
// RegistryProvider reads initialValues only when the registry is first
// created, so the array doesn't need a stable identity.
return (
<RegistryProvider initialValues={[[firestoreLayerAtom, layer] as const]}>
<div className="flex min-h-screen bg-gray-50">
<SideMenu>
<MenuItem icon="🏠" label="Home" to="/" />
<MenuItem icon="📊" label="Functions" to="/functions" />
<MenuItem icon="🔥" label="Firestore" to="/firestore" />
</SideMenu>
{/* Main content area */}
<main className="flex-1 md:ml-64 p-8">
<div className="max-w-4xl mx-auto">{children}</div>
</main>
</div>
<Devtools />
</RegistryProvider>
);
}
export default App;