This commit is contained in:
Turbobot
2024-03-21 13:41:16 +08:00
committed by giancarlo
commit bb58169fe9
204 changed files with 26228 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}

43
apps/expo/app.config.ts Normal file
View File

@@ -0,0 +1,43 @@
import type { ExpoConfig } from "@expo/config";
const defineConfig = (): ExpoConfig => ({
name: "expo",
slug: "expo",
scheme: "expo",
version: "0.1.0",
orientation: "portrait",
icon: "./assets/icon.png",
userInterfaceStyle: "light",
splash: {
image: "./assets/icon.png",
resizeMode: "contain",
backgroundColor: "#1F104A",
},
updates: {
fallbackToCacheTimeout: 0,
},
assetBundlePatterns: ["**/*"],
ios: {
bundleIdentifier: "your.bundle.identifier",
supportsTablet: true,
},
android: {
package: "your.bundle.identifier",
adaptiveIcon: {
foregroundImage: "./assets/icon.png",
backgroundColor: "#1F104A",
},
},
// extra: {
// eas: {
// projectId: "your-eas-project-id",
// },
// },
experiments: {
tsconfigPaths: true,
typedRoutes: true,
},
plugins: ["expo-router", "./expo-plugins/with-modify-gradle.js"],
});
export default defineConfig;

BIN
apps/expo/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

15
apps/expo/babel.config.js Normal file
View File

@@ -0,0 +1,15 @@
/** @type {import("@babel/core").ConfigFunction} */
module.exports = function (api) {
api.cache.forever();
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
plugins: [
require.resolve("expo-router/babel"),
require.resolve("react-native-reanimated/plugin"),
],
};
};

31
apps/expo/eas.json Normal file
View File

@@ -0,0 +1,31 @@
{
"cli": {
"version": ">= 4.1.2"
},
"build": {
"base": {
"node": "18.16.1",
"ios": {
"resourceClass": "m-medium"
}
},
"development": {
"extends": "base",
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"extends": "base",
"distribution": "internal",
"ios": {
"simulator": true
}
},
"production": {
"extends": "base"
}
},
"submit": {
"production": {}
}
}

View File

@@ -0,0 +1,44 @@
// This plugin is required for fixing `.apk` build issue
// It appends Expo and RN versions into the `build.gradle` file
// References:
// https://github.com/t3-oss/create-t3-turbo/issues/120
// https://github.com/expo/expo/issues/18129
/** @type {import("@expo/config-plugins").ConfigPlugin} */
const defineConfig = (config) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require("@expo/config-plugins").withProjectBuildGradle(
config,
(config) => {
if (!config.modResults.contents.includes("ext.getPackageJsonVersion =")) {
config.modResults.contents = config.modResults.contents.replace(
"buildscript {",
`buildscript {
ext.getPackageJsonVersion = { packageName ->
new File(['node', '--print', "JSON.parse(require('fs').readFileSync(require.resolve('\${packageName}/package.json'), 'utf-8')).version"].execute(null, rootDir).text.trim())
}`,
);
}
if (!config.modResults.contents.includes("reactNativeVersion =")) {
config.modResults.contents = config.modResults.contents.replace(
"ext {",
`ext {
reactNativeVersion = "\${ext.getPackageJsonVersion('react-native')}"`,
);
}
if (!config.modResults.contents.includes("expoPackageVersion =")) {
config.modResults.contents = config.modResults.contents.replace(
"ext {",
`ext {
expoPackageVersion = "\${ext.getPackageJsonVersion('expo')}"`,
);
}
return config;
},
);
};
module.exports = defineConfig;

29
apps/expo/metro.config.js Normal file
View File

@@ -0,0 +1,29 @@
// Learn more: https://docs.expo.dev/guides/monorepos/
const { getDefaultConfig } = require("@expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const path = require("path");
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, "../..");
// Create the default Metro config
const config = getDefaultConfig(projectRoot, { isCSSEnabled: true });
if (config.resolver) {
// 1. Watch all files within the monorepo
config.watchFolders = [workspaceRoot];
// 2. Let Metro know where to resolve packages and in what order
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, "node_modules"),
path.resolve(workspaceRoot, "node_modules"),
];
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
config.resolver.disableHierarchicalLookup = true;
}
// @ts-expect-error - FIXME: type is mismatching?
module.exports = withNativeWind(config, {
input: "./src/styles.css",
configPath: "./tailwind.config.ts",
});

68
apps/expo/package.json Normal file
View File

@@ -0,0 +1,68 @@
{
"name": "@acme/expo",
"version": "0.1.0",
"private": true,
"main": "expo-router/entry",
"scripts": {
"clean": "git clean -xdf .expo .turbo node_modules",
"dev": "expo start --ios",
"dev:android": "expo start --android",
"dev:ios": "expo start --ios",
"android": "expo run:android",
"ios": "expo run:ios",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@expo/metro-config": "^0.10.7",
"@shopify/flash-list": "1.4.3",
"@tanstack/react-query": "^5.17.15",
"@trpc/client": "next",
"@trpc/react-query": "next",
"@trpc/server": "next",
"expo": "^49.0.22",
"expo-constants": "~14.4.2",
"expo-linking": "~5.0.2",
"expo-router": "2.0.14",
"expo-splash-screen": "~0.22.0",
"expo-status-bar": "~1.7.1",
"nativewind": "^4.0.23",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.73.1",
"react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "~3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.1",
"superjson": "2.2.1"
},
"devDependencies": {
"@acme/api": "0.1.0",
"@acme/eslint-config": "0.2.0",
"@acme/prettier-config": "0.1.0",
"@acme/tailwind-config": "0.1.0",
"@acme/tsconfig": "0.1.0",
"@babel/core": "^7.23.7",
"@babel/preset-env": "^7.23.8",
"@babel/runtime": "^7.23.8",
"@expo/config-plugins": "^7.8.4",
"@types/babel__core": "^7.20.5",
"@types/react": "^18.2.48",
"eslint": "^8.56.0",
"prettier": "^3.2.4",
"tailwindcss": "3.4.1",
"typescript": "^5.3.3"
},
"eslintConfig": {
"root": true,
"extends": [
"@acme/eslint-config/base",
"@acme/eslint-config/react"
],
"ignorePatterns": [
"expo-plugins/**"
]
},
"prettier": "@acme/prettier-config"
}

View File

@@ -0,0 +1,27 @@
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { TRPCProvider } 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() {
return (
<TRPCProvider>
{/*
The Stack component displays the current page.
It also allows you to configure your screens
*/}
<Stack
screenOptions={{
headerStyle: {
backgroundColor: "#f472b6",
},
}}
/>
<StatusBar />
</TRPCProvider>
);
}

145
apps/expo/src/app/index.tsx Normal file
View File

@@ -0,0 +1,145 @@
// import { useState } from "react";
// import { Button, Pressable, Text, TextInput, View } from "react-native";
// import { SafeAreaView } from "react-native-safe-area-context";
// import { Link, Stack } from "expo-router";
// import { FlashList } from "@shopify/flash-list";
// import type { RouterOutputs } from "~/utils/api";
// import { api } from "~/utils/api";
// function PostCard(props: {
// post: RouterOutputs["post"]["all"][number];
// onDelete: () => void;
// }) {
// return (
// <View className="flex flex-row rounded-lg bg-white/10 p-4">
// <View className="flex-grow">
// <Link
// asChild
// href={{
// pathname: "/post/[id]",
// params: { id: props.post.id },
// }}
// >
// <Pressable>
// <Text className="text-xl font-semibold text-pink-400">
// {props.post.title}
// </Text>
// <Text className="mt-2 text-white">{props.post.content}</Text>
// </Pressable>
// </Link>
// </View>
// <Pressable onPress={props.onDelete}>
// <Text className="font-bold uppercase text-pink-400">Delete</Text>
// </Pressable>
// </View>
// );
// }
// function CreatePost() {
// const utils = api.useUtils();
// const [title, setTitle] = useState("");
// const [content, setContent] = useState("");
// const { mutate, error } = api.post.create.useMutation({
// async onSuccess() {
// setTitle("");
// setContent("");
// await utils.post.all.invalidate();
// },
// });
// return (
// <View className="mt-4">
// <TextInput
// className="mb-2 rounded bg-white/10 p-2 text-white"
// placeholderTextColor="rgba(255, 255, 255, 0.5)"
// value={title}
// onChangeText={setTitle}
// placeholder="Title"
// />
// {error?.data?.zodError?.fieldErrors.title && (
// <Text className="mb-2 text-red-500">
// {error.data.zodError.fieldErrors.title}
// </Text>
// )}
// <TextInput
// className="mb-2 rounded bg-white/10 p-2 text-white"
// placeholderTextColor="rgba(255, 255, 255, 0.5)"
// value={content}
// onChangeText={setContent}
// placeholder="Content"
// />
// {error?.data?.zodError?.fieldErrors.content && (
// <Text className="mb-2 text-red-500">
// {error.data.zodError.fieldErrors.content}
// </Text>
// )}
// <Pressable
// className="rounded bg-pink-400 p-2"
// onPress={() => {
// mutate({
// title,
// content,
// });
// }}
// >
// <Text className="font-semibold text-white">Publish post</Text>
// </Pressable>
// {error?.data?.code === "UNAUTHORIZED" && (
// <Text className="mt-2 text-red-500">
// You need to be logged in to create a post
// </Text>
// )}
// </View>
// );
// }
// export default function Index() {
// const utils = api.useUtils();
// const postQuery = api.post.all.useQuery();
// const deletePostMutation = api.post.delete.useMutation({
// onSettled: () => utils.post.all.invalidate(),
// });
// return (
// <SafeAreaView className="bg-[#1F104A]">
// {/* Changes page title visible on the header */}
// <Stack.Screen options={{ title: "Home Page" }} />
// <View className="h-full w-full p-4">
// <Text className="pb-2 text-center text-5xl font-bold text-white">
// Create <Text className="text-pink-400">T3</Text> Turbo
// </Text>
// <Button
// onPress={() => void utils.post.all.invalidate()}
// title="Refresh posts"
// color={"#f472b6"}
// />
// <View className="py-2">
// <Text className="font-semibold italic text-white">
// Press on a post
// </Text>
// </View>
// <FlashList
// data={postQuery.data}
// estimatedItemSize={20}
// ItemSeparatorComponent={() => <View className="h-2" />}
// renderItem={(p) => (
// <PostCard
// post={p.item}
// onDelete={() => deletePostMutation.mutate(p.item.id)}
// />
// )}
// />
// <CreatePost />
// </View>
// </SafeAreaView>
// );
// }

View File

@@ -0,0 +1,22 @@
// import { SafeAreaView, Text, View } from "react-native";
// import { Stack, useGlobalSearchParams } from "expo-router";
// import { api } from "~/utils/api";
// export default function Post() {
// const { id } = useGlobalSearchParams();
// if (!id || typeof id !== "string") throw new Error("unreachable");
// const { data } = api.post.byId.useQuery({ id: parseInt(id) });
// if (!data) return null;
// return (
// <SafeAreaView className="bg-[#1F104A]">
// <Stack.Screen options={{ title: data.title }} />
// <View className="h-full w-full p-4">
// <Text className="py-2 text-3xl font-bold text-white">{data.title}</Text>
// <Text className="py-4 text-white">{data.content}</Text>
// </View>
// </SafeAreaView>
// );
// }

3
apps/expo/src/styles.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1 @@
/// <reference types="nativewind/types" />

View File

@@ -0,0 +1,76 @@
import { useState } from "react";
import Constants from "expo-constants";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink, loggerLink } from "@trpc/client";
import { createTRPCReact } from "@trpc/react-query";
import superjson from "superjson";
import type { AppRouter } from "@acme/api";
/**
* A set of typesafe hooks for consuming your API.
*/
export const api = createTRPCReact<AppRouter>();
export { type RouterInputs, type RouterOutputs } from "@acme/api";
/**
* Extend this function when going to production by
* setting the baseUrl to your production API URL.
*/
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`;
};
/**
* A wrapper for your app that provides the TRPC context.
* Use only in _app.tsx
*/
export function TRPCProvider(props: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
api.createClient({
transformer: superjson, // TODO: Add transforming for Dinero.js
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
headers() {
const headers = new Map<string, string>();
headers.set("x-trpc-source", "expo-react");
return Object.fromEntries(headers);
},
}),
loggerLink({
enabled: (opts) =>
process.env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
colorMode: "ansi",
}),
],
}),
);
return (
<api.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{props.children}
</QueryClientProvider>
</api.Provider>
);
}

View File

@@ -0,0 +1,10 @@
// @ts-expect-error - no types
import nativewind from "nativewind/preset";
import type { Config } from "tailwindcss";
import baseConfig from "@acme/tailwind-config";
export default {
content: ["./src/**/*.{ts,tsx}"],
presets: [baseConfig, nativewind],
} satisfies Config;

21
apps/expo/tsconfig.json Normal file
View File

@@ -0,0 +1,21 @@
{
"extends": "@acme/tsconfig/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"],
},
"jsx": "react-native",
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
"types": ["nativewind/types"],
},
"include": [
"src",
"*.ts",
"index.tsx",
"*.js",
".expo/types/**/*.ts",
"expo-env.d.ts",
],
"exclude": ["node_modules"],
}