diff --git a/README.md b/README.md new file mode 100644 index 0000000..8cfecf8 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# React Native starter kit with Appwrite + +Kickstart your React Native development with this ready-to-use starter project integrated with [Appwrite](https://www.appwrite.io) + +## 🚀Getting started + +### +Clone the Project +Clone this repository to your local machine using Git: + +`git clone https://github.com/appwrite/starter-for-react-native` + +## 🛠️ Development guid +1. **Configure Appwrite**
+ Navigate to `.env` and update the values to match your Appwrite project credentials. +2. **Customize as needed**
+ Modify the starter kit to suit your app's requirements. Adjust UI, features, or backend + integrations as per your needs. +3. **Install dependencies**
+ Run `npm install` to install all dependencies. +4. **Run the app**
+ Start the project by running `npx expo start`. Download [Expo Go](https://expo.dev/go) to run the app on your device. + +## 📦 Building for production +To create a production build of your app, follow the documentation by [Expo Application Services](https://expo.dev/eas#build) + +## 💡 Additional notes +- This starter project is designed to streamline your React Native development with Appwrite. +- Refer to the [Appwrite documentation](https://appwrite.io/docs) for detailed integration guidance. \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000..b3410b5 --- /dev/null +++ b/app.json @@ -0,0 +1,41 @@ +{ + "expo": { + "name": "starter-react-native", + "slug": "starter-react-native", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "myapp", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff" + } + ] + ], + "experiments": { + "typedRoutes": true + } + } +} diff --git a/app/_layout.tsx b/app/_layout.tsx new file mode 100644 index 0000000..a1e33e8 --- /dev/null +++ b/app/_layout.tsx @@ -0,0 +1,10 @@ +import { Slot } from "expo-router"; +import { View } from "react-native"; + +export default function HomeLayout() { + return ( + + + + ); +} diff --git a/app/index.tsx b/app/index.tsx new file mode 100644 index 0000000..f614e76 --- /dev/null +++ b/app/index.tsx @@ -0,0 +1,153 @@ +import { Platform, ScrollView, StyleSheet, Text, View } from "react-native"; +import { Header } from "@/components/Header"; +import { useRef, useState } from "react"; +import { Card } from "@/components/Card"; +import { fontStyles } from "@/styles/font"; +import { IconArrowSmRight } from "@/assets/images/IconArrowSmRight"; +import { Code } from "@/components/Code"; +import { Logs } from "@/components/Logs"; +import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { Log } from "@/types/log"; +import { AppwriteException, Client } from "appwrite"; + +const client = new Client() + .setProject(process.env.EXPO_PUBLIC_APPWRITE_PROJECT_ID ?? "") + .setEndpoint(process.env.EXPO_PUBLIC_APPWRITE_ENDPOINT ?? ""); + +export default function HomeScreen() { + const [connectionState, setConnectionState] = useState< + "idle" | "loading" | "success" | "error" + >("idle"); + const bottomSheetRef = useRef(null); + const [currentSnapIndex, setCurrentSnapIndex] = useState(0); + const [logs, setLogs] = useState>([]); + + const doPing = async () => { + setConnectionState("loading"); + let log: Log; + try { + const res = await client.ping(); + log = { + date: new Date(), + method: "GET", + path: "/v1/ping", + status: 200, + response: res, + }; + setConnectionState("success"); + } catch (err) { + log = { + date: new Date(), + method: "GET", + path: "/v1/ping", + status: err instanceof AppwriteException ? err.code : 500, + response: err instanceof AppwriteException ? err.message : "unknown", + }; + setConnectionState("error"); + } + setLogs([...logs, log]); + }; + + const toggleBottomSheet = () => { + if (bottomSheetRef.current) { + const newIndex = currentSnapIndex === 1 ? 0 : 1; + setCurrentSnapIndex(newIndex); + bottomSheetRef.current.snapToIndex(newIndex); + } + }; + + const handleSnapChange = (index: number) => { + setCurrentSnapIndex(index); + }; + + return ( + + + +
+ + + + Edit your app + + + Edit + app/index.tsx + to get started with building + + your app and many more to come + + + + + + Go to console + + + + Navigate to the console to control and oversee the Appwrite + services. + + + + + Explore docs + + + + Discover the full power of Appwrite by diving into our + documentation. + + + + + + + 0} + logs={logs} + /> + + + + + ); +} + +const styles = StyleSheet.create({ + bottomSheet: { + borderTopWidth: 1, + minHeight: Platform.OS === "android" ? 50 : 70, + flex: 1, + borderColor: "#EDEDF0", + }, + cardContainer: { + paddingInline: 20, + display: "flex", + gap: 16, + }, + scrollview: { + height: 200, + }, + cardHeader: { + display: "flex", + justifyContent: "space-between", + flexDirection: "row", + alignItems: "center", + marginBottom: 8, + }, + editDescription: { + display: "flex", + flexDirection: "row", + justifyContent: "flex-start", + }, +}); diff --git a/assets/images/IconArrowSmRight.tsx b/assets/images/IconArrowSmRight.tsx new file mode 100644 index 0000000..b4c194c --- /dev/null +++ b/assets/images/IconArrowSmRight.tsx @@ -0,0 +1,14 @@ +import Svg, { Path } from "react-native-svg"; + +export const IconArrowSmRight = () => { + return ( + + + + ); +}; diff --git a/assets/images/IconChevronDown.tsx b/assets/images/IconChevronDown.tsx new file mode 100644 index 0000000..c875786 --- /dev/null +++ b/assets/images/IconChevronDown.tsx @@ -0,0 +1,14 @@ +import Svg, { Path } from "react-native-svg"; + +export const IconChevronDown = () => { + return ( + + + + ); +}; diff --git a/assets/images/IconChevronUp.tsx b/assets/images/IconChevronUp.tsx new file mode 100644 index 0000000..e87138f --- /dev/null +++ b/assets/images/IconChevronUp.tsx @@ -0,0 +1,14 @@ +import Svg, { Path } from "react-native-svg"; + +export const IconChevronUp = () => { + return ( + + + + ); +}; diff --git a/assets/images/appwrite.png b/assets/images/appwrite.png new file mode 100644 index 0000000..9ef3337 Binary files /dev/null and b/assets/images/appwrite.png differ diff --git a/assets/images/appwrite@2x.png b/assets/images/appwrite@2x.png new file mode 100644 index 0000000..273c5df Binary files /dev/null and b/assets/images/appwrite@2x.png differ diff --git a/assets/images/appwrite@3x.png b/assets/images/appwrite@3x.png new file mode 100644 index 0000000..d09274c Binary files /dev/null and b/assets/images/appwrite@3x.png differ diff --git a/assets/images/grid.png b/assets/images/grid.png new file mode 100644 index 0000000..7cb17df Binary files /dev/null and b/assets/images/grid.png differ diff --git a/assets/images/grid@2x.png b/assets/images/grid@2x.png new file mode 100644 index 0000000..43e0892 Binary files /dev/null and b/assets/images/grid@2x.png differ diff --git a/assets/images/grid@3x.png b/assets/images/grid@3x.png new file mode 100644 index 0000000..5e3e107 Binary files /dev/null and b/assets/images/grid@3x.png differ diff --git a/assets/images/rn.png b/assets/images/rn.png new file mode 100644 index 0000000..d8bb82b Binary files /dev/null and b/assets/images/rn.png differ diff --git a/assets/images/rn@2x.png b/assets/images/rn@2x.png new file mode 100644 index 0000000..64720e9 Binary files /dev/null and b/assets/images/rn@2x.png differ diff --git a/assets/images/rn@3x.png b/assets/images/rn@3x.png new file mode 100644 index 0000000..f1118e6 Binary files /dev/null and b/assets/images/rn@3x.png differ diff --git a/assets/images/splash-icon.png b/assets/images/splash-icon.png new file mode 100644 index 0000000..00a960b Binary files /dev/null and b/assets/images/splash-icon.png differ diff --git a/components/Button.tsx b/components/Button.tsx new file mode 100644 index 0000000..f96bc07 --- /dev/null +++ b/components/Button.tsx @@ -0,0 +1,31 @@ +import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; + +interface ButtonProps { + text: string; + onPress: () => void; +} + +export const Button = ({ text, onPress }: ButtonProps) => { + return ( + + + {text} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + backgroundColor: "#FD366E", + paddingBlock: 10, + paddingHorizontal: 12, + borderRadius: 8, + }, + text: { + color: "#FFF", + fontWeight: 500, + fontSize: 14, + lineHeight: 20, + }, +}); diff --git a/components/Card.tsx b/components/Card.tsx new file mode 100644 index 0000000..fa8d97d --- /dev/null +++ b/components/Card.tsx @@ -0,0 +1,33 @@ +import { ReactNode } from "react"; +import { Linking, StyleSheet, TouchableOpacity, View } from "react-native"; + +interface CardProps { + children: ReactNode; + href?: string; +} + +export const Card = ({ children, href }: CardProps) => { + if (href) { + return ( + { + Linking.openURL(href); + }} + > + {children} + + ); + } + return {children}; +}; + +const styles = StyleSheet.create({ + card: { + flex: 1, + backgroundColor: "#FFF", + borderColor: "#EDEDF0", + borderWidth: 1, + borderRadius: 8, + padding: 16, + }, +}); diff --git a/components/Code.tsx b/components/Code.tsx new file mode 100644 index 0000000..9956775 --- /dev/null +++ b/components/Code.tsx @@ -0,0 +1,37 @@ +import { StyleSheet, Text, View } from "react-native"; + +interface CodeProps { + children: string; + variant: "primary" | "secondary"; +} +export const Code = ({ children, variant }: CodeProps) => { + return ( + + + {children} + + + ); +}; +const styles = StyleSheet.create({ + codeContainer: { + paddingInline: 6, + borderRadius: 4, + backgroundColor: "#EDEDF0", + }, + codeContainerSecondary: {}, + code: { + fontSize: 14, + }, +}); diff --git a/components/Header.tsx b/components/Header.tsx new file mode 100644 index 0000000..157aca5 --- /dev/null +++ b/components/Header.tsx @@ -0,0 +1,132 @@ +import { Text, StyleSheet, View, ActivityIndicator } from "react-native"; +import { Image } from "expo-image"; +import { LoadingLine } from "@/components/status-lines/LoadingLine"; +import { SuccessLine } from "@/components/status-lines/SuccessLine"; +import { fontStyles } from "@/styles/font"; +import { Button } from "@/components/Button"; + +interface HeaderProps { + state: "idle" | "loading" | "success" | "error"; + pingFunction: () => void; +} + +export const Header = ({ state, pingFunction }: HeaderProps) => { + const getStateLine = () => { + switch (state) { + case "success": + return ; + case "loading": + return ; + default: + return <>; + } + }; + + const getTitleText = () => { + switch (state) { + case "success": + return "Congratulations!"; + case "loading": + return " "; + default: + return "Check connection"; + } + }; + + const getDescriptionText = () => { + switch (state) { + case "loading": + return ( + + + Waiting for connection... + + ); + case "success": + return ( + + You connected to your app successfully + + ); + default: + return ( + + Send a ping to verify the connection + + ); + } + }; + + return ( + + + + + + {getStateLine()} + + + + {getTitleText()} + {getDescriptionText()} + {state !== "loading" && ( + +