mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-09 21:07:46 +00:00
Merge branch 'main' into feat-improve-code-fence
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
type MappedTutorial = (typeof data.tutorials)[number];
|
||||
|
||||
const iconMap: Record<string, string> = {
|
||||
'react native': 'icon-react',
|
||||
react: 'icon-react',
|
||||
vue: 'web-icon-vue',
|
||||
angular: 'icon-angular',
|
||||
|
||||
@@ -17,7 +17,7 @@ This tutorial will introduce the following concepts:
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
5. Storage
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
1. Basic knowledge of Kotlin and Android development.
|
||||
|
||||
@@ -120,10 +120,10 @@ To catch that moment in time when the page loads, we call our `fetch` function,
|
||||
Second, it's likely that a user will want to delete one of their ideas from the database.
|
||||
We help them do that by adding a delete button in the top right corner of the idea list item, but only on the ideas added by the user itself.
|
||||
|
||||
Add the file `IdeasList` from the `componenents` directory and insert the following code:
|
||||
Add the file `IdeasList` from the `components` directory and insert the following code:
|
||||
|
||||
```vue
|
||||
<!-- componenents/IdeasList.vue -->
|
||||
<!-- components/IdeasList.vue -->
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
|
||||
10
src/routes/docs/tutorials/react-native/+layout.svelte
Normal file
10
src/routes/docs/tutorials/react-native/+layout.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { globToTutorial } from '$lib/utils/tutorials.js';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
export let data;
|
||||
const tutorials = globToTutorial(data);
|
||||
setContext('tutorials', tutorials);
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
11
src/routes/docs/tutorials/react-native/+layout.ts
Normal file
11
src/routes/docs/tutorials/react-native/+layout.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = ({ url }) => {
|
||||
const tutorials = import.meta.glob('./**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
return {
|
||||
tutorials,
|
||||
pathname: url.pathname
|
||||
};
|
||||
};
|
||||
6
src/routes/docs/tutorials/react-native/+page.ts
Normal file
6
src/routes/docs/tutorials/react-native/+page.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
redirect(303, '/docs/tutorials/react-native/step-1');
|
||||
};
|
||||
28
src/routes/docs/tutorials/react-native/step-1/+page.markdoc
Normal file
28
src/routes/docs/tutorials/react-native/step-1/+page.markdoc
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Build an ideas tracker with React Native
|
||||
description: Learn to build a React Native app with no backend code using an Appwrite backend.
|
||||
step: 1
|
||||
difficulty: beginner
|
||||
readtime: 10
|
||||
category: Mobile and native
|
||||
framework: React Native
|
||||
---
|
||||
|
||||
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish.
|
||||
In this tutorial, you will build Idea tracker with Appwrite and React Native.
|
||||
|
||||
# Concepts {% #concepts %}
|
||||
|
||||
This tutorial will introduce the following concepts:
|
||||
|
||||
1. Setting up your first project
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
|
||||
1. Android, iOS simulators, or a physical device to run the app
|
||||
2. Have [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer
|
||||
3. Basic knowledge of React Native and Expo
|
||||
43
src/routes/docs/tutorials/react-native/step-2/+page.markdoc
Normal file
43
src/routes/docs/tutorials/react-native/step-2/+page.markdoc
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create app
|
||||
description: Create a React Native app project and integrate with Appwrite.
|
||||
step: 2
|
||||
---
|
||||
|
||||
# Create React Native project {% #create-react-project %}
|
||||
|
||||
Create a React Native app with the `npm create` command.
|
||||
|
||||
```sh
|
||||
npx create-expo-app ideas-tracker
|
||||
cd ideas-tracker
|
||||
```
|
||||
|
||||
# Add dependencies {% #add-dependencies %}
|
||||
|
||||
Install the React Native Appwrite SDK.
|
||||
|
||||
```sh
|
||||
npx expo install react-native-appwrite react-native-url-polyfill
|
||||
```
|
||||
|
||||
Then, install React Navigation to help implement simple navigation logic.
|
||||
|
||||
```sh
|
||||
npm install @react-navigation/native @react-navigation/native-stack
|
||||
```
|
||||
|
||||
Install peer dependencies needed for React Navigation.
|
||||
|
||||
```sh
|
||||
npx expo install react-native-screens react-native-safe-area-context
|
||||
```
|
||||
|
||||
For iOS with bare React Native project, make sure you have CocoaPods installed. Then install the pods to complete the installation:
|
||||
|
||||
```
|
||||
cd ios
|
||||
pod install
|
||||
cd ..
|
||||
```
|
||||
60
src/routes/docs/tutorials/react-native/step-3/+page.markdoc
Normal file
60
src/routes/docs/tutorials/react-native/step-3/+page.markdoc
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Set up Appwrite
|
||||
description: Import and initialize Appwrite for your React Native application.
|
||||
step: 3
|
||||
---
|
||||
|
||||
# Create project {% #create-project %}
|
||||
|
||||
Head to the [Appwrite Console](https://cloud.appwrite.io/console).
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
If this is your first time using Appwrite, create an account and create your first project.
|
||||
|
||||
Then, under **Add a platform**, add a **Android** or **Apple** platform with the package/bundle ID `com.example.idea-tracker`.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
You can skip optional steps.
|
||||
|
||||
# Initialize Appwrite SDK {% #init-sdk %}
|
||||
|
||||
To use Appwrite in our React Native app, you'll need to find our project ID.
|
||||
Find your project's ID in the **Settings** page.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
Create a new file `lib/appwrite.js` to hold our Appwrite related code.
|
||||
Only one instance of the `Client()` class should be created per app.
|
||||
Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID.
|
||||
|
||||
```js
|
||||
import { Client, Databases, Account } from "react-native-appwrite";
|
||||
|
||||
const client = new Client();
|
||||
client
|
||||
.setEndpoint("https://cloud.appwrite.io/v1")
|
||||
.setProject("<YOUR_PROJECT_ID>") // Replace with your project ID
|
||||
.setPlatform('com.example.idea-tracker');
|
||||
|
||||
|
||||
export const account = new Account(client);
|
||||
export const databases = new Databases(client);
|
||||
```
|
||||
166
src/routes/docs/tutorials/react-native/step-4/+page.markdoc
Normal file
166
src/routes/docs/tutorials/react-native/step-4/+page.markdoc
Normal file
@@ -0,0 +1,166 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add authentication
|
||||
description: Add authentication to your React Native application.
|
||||
step: 4
|
||||
---
|
||||
|
||||
# User context {% #user-context %}
|
||||
|
||||
In React Native, you can use [context](https://reactjs.org/docs/context.html) to share data between components.
|
||||
You can use a context and a custom hook to manage our user's data.
|
||||
|
||||
Create a new file `contexts/UserContext.jsx` and add the following code to it.
|
||||
|
||||
```js
|
||||
import { ID } from "react-native-appwrite";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import { account } from "../lib/appwrite";
|
||||
import { toast } from "../lib/toast";
|
||||
|
||||
|
||||
const UserContext = createContext();
|
||||
|
||||
export function useUser() {
|
||||
return useContext(UserContext);
|
||||
}
|
||||
|
||||
export function UserProvider(props) {
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
async function login(email, password) {
|
||||
const loggedIn = await account.createEmailSession(email, password);
|
||||
setUser(loggedIn);
|
||||
toast('Welcome back. You are logged in');
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await account.deleteSession("current");
|
||||
setUser(null);
|
||||
toast('Logged out');
|
||||
}
|
||||
|
||||
async function register(email, password) {
|
||||
await account.create(ID.unique(), email, password);
|
||||
await login(email, password);
|
||||
toast('Account created');
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const loggedIn = await account.get();
|
||||
setUser(loggedIn);
|
||||
toast('Welcome back. You are logged in');
|
||||
} catch (err) {
|
||||
setUser(null);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={{ current: user, login, logout, register, toast }}>
|
||||
{props.children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can use the `useUser` hook to access the user's data from any component wrapped by this context's provider.
|
||||
|
||||
# Display toasts {% #display-toasts %}
|
||||
|
||||
For a better user experience, display toasts when the users perform an action, such as login, logout, create new ideas, etc.
|
||||
|
||||
We can do this by creating a new file `lib/toast.js` and adding the following code to it.
|
||||
|
||||
```js
|
||||
import { ToastAndroid, Platform, AlertIOS } from 'react-native';
|
||||
|
||||
export function toast(msg) {
|
||||
if (Platform.OS === 'android') {
|
||||
ToastAndroid.show(msg, ToastAndroid.SHORT)
|
||||
} else {
|
||||
AlertIOS.alert(msg);
|
||||
}
|
||||
}
|
||||
```
|
||||
# Login page {% #login-page %}
|
||||
|
||||
Create a new file `views/Login.jsx` and add the following code to it.
|
||||
this page contains a basic form to allow the user to login or register.
|
||||
Notice how this page consumes the `useUser` hook to access the user's data and perform login and register actions.
|
||||
|
||||
```js
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
|
||||
import { useUser } from '../contexts/UserContext';
|
||||
|
||||
|
||||
export default function LoginScreen() {
|
||||
const user = useUser();
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.header}>Login or register</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
title="Login"
|
||||
onPress={
|
||||
() => {
|
||||
user.login(email, password)
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title="Register"
|
||||
onPress={
|
||||
() => {
|
||||
user.register(email, password)
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
header: {
|
||||
fontSize: 24,
|
||||
marginBottom: 20,
|
||||
},
|
||||
input: {
|
||||
height: 40,
|
||||
borderColor: 'gray',
|
||||
borderWidth: 1,
|
||||
marginBottom: 10,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
});
|
||||
```
|
||||
106
src/routes/docs/tutorials/react-native/step-5/+page.markdoc
Normal file
106
src/routes/docs/tutorials/react-native/step-5/+page.markdoc
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add routing
|
||||
description: Add routing to your React Native applicating using Appwrite.
|
||||
step: 5
|
||||
---
|
||||
In this step, you'll add some basic routing to your app.
|
||||
Based on the user's login status, you'll redirect them to the login page or the home page.
|
||||
|
||||
# Home page {% #home-page %}
|
||||
|
||||
Create a new file `views/Home.jsx` and add the following stub code to it.
|
||||
We'll update this page later to display the ideas posted by other users and allow the user to post their ideas.
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import { useUser } from '../contexts/UserContext';
|
||||
|
||||
|
||||
export default function HomeScreen() {
|
||||
const user = useUser();
|
||||
|
||||
console.log(user.current);
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
Welcome, {user.current ? user.current.email : 'Please login'}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
# Basic routing {% #basic-routing %}
|
||||
To handle basic routing, you can use the `react-navigation` library.
|
||||
This router also consumes the `UserContext` to determine if the user is logged in or not to redirect the user automatically.
|
||||
|
||||
Create a file `lib/Router.jsx` and add the following code to it.
|
||||
```js
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import LoginScreen from '../views/Login';
|
||||
import HomeScreen from '../views/Home';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { useUser } from '../contexts/UserContext';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
export function Router() {
|
||||
const user = useUser();
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator>
|
||||
{user.current == null ? (
|
||||
<Stack.Screen
|
||||
name="Login"
|
||||
component={LoginScreen}
|
||||
options={{ title: 'Login' }}
|
||||
/>
|
||||
) : (
|
||||
<Stack.Screen
|
||||
name="Home"
|
||||
component={HomeScreen}
|
||||
options={{ title: 'Home' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
We'll display this router in the `App.js` file.
|
||||
|
||||
```js
|
||||
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { UserProvider } from './contexts/UserContext';
|
||||
import { Router } from './lib/Router';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<UserProvider>
|
||||
<Router />
|
||||
</UserProvider >
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
# Test the routing {% #test-routing %}
|
||||
Now, if you run the app, you should see the login page.
|
||||
If you create a new account, you should be logged in and redirected to the home page.
|
||||
|
||||
Run the app using the following command.
|
||||
```sh
|
||||
npm run start
|
||||
```
|
||||
137
src/routes/docs/tutorials/react-native/step-6/+page.markdoc
Normal file
137
src/routes/docs/tutorials/react-native/step-6/+page.markdoc
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Add database
|
||||
description: Connect a database to your React Native application using Appwrite Web SDK.
|
||||
step: 6
|
||||
---
|
||||
In this step, you'll set up a database to store ideas in Appwrite, configure permissions, then create a
|
||||
context to manage ideas in your React Native app.
|
||||
|
||||
# Create collection {% #create-collection %}
|
||||
In Appwrite, data is stored as a collection of documents. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas.
|
||||
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
Create a new collection with the following attributes:
|
||||
| Field | Type | Required |
|
||||
|-------------|--------|----------|
|
||||
| userId | String | Yes |
|
||||
| title | String | Yes |
|
||||
| description | String | No |
|
||||
|
||||
# Configure permissions {% #configure-permissions %}
|
||||
{% only_dark %}
|
||||

|
||||
{% /only_dark %}
|
||||
{% only_light %}
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
Navigate to the **Settings** tab of your collection, add the role **Any** and check the **Read** box.
|
||||
Next, add a **Users** role and give them access to **Create** by checking those boxes.
|
||||
These permissions apply to all documents in your new collection.
|
||||
|
||||
Finally, enable **Document security** to allow further permissions to be set at the document level.
|
||||
Remember to click the **Update** button to apply your changes.
|
||||
|
||||
# Ideas context {% #ideas-context %}
|
||||
|
||||
Now that you have a collection to hold ideas, we can read and write to it from our app.
|
||||
Like you did with the user data, we will create a React context to hold our ideas.
|
||||
Create a new file `contexts/IdeasContext.jsx` and add the following code to it.
|
||||
|
||||
```js
|
||||
import { ID, Permission, Role, Query } from "react-native-appwrite";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import { databases } from "../lib/appwrite";
|
||||
import { toast } from "../lib/toast";
|
||||
|
||||
export const IDEAS_DATABASE_ID = "default"; // Replace with your database ID
|
||||
export const IDEAS_COLLECTION_ID = "ideas-tracker"; // Replace with your collection ID
|
||||
|
||||
const IdeasContext = createContext();
|
||||
|
||||
export function useIdeas() {
|
||||
return useContext(IdeasContext);
|
||||
}
|
||||
|
||||
export function IdeasProvider(props) {
|
||||
const [ideas, setIdeas] = useState([]);
|
||||
|
||||
async function add(idea) {
|
||||
const response = await databases.createDocument(
|
||||
IDEAS_DATABASE_ID,
|
||||
IDEAS_COLLECTION_ID,
|
||||
ID.unique(),
|
||||
idea,
|
||||
[Permission.write(Role.user(idea.userId))]
|
||||
);
|
||||
toast('Ideas added');
|
||||
setIdeas((ideas) => [response, ...ideas].slice(0, 10));
|
||||
}
|
||||
|
||||
async function remove(id) {
|
||||
await databases.deleteDocument(IDEAS_DATABASE_ID, IDEAS_COLLECTION_ID, id);
|
||||
toast('Idea removed');
|
||||
setIdeas((ideas) => ideas.filter((idea) => idea.$id !== id));
|
||||
await init(); // Refetch ideas to ensure we have 10 items
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const response = await databases.listDocuments(
|
||||
IDEAS_DATABASE_ID,
|
||||
IDEAS_COLLECTION_ID,
|
||||
[Query.orderDesc("$createdAt"), Query.limit(10)]
|
||||
);
|
||||
setIdeas(response.documents);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IdeasContext.Provider value={{ current: ideas, add, remove }}>
|
||||
{props.children}
|
||||
</IdeasContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Notice that new ideas have the added permission `Permission.write(Role.user(idea.userId))`.
|
||||
This permission ensures that only the user who created the idea can modify it.
|
||||
|
||||
Remeber to add the `IdeasProvider` to your `App.js` file.
|
||||
|
||||
```js
|
||||
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { UserProvider } from './contexts/UserContext';
|
||||
import { IdeasProvider } from './contexts/IdeasContext'; // Add import
|
||||
import { Router } from './lib/Router';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<UserProvider>
|
||||
<!-- Add the Ideas Provider -->
|
||||
<IdeasProvider>
|
||||
<Router />
|
||||
</IdeasProvider>
|
||||
</UserProvider >
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
```
|
||||
131
src/routes/docs/tutorials/react-native/step-7/+page.markdoc
Normal file
131
src/routes/docs/tutorials/react-native/step-7/+page.markdoc
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create ideas page
|
||||
description: Add database queries and pagination using Appwrite in your React Native application.
|
||||
step: 7
|
||||
---
|
||||
|
||||
Using the `useIdeas` hook you can now display the ideas on the page and create a form to submit new ideas.
|
||||
|
||||
If an idea is submitted by the logged-in user, a remove button will be displayed to remove the idea.
|
||||
While this check uses the user ID to determine the render logic, permissions set in step 6 will be used to enforce
|
||||
that only the owner of the idea can remove it.
|
||||
|
||||
Overwrite the contents of `views/Home.jsx` with the following:
|
||||
|
||||
```js
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TextInput, Button, StyleSheet, ScrollView } from 'react-native';
|
||||
import { useUser } from '../contexts/UserContext';
|
||||
import { useIdeas } from '../contexts/IdeasContext';
|
||||
|
||||
|
||||
export default function HomeScreen() {
|
||||
const user = useUser();
|
||||
const ideas = useIdeas();
|
||||
|
||||
const [title, setTitle] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
return (
|
||||
<ScrollView>
|
||||
{user.current ? (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.header}>Submit Idea</Text>
|
||||
<View>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Title"
|
||||
value={title}
|
||||
onChangeText={(text) => setTitle(text)}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Description"
|
||||
value={description}
|
||||
onChangeText={(text) => setDescription(text)}
|
||||
/>
|
||||
<Button
|
||||
title="Submit"
|
||||
onPress={() =>
|
||||
ideas.add({ userId: user.current.$id, title, description })
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.section}>
|
||||
<Text>Please login to submit an idea.</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.header}>Latest Ideas</Text>
|
||||
<View>
|
||||
{ideas.current.map((idea) => (
|
||||
<View key={idea.$id} style={styles.card}>
|
||||
<Text style={styles.cardTitle}>{idea.title}</Text>
|
||||
<Text style={styles.cardDescription}>{idea.description}</Text>
|
||||
{/* Show the remove button to idea owner. */}
|
||||
{user.current && user.current.$id === idea.userId && (
|
||||
<Button
|
||||
title="Remove"
|
||||
onPress={() => ideas.remove(idea.$id)}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
section: {
|
||||
marginBottom: 20,
|
||||
padding: 16,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
header: {
|
||||
fontSize: 24,
|
||||
marginBottom: 20,
|
||||
},
|
||||
input: {
|
||||
height: 40,
|
||||
borderColor: 'gray',
|
||||
borderWidth: 1,
|
||||
marginBottom: 10,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
card: {
|
||||
backgroundColor: 'white',
|
||||
padding: 16,
|
||||
marginBottom: 16,
|
||||
borderRadius: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 8,
|
||||
},
|
||||
cardDescription: {
|
||||
fontSize: 14,
|
||||
marginBottom: 8,
|
||||
},
|
||||
});
|
||||
```
|
||||
14
src/routes/docs/tutorials/react-native/step-8/+page.markdoc
Normal file
14
src/routes/docs/tutorials/react-native/step-8/+page.markdoc
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Next steps
|
||||
description: Run your React Native project built with Appwrite
|
||||
step: 8
|
||||
---
|
||||
|
||||
# Test your project {% #test-project %}
|
||||
You can run your projects with `npm run start`. This will start the Metro bundler and open the Expo Go app on your device.
|
||||
You can also run your project on an emulator or simulator by pressing `i` for iOS and `a` for Android.
|
||||
|
||||
# Bundling for production {% #bundle-production %}
|
||||
Appwrite's React Native SDK is designed to work with the Expo Metro bundler.
|
||||
When you are ready to build your app for production, you can learn more in the [Expo documentation](https://docs.expo.dev/).
|
||||
@@ -27,7 +27,7 @@ This tutorial will introduce the following concepts:
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
5. Storage
|
||||
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
|
||||
@@ -9,7 +9,7 @@ In our app we want to have a navigation bar that is always visible. We will add
|
||||
- a logout button if the user is logged in.
|
||||
- a login button if the user is not logged in.
|
||||
|
||||
Update the App componenent in `src/App.jsx`:
|
||||
Update the App component in `src/App.jsx`:
|
||||
|
||||
```client-web
|
||||
import { Login } from "./pages/Login";
|
||||
|
||||
@@ -50,7 +50,7 @@ export function IdeasProvider(props) {
|
||||
ID.unique(),
|
||||
idea
|
||||
);
|
||||
setIdeas((ideas) => [response.$id, ...ideas].slice(0, 10));
|
||||
setIdeas((ideas) => [response, ...ideas].slice(0, 10));
|
||||
}
|
||||
|
||||
async function remove(id) {
|
||||
|
||||
@@ -29,7 +29,7 @@ This tutorial will introduce the following concepts:
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
5. Storage
|
||||
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
|
||||
@@ -26,7 +26,7 @@ This tutorial will introduce the following concepts:
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
5. Storage
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
1. Basic knowledge of JavaScript and Svelte.
|
||||
|
||||
@@ -16,7 +16,7 @@ import { getIdeas } from '$lib/ideas';
|
||||
|
||||
export async function load() {
|
||||
return {
|
||||
ideas: getIdeas()
|
||||
ideas: await getIdeas()
|
||||
};
|
||||
}
|
||||
```
|
||||
@@ -118,4 +118,4 @@ Simple as that! Now, let's create the page itself. Replace the contents in `src/
|
||||
</style>
|
||||
```
|
||||
|
||||
With this you have successfully created an Ideas Tracker! You can now submit ideas and view them.
|
||||
With this you have successfully created an Ideas Tracker! You can now submit ideas and view them.
|
||||
|
||||
@@ -26,7 +26,7 @@ This tutorial will introduce the following concepts:
|
||||
2. Authentication
|
||||
3. Databases and collections
|
||||
4. Queries and pagination
|
||||
5. Storage
|
||||
|
||||
|
||||
|
||||
# Prerequisites {% #prerequisites %}
|
||||
|
||||
@@ -9,7 +9,7 @@ In our app we want to have a navigation bar that is always visible. Use the `use
|
||||
- a logout button if the user is logged in.
|
||||
- a login button if the user is not logged in.
|
||||
|
||||
Update the App componenent in `src/App.vue`:
|
||||
Update the App component in `src/App.vue`:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
|
||||
Reference in New Issue
Block a user