feat: add auth to zero queries
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -31,7 +31,7 @@ yarn-error.*
|
|||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env*.local
|
.env
|
||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|||||||
3
app.json
3
app.json
@@ -38,7 +38,8 @@
|
|||||||
"backgroundColor": "#000000"
|
"backgroundColor": "#000000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"expo-sqlite"
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true,
|
"typedRoutes": true,
|
||||||
|
|||||||
@@ -3,23 +3,50 @@ import 'react-native-reanimated';
|
|||||||
|
|
||||||
import { authClient } from '@/lib/auth-client';
|
import { authClient } from '@/lib/auth-client';
|
||||||
import { ZeroProvider } from '@rocicorp/zero/react';
|
import { ZeroProvider } from '@rocicorp/zero/react';
|
||||||
import { zero } from '@/lib/zero';
|
import { useMemo } from 'react';
|
||||||
|
import { authDataSchema } from '@/shared/src/auth';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
import type { ZeroOptions } from '@rocicorp/zero';
|
||||||
|
import { schema, type Schema } from '@/shared/src';
|
||||||
|
import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native";
|
||||||
|
|
||||||
export const unstable_settings = {
|
export const unstable_settings = {
|
||||||
anchor: '(tabs)',
|
anchor: 'index',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const kvStore = Platform.OS === "web" ? undefined : expoSQLiteStoreProvider();
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const { data, isPending } = authClient.useSession();
|
const { data: session, isPending } = authClient.useSession();
|
||||||
|
|
||||||
|
const authData = useMemo(() => {
|
||||||
|
const result = authDataSchema.safeParse(session);
|
||||||
|
return result.success ? result.data : null;
|
||||||
|
}, [session]);
|
||||||
|
|
||||||
|
const cookie = useMemo(() => {
|
||||||
|
return Platform.OS == 'web' ? undefined : authClient.getCookie();
|
||||||
|
}, [session, isPending]);
|
||||||
|
|
||||||
|
const zeroProps = useMemo(() => {
|
||||||
|
return {
|
||||||
|
storageKey: 'money',
|
||||||
|
kvStore,
|
||||||
|
server: 'http://localhost:4848',
|
||||||
|
userID: authData?.user.id ?? "anon",
|
||||||
|
schema,
|
||||||
|
// mutators: createMutators(),
|
||||||
|
auth: cookie,
|
||||||
|
} as const satisfies ZeroOptions<Schema>;
|
||||||
|
}, [authData, cookie]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZeroProvider zero={zero}>
|
<ZeroProvider {...zeroProps}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack.Protected guard={!isPending && !!data}>
|
<Stack.Protected guard={!isPending && !!session}>
|
||||||
<Stack.Screen name="index" />
|
<Stack.Screen name="index" />
|
||||||
</Stack.Protected>
|
</Stack.Protected>
|
||||||
<Stack.Protected guard={!isPending && !data}>
|
<Stack.Protected guard={!isPending && !session}>
|
||||||
<Stack.Screen name="auth" />
|
<Stack.Screen name="auth" />
|
||||||
</Stack.Protected>
|
</Stack.Protected>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ import { useQuery, useZero } from "@rocicorp/zero/react";
|
|||||||
import { queries } from '@money/shared';
|
import { queries } from '@money/shared';
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { data } = authClient.useSession();
|
const { data: session } = authClient.useSession();
|
||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
authClient.signOut();
|
authClient.signOut();
|
||||||
}
|
}
|
||||||
const [transactions] = useQuery(queries.allTransactions());
|
const [transactions] = useQuery(queries.allTransactions(session));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<Text>Hello {data?.user.name}</Text>
|
<Text>Hello {session?.user.name}</Text>
|
||||||
<Button onPress={onLogout} title="Logout" />
|
<Button onPress={onLogout} title="Logout" />
|
||||||
<Text>Transactions: {JSON.stringify(transactions, null, 4)}</Text>
|
<Text>Transactions: {JSON.stringify(transactions, null, 4)}</Text>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import {Zero} from '@rocicorp/zero';
|
|
||||||
import { schema } from "@money/shared";
|
|
||||||
|
|
||||||
export const zero = new Zero({
|
|
||||||
userID: 'anon',
|
|
||||||
server: 'http://localhost:4848',
|
|
||||||
schema,
|
|
||||||
});
|
|
||||||
@@ -2,6 +2,9 @@ const { getDefaultConfig } = require("expo/metro-config");
|
|||||||
|
|
||||||
const config = getDefaultConfig(__dirname)
|
const config = getDefaultConfig(__dirname)
|
||||||
|
|
||||||
|
// Add wasm asset support
|
||||||
|
config.resolver.assetExts.push("wasm");
|
||||||
|
|
||||||
config.resolver.unstable_enablePackageExports = true;
|
config.resolver.unstable_enablePackageExports = true;
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"expo-linking": "~8.0.8",
|
"expo-linking": "~8.0.8",
|
||||||
"expo-router": "~6.0.11",
|
"expo-router": "~6.0.11",
|
||||||
"expo-splash-screen": "~31.0.10",
|
"expo-splash-screen": "~31.0.10",
|
||||||
|
"expo-sqlite": "~16.0.8",
|
||||||
"expo-status-bar": "~3.0.8",
|
"expo-status-bar": "~3.0.8",
|
||||||
"expo-symbols": "~1.0.7",
|
"expo-symbols": "~1.0.7",
|
||||||
"expo-system-ui": "~6.0.7",
|
"expo-system-ui": "~6.0.7",
|
||||||
|
|||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -56,6 +56,9 @@ importers:
|
|||||||
expo-splash-screen:
|
expo-splash-screen:
|
||||||
specifier: ~31.0.10
|
specifier: ~31.0.10
|
||||||
version: 31.0.10(expo@54.0.13)
|
version: 31.0.10(expo@54.0.13)
|
||||||
|
expo-sqlite:
|
||||||
|
specifier: ~16.0.8
|
||||||
|
version: 16.0.8(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||||
expo-status-bar:
|
expo-status-bar:
|
||||||
specifier: ~3.0.8
|
specifier: ~3.0.8
|
||||||
version: 3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
version: 3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||||
@@ -2574,6 +2577,9 @@ packages:
|
|||||||
avvio@9.1.0:
|
avvio@9.1.0:
|
||||||
resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==}
|
resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==}
|
||||||
|
|
||||||
|
await-lock@2.2.2:
|
||||||
|
resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==}
|
||||||
|
|
||||||
babel-jest@29.7.0:
|
babel-jest@29.7.0:
|
||||||
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
@@ -3424,6 +3430,13 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
expo: '*'
|
expo: '*'
|
||||||
|
|
||||||
|
expo-sqlite@16.0.8:
|
||||||
|
resolution: {integrity: sha512-xw776gFgH4ZM5oGs0spSLNmkHO/kJ/EuRXGzE4/22yII9EmG84vm7aM/M2aEb8taBTqwhSGYUpkwkRT5YFFmsg==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
|
||||||
expo-status-bar@3.0.8:
|
expo-status-bar@3.0.8:
|
||||||
resolution: {integrity: sha512-L248XKPhum7tvREoS1VfE0H6dPCaGtoUWzRsUv7hGKdiB4cus33Rc0sxkWkoQ77wE8stlnUlL5lvmT0oqZ3ZBw==}
|
resolution: {integrity: sha512-L248XKPhum7tvREoS1VfE0H6dPCaGtoUWzRsUv7hGKdiB4cus33Rc0sxkWkoQ77wE8stlnUlL5lvmT0oqZ3ZBw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -9041,6 +9054,8 @@ snapshots:
|
|||||||
'@fastify/error': 4.2.0
|
'@fastify/error': 4.2.0
|
||||||
fastq: 1.19.1
|
fastq: 1.19.1
|
||||||
|
|
||||||
|
await-lock@2.2.2: {}
|
||||||
|
|
||||||
babel-jest@29.7.0(@babel/core@7.28.4):
|
babel-jest@29.7.0(@babel/core@7.28.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.28.4
|
'@babel/core': 7.28.4
|
||||||
@@ -10124,6 +10139,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
expo-sqlite@16.0.8(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
|
||||||
|
dependencies:
|
||||||
|
await-lock: 2.2.2
|
||||||
|
expo: 54.0.13(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.11)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||||
|
react: 19.1.0
|
||||||
|
react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0)
|
||||||
|
|
||||||
expo-status-bar@3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
|
expo-status-bar@3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.0
|
react: 19.1.0
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import { syncedQuery } from "@rocicorp/zero";
|
import { syncedQueryWithContext } from "@rocicorp/zero";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { builder } from "@money/shared";
|
import { builder } from "@money/shared";
|
||||||
|
import type { AuthData } from "./auth";
|
||||||
|
import { isLoggedIn } from "./zql";
|
||||||
|
|
||||||
export const queries = {
|
export const queries = {
|
||||||
allTransactions: syncedQuery('allTransactions', z.tuple([]), () =>
|
allTransactions: syncedQueryWithContext('allTransactions', z.tuple([]), (authData: AuthData | null) => {
|
||||||
builder.transaction.limit(10)
|
isLoggedIn(authData);
|
||||||
|
return builder.transaction
|
||||||
|
.where('user_id', '=', authData.user.id)
|
||||||
|
.limit(10)
|
||||||
|
}
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSchema, table, string, number, createBuilder, definePermissions } from "@rocicorp/zero";
|
import { type Schema as ZeroSchema, createSchema, table, string, number, createBuilder, definePermissions } from "@rocicorp/zero";
|
||||||
|
|
||||||
const transaction = table('transaction')
|
const transaction = table('transaction')
|
||||||
.columns({
|
.columns({
|
||||||
@@ -7,13 +7,20 @@ const transaction = table('transaction')
|
|||||||
name: string(),
|
name: string(),
|
||||||
amount: number(),
|
amount: number(),
|
||||||
})
|
})
|
||||||
.primaryKey('id');
|
.primaryKey('id').schema;
|
||||||
|
|
||||||
export const schema = createSchema({
|
export const schema = {
|
||||||
tables: [transaction],
|
tables: { transaction },
|
||||||
|
relationships: {},
|
||||||
enableLegacyMutators: false,
|
enableLegacyMutators: false,
|
||||||
enableLegacyQueries: false,
|
enableLegacyQueries: false,
|
||||||
});
|
} satisfies ZeroSchema;
|
||||||
|
|
||||||
|
// export const schema = createSchema({
|
||||||
|
// tables: [transaction],
|
||||||
|
// enableLegacyMutators: false,
|
||||||
|
// enableLegacyQueries: false,
|
||||||
|
// });
|
||||||
|
|
||||||
export const builder = createBuilder(schema);
|
export const builder = createBuilder(schema);
|
||||||
|
|
||||||
|
|||||||
9
shared/src/zql.ts
Normal file
9
shared/src/zql.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { AuthData } from "./auth";
|
||||||
|
|
||||||
|
export function isLoggedIn(
|
||||||
|
authData: AuthData | null,
|
||||||
|
): asserts authData is AuthData {
|
||||||
|
if (!authData?.user.id) {
|
||||||
|
throw new Error("User is not logged in");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user