init commit
This commit is contained in:
1
apps/expo/.cache/.prettiercache
Normal file
1
apps/expo/.cache/.prettiercache
Normal file
@@ -0,0 +1 @@
|
||||
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21"],{"key":"22","value":"23"},{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},{"key":"46","value":"47"},{"key":"48","value":"49"},{"key":"50","value":"51"},{"key":"52","value":"53"},{"key":"54","value":"55"},{"key":"56","value":"57"},{"key":"58","value":"59"},{"key":"60","value":"61"},{"key":"62","value":"63"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/eslint.config.mts",{"size":276,"mtime":1761443987000,"hash":"64","data":"65"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/app/index.tsx",{"size":5019,"mtime":1761443987000,"hash":"66","data":"67"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/app.config.ts",{"size":1333,"mtime":1761443987000,"hash":"68","data":"69"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/package.json",{"size":1736,"mtime":1761665033217,"hash":"70","data":"71"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/app/_layout.tsx",{"size":927,"mtime":1761443987000,"hash":"72","data":"73"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/metro.config.js",{"size":511,"mtime":1761443987000,"hash":"74","data":"75"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/utils/auth.ts",{"size":398,"mtime":1761443987000,"hash":"76","data":"77"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/index.ts",{"size":28,"mtime":1761443987000,"hash":"78","data":"79"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/utils/api.tsx",{"size":1327,"mtime":1761443987000,"hash":"80","data":"81"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/utils/session-store.ts",{"size":272,"mtime":1761443987000,"hash":"82","data":"83"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/turbo.json",{"size":163,"mtime":1761443987000,"hash":"84","data":"85"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/assets/icon-light.png",{"size":19133,"mtime":1761443987000,"hash":"86"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/eas.json",{"size":567,"mtime":1761443987000,"hash":"87","data":"88"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/postcss.config.js",{"size":66,"mtime":1761443987000,"hash":"89","data":"90"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/app/post/[id].tsx",{"size":757,"mtime":1761443987000,"hash":"91","data":"92"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/assets/icon-dark.png",{"size":19633,"mtime":1761443987000,"hash":"93"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/styles.css",{"size":90,"mtime":1761443987000,"hash":"94","data":"95"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/tsconfig.json",{"size":388,"mtime":1761663711587,"hash":"96","data":"97"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/src/utils/base-url.ts",{"size":880,"mtime":1761443987000,"hash":"98","data":"99"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/.expo-shared/assets.json",{"size":155,"mtime":1761443987000,"hash":"100","data":"101"},"/home/gib/Documents/Code/monorepo/convex-monorepo/apps/expo/nativewind-env.d.ts",{"size":246,"mtime":1761443987000,"hash":"102","data":"103"},"3157b700ecbb4aa217059352507464a2",{"hashOfOptions":"104"},"2be11479779af34f59ec5003513b8e0b",{"hashOfOptions":"105"},"bd476a3950d078330d3e06a560e9f747",{"hashOfOptions":"106"},"f137cf230cf525ca8c2f0bdf233a6e3f",{"hashOfOptions":"107"},"518cd9c33ede87c649b01dd727bb00a0",{"hashOfOptions":"108"},"fbe52c661bc5cbe8d2f7c1058981b896",{"hashOfOptions":"109"},"9455acf80595f4373e11d98e5bc87d89",{"hashOfOptions":"110"},"cc3395d2be4587e21093428fdb6f69e6",{"hashOfOptions":"111"},"0adb5df94af971795b21eddb560c26b5",{"hashOfOptions":"112"},"691dcf997a384f03d1ba5c043c1a3405",{"hashOfOptions":"113"},"c7d4dcf839dfeaa02e0407adfd5e47a6",{"hashOfOptions":"114"},"863da15dbd856008b7c24077ca746d91","a3c1487f8318513ae7c156acc857fde2",{"hashOfOptions":"115"},"5080fb7f49a201ad809050ee57249d41",{"hashOfOptions":"116"},"28454ca5c544a35129bb829072b8dbc1",{"hashOfOptions":"117"},"1e8ac0d261e95efb19d290ffcf70ce36","5068090396cd9f4f9463f09f43c9e257",{"hashOfOptions":"118"},"74d22c2830502b877ac3b806a788bfa3",{"hashOfOptions":"119"},"29e520338914a80764ca0866a87fac3d",{"hashOfOptions":"120"},"0f7f54c7161b8403d3bc42d91f59cd91",{"hashOfOptions":"121"},"d4d589c153ac8b5e7bf0fb130a5b5a7d",{"hashOfOptions":"122"},"3529965326","1818025557","2842754191","1504153565","3815129580","3236408049","1664229517","2979851528","3930068749","2135093465","2923444549","366302476","4133218715","3075567833","3597942159","1044122342","1450990178","3667891651","3412980713"]
|
||||
4
apps/expo/.expo-shared/assets.json
Normal file
4
apps/expo/.expo-shared/assets.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
|
||||
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
|
||||
}
|
||||
60
apps/expo/app.config.ts
Normal file
60
apps/expo/app.config.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { ConfigContext, ExpoConfig } from "expo/config";
|
||||
|
||||
export default ({ config }: ConfigContext): ExpoConfig => ({
|
||||
...config,
|
||||
name: "expo",
|
||||
slug: "expo",
|
||||
scheme: "expo",
|
||||
version: "0.1.0",
|
||||
orientation: "portrait",
|
||||
icon: "./assets/icon-light.png",
|
||||
userInterfaceStyle: "automatic",
|
||||
updates: {
|
||||
fallbackToCacheTimeout: 0,
|
||||
},
|
||||
newArchEnabled: true,
|
||||
assetBundlePatterns: ["**/*"],
|
||||
ios: {
|
||||
bundleIdentifier: "your.bundle.identifier",
|
||||
supportsTablet: true,
|
||||
icon: {
|
||||
light: "./assets/icon-light.png",
|
||||
dark: "./assets/icon-dark.png",
|
||||
},
|
||||
},
|
||||
android: {
|
||||
package: "your.bundle.identifier",
|
||||
adaptiveIcon: {
|
||||
foregroundImage: "./assets/icon-light.png",
|
||||
backgroundColor: "#1F104A",
|
||||
},
|
||||
edgeToEdgeEnabled: true,
|
||||
},
|
||||
// extra: {
|
||||
// eas: {
|
||||
// projectId: "your-eas-project-id",
|
||||
// },
|
||||
// },
|
||||
experiments: {
|
||||
tsconfigPaths: true,
|
||||
typedRoutes: true,
|
||||
reactCanary: true,
|
||||
reactCompiler: true,
|
||||
},
|
||||
plugins: [
|
||||
"expo-router",
|
||||
"expo-secure-store",
|
||||
"expo-web-browser",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
backgroundColor: "#E4E4E7",
|
||||
image: "./assets/icon-light.png",
|
||||
dark: {
|
||||
backgroundColor: "#18181B",
|
||||
image: "./assets/icon-dark.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
BIN
apps/expo/assets/icon-dark.png
Normal file
BIN
apps/expo/assets/icon-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
apps/expo/assets/icon-light.png
Normal file
BIN
apps/expo/assets/icon-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
33
apps/expo/eas.json
Normal file
33
apps/expo/eas.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"cli": {
|
||||
"version": ">= 4.1.2",
|
||||
"appVersionSource": "remote"
|
||||
},
|
||||
"build": {
|
||||
"base": {
|
||||
"node": "22.12.0",
|
||||
"pnpm": "9.15.4",
|
||||
"ios": {
|
||||
"resourceClass": "m-medium"
|
||||
}
|
||||
},
|
||||
"development": {
|
||||
"extends": "base",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal"
|
||||
},
|
||||
"preview": {
|
||||
"extends": "base",
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"simulator": true
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"extends": "base"
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"production": {}
|
||||
}
|
||||
}
|
||||
12
apps/expo/eslint.config.mts
Normal file
12
apps/expo/eslint.config.mts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from "eslint/config";
|
||||
|
||||
import { baseConfig } from "@acme/eslint-config/base";
|
||||
import { reactConfig } from "@acme/eslint-config/react";
|
||||
|
||||
export default defineConfig(
|
||||
{
|
||||
ignores: [".expo/**", "expo-plugins/**"],
|
||||
},
|
||||
baseConfig,
|
||||
reactConfig,
|
||||
);
|
||||
1
apps/expo/index.ts
Normal file
1
apps/expo/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import "expo-router/entry";
|
||||
16
apps/expo/metro.config.js
Normal file
16
apps/expo/metro.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// Learn more: https://docs.expo.dev/guides/monorepos/
|
||||
const path = require("node:path");
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const { FileStore } = require("metro-cache");
|
||||
const { withNativewind } = require("nativewind/metro");
|
||||
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
config.cacheStores = [
|
||||
new FileStore({
|
||||
root: path.join(__dirname, "node_modules", ".cache", "metro"),
|
||||
}),
|
||||
];
|
||||
|
||||
/** @type {import('expo/metro-config').MetroConfig} */
|
||||
module.exports = withNativewind(config);
|
||||
3
apps/expo/nativewind-env.d.ts
vendored
Normal file
3
apps/expo/nativewind-env.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/// <reference types="react-native-css/types" />
|
||||
|
||||
// NOTE: This file should not be edited and should be committed with your source code. It is generated by react-native-css. If you need to move or disable this file, please see the documentation.
|
||||
55
apps/expo/package.json
Normal file
55
apps/expo/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@acme/expo",
|
||||
"private": true,
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf .cache .expo .turbo android ios node_modules",
|
||||
"dev": "expo start",
|
||||
"dev:android": "expo start --android",
|
||||
"dev:ios": "expo start --ios",
|
||||
"android": "expo run:android",
|
||||
"ios": "expo run:ios",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore --ignore-path .prettierignore",
|
||||
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@acme/backend": "workspace:*",
|
||||
"@convex-dev/auth": "workspace:*",
|
||||
"@legendapp/list": "^2.0.14",
|
||||
"convex": "workspace:*",
|
||||
"expo": "~54.0.20",
|
||||
"expo-constants": "~18.0.10",
|
||||
"expo-dev-client": "~6.0.16",
|
||||
"expo-linking": "~8.0.8",
|
||||
"expo-router": "~6.0.13",
|
||||
"expo-secure-store": "~15.0.7",
|
||||
"expo-splash-screen": "~31.0.10",
|
||||
"expo-status-bar": "~3.0.8",
|
||||
"expo-system-ui": "~6.0.8",
|
||||
"expo-web-browser": "~15.0.8",
|
||||
"nativewind": "5.0.0-preview.2",
|
||||
"react": "catalog:react19",
|
||||
"react-dom": "catalog:react19",
|
||||
"react-native": "~0.81.5",
|
||||
"react-native-css": "3.0.1",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-reanimated": "~4.1.3",
|
||||
"react-native-safe-area-context": "~5.6.1",
|
||||
"react-native-screens": "~4.16.0",
|
||||
"react-native-worklets": "~0.5.1",
|
||||
"superjson": "2.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@acme/eslint-config": "workspace:*",
|
||||
"@acme/prettier-config": "workspace:*",
|
||||
"@acme/tailwind-config": "workspace:*",
|
||||
"@acme/tsconfig": "workspace:*",
|
||||
"@types/react": "catalog:react19",
|
||||
"eslint": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"prettier": "@acme/prettier-config"
|
||||
}
|
||||
1
apps/expo/postcss.config.js
Normal file
1
apps/expo/postcss.config.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require("@acme/tailwind-config/postcss-config");
|
||||
33
apps/expo/src/app/_layout.tsx
Normal file
33
apps/expo/src/app/_layout.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useColorScheme } from "react-native";
|
||||
import { Stack } from "expo-router";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
import { queryClient } from "~/utils/api";
|
||||
|
||||
import "../styles.css";
|
||||
|
||||
// This is the main layout of the app
|
||||
// It wraps your pages with the providers they need
|
||||
export default function RootLayout() {
|
||||
const colorScheme = useColorScheme();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{/*
|
||||
The Stack component displays the current page.
|
||||
It also allows you to configure your screens
|
||||
*/}
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerStyle: {
|
||||
backgroundColor: "#c03484",
|
||||
},
|
||||
contentStyle: {
|
||||
backgroundColor: colorScheme == "dark" ? "#09090B" : "#FFFFFF",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<StatusBar />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
172
apps/expo/src/app/index.tsx
Normal file
172
apps/expo/src/app/index.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
import { useState } from "react";
|
||||
import { Pressable, Text, TextInput, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { Link, Stack } from "expo-router";
|
||||
import { LegendList } from "@legendapp/list";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import type { RouterOutputs } from "~/utils/api";
|
||||
import { trpc } from "~/utils/api";
|
||||
import { authClient } from "~/utils/auth";
|
||||
|
||||
function PostCard(props: {
|
||||
post: RouterOutputs["post"]["all"][number];
|
||||
onDelete: () => void;
|
||||
}) {
|
||||
return (
|
||||
<View className="bg-muted flex flex-row rounded-lg p-4">
|
||||
<View className="grow">
|
||||
<Link
|
||||
asChild
|
||||
href={{
|
||||
pathname: "/post/[id]",
|
||||
params: { id: props.post.id },
|
||||
}}
|
||||
>
|
||||
<Pressable className="">
|
||||
<Text className="text-primary text-xl font-semibold">
|
||||
{props.post.title}
|
||||
</Text>
|
||||
<Text className="text-foreground mt-2">{props.post.content}</Text>
|
||||
</Pressable>
|
||||
</Link>
|
||||
</View>
|
||||
<Pressable onPress={props.onDelete}>
|
||||
<Text className="text-primary font-bold uppercase">Delete</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function CreatePost() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [title, setTitle] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
|
||||
const { mutate, error } = useMutation(
|
||||
trpc.post.create.mutationOptions({
|
||||
async onSuccess() {
|
||||
setTitle("");
|
||||
setContent("");
|
||||
await queryClient.invalidateQueries(trpc.post.all.queryFilter());
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<View className="mt-4 flex gap-2">
|
||||
<TextInput
|
||||
className="border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight"
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
placeholder="Title"
|
||||
/>
|
||||
{error?.data?.zodError?.fieldErrors.title && (
|
||||
<Text className="text-destructive mb-2">
|
||||
{error.data.zodError.fieldErrors.title}
|
||||
</Text>
|
||||
)}
|
||||
<TextInput
|
||||
className="border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight"
|
||||
value={content}
|
||||
onChangeText={setContent}
|
||||
placeholder="Content"
|
||||
/>
|
||||
{error?.data?.zodError?.fieldErrors.content && (
|
||||
<Text className="text-destructive mb-2">
|
||||
{error.data.zodError.fieldErrors.content}
|
||||
</Text>
|
||||
)}
|
||||
<Pressable
|
||||
className="bg-primary flex items-center rounded-sm p-2"
|
||||
onPress={() => {
|
||||
mutate({
|
||||
title,
|
||||
content,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text className="text-foreground">Create</Text>
|
||||
</Pressable>
|
||||
{error?.data?.code === "UNAUTHORIZED" && (
|
||||
<Text className="text-destructive mt-2">
|
||||
You need to be logged in to create a post
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileAuth() {
|
||||
const { data: session } = authClient.useSession();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text className="text-foreground pb-2 text-center text-xl font-semibold">
|
||||
{session?.user.name ? `Hello, ${session.user.name}` : "Not logged in"}
|
||||
</Text>
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
session
|
||||
? authClient.signOut()
|
||||
: authClient.signIn.social({
|
||||
provider: "discord",
|
||||
callbackURL: "/",
|
||||
})
|
||||
}
|
||||
className="bg-primary flex items-center rounded-sm p-2"
|
||||
>
|
||||
<Text>{session ? "Sign Out" : "Sign In With Discord"}</Text>
|
||||
</Pressable>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const postQuery = useQuery(trpc.post.all.queryOptions());
|
||||
|
||||
const deletePostMutation = useMutation(
|
||||
trpc.post.delete.mutationOptions({
|
||||
onSettled: () =>
|
||||
queryClient.invalidateQueries(trpc.post.all.queryFilter()),
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView className="bg-background">
|
||||
{/* Changes page title visible on the header */}
|
||||
<Stack.Screen options={{ title: "Home Page" }} />
|
||||
<View className="bg-background h-full w-full p-4">
|
||||
<Text className="text-foreground pb-2 text-center text-5xl font-bold">
|
||||
Create <Text className="text-primary">T3</Text> Turbo
|
||||
</Text>
|
||||
|
||||
<MobileAuth />
|
||||
|
||||
<View className="py-2">
|
||||
<Text className="text-primary font-semibold italic">
|
||||
Press on a post
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<LegendList
|
||||
data={postQuery.data ?? []}
|
||||
estimatedItemSize={20}
|
||||
keyExtractor={(item) => item.id}
|
||||
ItemSeparatorComponent={() => <View className="h-2" />}
|
||||
renderItem={(p) => (
|
||||
<PostCard
|
||||
post={p.item}
|
||||
onDelete={() => deletePostMutation.mutate(p.item.id)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<CreatePost />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
24
apps/expo/src/app/post/[id].tsx
Normal file
24
apps/expo/src/app/post/[id].tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { SafeAreaView, Text, View } from "react-native";
|
||||
import { Stack, useGlobalSearchParams } from "expo-router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { trpc } from "~/utils/api";
|
||||
|
||||
export default function Post() {
|
||||
const { id } = useGlobalSearchParams<{ id: string }>();
|
||||
const { data } = useQuery(trpc.post.byId.queryOptions({ id }));
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
<SafeAreaView className="bg-background">
|
||||
<Stack.Screen options={{ title: data.title }} />
|
||||
<View className="h-full w-full p-4">
|
||||
<Text className="text-primary py-2 text-3xl font-bold">
|
||||
{data.title}
|
||||
</Text>
|
||||
<Text className="text-foreground py-4">{data.content}</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
3
apps/expo/src/styles.css
Normal file
3
apps/expo/src/styles.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@import "tailwindcss";
|
||||
@import "nativewind/theme";
|
||||
@import "@acme/tailwind-config/theme";
|
||||
50
apps/expo/src/utils/api.tsx
Normal file
50
apps/expo/src/utils/api.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { createTRPCClient, httpBatchLink, loggerLink } from "@trpc/client";
|
||||
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
||||
import superjson from "superjson";
|
||||
|
||||
import type { AppRouter } from "@acme/api";
|
||||
|
||||
import { authClient } from "./auth";
|
||||
import { getBaseUrl } from "./base-url";
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// ...
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* A set of typesafe hooks for consuming your API.
|
||||
*/
|
||||
export const trpc = createTRPCOptionsProxy<AppRouter>({
|
||||
client: createTRPCClient({
|
||||
links: [
|
||||
loggerLink({
|
||||
enabled: (opts) =>
|
||||
process.env.NODE_ENV === "development" ||
|
||||
(opts.direction === "down" && opts.result instanceof Error),
|
||||
colorMode: "ansi",
|
||||
}),
|
||||
httpBatchLink({
|
||||
transformer: superjson,
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
headers() {
|
||||
const headers = new Map<string, string>();
|
||||
headers.set("x-trpc-source", "expo-react");
|
||||
|
||||
const cookies = authClient.getCookie();
|
||||
if (cookies) {
|
||||
headers.set("Cookie", cookies);
|
||||
}
|
||||
return headers;
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
queryClient,
|
||||
});
|
||||
|
||||
export type { RouterInputs, RouterOutputs } from "@acme/api";
|
||||
16
apps/expo/src/utils/auth.ts
Normal file
16
apps/expo/src/utils/auth.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
import { expoClient } from "@better-auth/expo/client";
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
|
||||
import { getBaseUrl } from "./base-url";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: getBaseUrl(),
|
||||
plugins: [
|
||||
expoClient({
|
||||
scheme: "expo",
|
||||
storagePrefix: "expo",
|
||||
storage: SecureStore,
|
||||
}),
|
||||
],
|
||||
});
|
||||
26
apps/expo/src/utils/base-url.ts
Normal file
26
apps/expo/src/utils/base-url.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import Constants from "expo-constants";
|
||||
|
||||
/**
|
||||
* Extend this function when going to production by
|
||||
* setting the baseUrl to your production API URL.
|
||||
*/
|
||||
export const getBaseUrl = () => {
|
||||
/**
|
||||
* Gets the IP address of your host-machine. If it cannot automatically find it,
|
||||
* you'll have to manually set it. NOTE: Port 3000 should work for most but confirm
|
||||
* you don't have anything else running on it, or you'd have to change it.
|
||||
*
|
||||
* **NOTE**: This is only for development. In production, you'll want to set the
|
||||
* baseUrl to your production API URL.
|
||||
*/
|
||||
const debuggerHost = Constants.expoConfig?.hostUri;
|
||||
const localhost = debuggerHost?.split(":")[0];
|
||||
|
||||
if (!localhost) {
|
||||
// return "https://turbo.t3.gg";
|
||||
throw new Error(
|
||||
"Failed to get localhost. Please point to your production server.",
|
||||
);
|
||||
}
|
||||
return `http://${localhost}:3000`;
|
||||
};
|
||||
7
apps/expo/src/utils/session-store.ts
Normal file
7
apps/expo/src/utils/session-store.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as SecureStore from "expo-secure-store";
|
||||
|
||||
const key = "session_token";
|
||||
|
||||
export const getToken = () => SecureStore.getItem(key);
|
||||
export const deleteToken = () => SecureStore.deleteItemAsync(key);
|
||||
export const setToken = (v: string) => SecureStore.setItem(key, v);
|
||||
20
apps/expo/tsconfig.json
Normal file
20
apps/expo/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": ["@acme/tsconfig/base.json"],
|
||||
"compilerOptions": {
|
||||
"jsx": "react-native",
|
||||
"checkJs": false,
|
||||
"moduleSuffixes": [".ios", ".android", ".native", ""],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"*.ts",
|
||||
"*.js",
|
||||
".expo/types/**/*.ts",
|
||||
"expo-env.d.ts",
|
||||
"nativewind-env.d.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
10
apps/expo/turbo.json
Normal file
10
apps/expo/turbo.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://turborepo.com/schema.json",
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"persistent": true,
|
||||
"interactive": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user