Skip to content

Django RN demo app: fix some issues picked up during Django backend cleanup #217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eighty-pots-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'django-react-native-todolist': minor
---

Use react hooks. Remove Mobx stores.
29 changes: 24 additions & 5 deletions demos/django-react-native-todolist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,31 @@ Run on Android
pnpm android
```

## Set up Django Backend
## Service Configuration

This demo can be used with cloud or local services.

### Local Services

The [Self Hosting Demo](https://github.com/powersync-ja/self-host-demo) repository contains a Docker Compose Django backend demo which can be used with this client.
See [instructions](https://github.com/powersync-ja/self-host-demo/blob/feature/django-backend/demos/django/README.md) for starting the backend locally.

#### Android

Note that Android requires port forwarding of local services. These can be configured with ADB as below:

```bash
adb reverse tcp:8080 tcp:8080 && adb reverse tcp:6061 tcp:6061
```

### Cloud Services

#### Set up Django Backend

This demo requires that you have the [PowerSync Django Backend: Todo List Demo](https://github.com/powersync-ja/powersync-django-backend-todolist-demo) running on your machine.
Follow the guide in the README of the PowerSync Django Backend to set it up.

## Set up PowerSync Instance
#### Set up PowerSync Instance

Create a new PowerSync instance, connecting to the database of the Supabase project.

Expand All @@ -42,11 +61,11 @@ bucket_definitions:
# Separate bucket per todo list
parameters: select id as list_id from lists where owner_id = token_parameters.user_id
data:
- select * from api_list
- select * from api_todo
- select * from lists
- select * from todos
```

## Configure The App
#### Configure The App

Copy the `AppConfig.template.ts` to a usable file

Expand Down
19 changes: 11 additions & 8 deletions demos/django-react-native-todolist/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Stack } from 'expo-router';
import React from 'react';
import { PowerSyncContext } from '@powersync/react';
import { useSystem } from '../library/stores/system';

/**
* This App uses a nested navigation stack.
Expand All @@ -16,14 +17,16 @@ import React from 'react';
* - Sign out. Psuedo view to initiate signout flow. Navigates back to first layer.
*/
const HomeLayout = () => {
const system = useSystem();
return (
<Stack screenOptions={{ headerTintColor: '#fff', headerStyle: { backgroundColor: '#2196f3' } }}>
<Stack.Screen name="signin" options={{ title: 'Supabase Login' }} />
<Stack.Screen name="register" options={{ title: 'Register' }} />

<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="views" options={{ headerShown: false }} />
</Stack>
<PowerSyncContext.Provider value={system.powersync}>
<Stack screenOptions={{ headerTintColor: '#fff', headerStyle: { backgroundColor: '#2196f3' } }}>
<Stack.Screen name="signin" options={{ title: 'Supabase Login' }} />
<Stack.Screen name="register" options={{ title: 'Register' }} />
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="views" options={{ headerShown: false }} />
</Stack>
</PowerSyncContext.Provider>
);
};

Expand Down
23 changes: 3 additions & 20 deletions demos/django-react-native-todolist/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as React from 'react';
import { ActivityIndicator, View } from 'react-native';
import { observer } from 'mobx-react-lite';
import { useSystem } from '../library/stores/system';
import { router } from 'expo-router';
import Logger from 'js-logger';
/**
Expand All @@ -10,34 +8,19 @@ import Logger from 'js-logger';
* - If one is present redirect to app views.
* - If not, reditect to login/register flow
*/
const App = observer(() => {
const { djangoConnector } = useSystem();

const App = () => {
React.useEffect(() => {
Logger.useDefaults();
Logger.setLevel(Logger.DEBUG);

const getSession = async () => {
const response = await fetch('http://127.0.0.1:8000/api/get_session', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously the URL was hardcoded, but besides that the get_session endpoint always returns a valid session. Which makes this logic not really have an affect.

The user should now be able to register and login - although that is not strictly required by the backend (it will create tokens for PowerSync either way). This login screen process does at least keep this demo in line with other Todolist demos. Correct session management can be added later. Users will unfortunately have to signin each time the app boots. Alternatively signin could just be dropped altogether.

method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data) {
router.replace('signin');
}
};

getSession();
setImmediate(() => router.replace('signin'));
}, []);

return (
<View style={{ flex: 1, flexGrow: 1, alignContent: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
});
};

export default App;
17 changes: 4 additions & 13 deletions demos/django-react-native-todolist/app/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useSystem } from '../library/stores/system';
import { TextInputWidget } from '../library/widgets/TextInputWidget';
import { SignInStyles } from './signin';
import { Icon } from 'react-native-elements';
import { router } from 'expo-router';

export default function Register() {
const { djangoConnector } = useSystem();
Expand All @@ -24,6 +25,7 @@ export default function Register() {

<TextInputWidget
placeholder="Username"
autoCapitalize="none"
onChangeText={(value) => setCredentials({ ...credentials, username: value })}
/>
<TextInputWidget
Expand All @@ -39,19 +41,8 @@ export default function Register() {
setLoading(true);
setError('');
try {
// const { data, error } = await djangoConnector.supabaseClient.auth.signUp({
// email: credentials.username,
// password: credentials.password
// });
// if (error) {
// throw error;
// }
// if (data.session) {
// // djangoConnector.supabaseClient.auth.setSession(data.session);
// router.replace('views/todos/lists');
// } else {
// router.replace('signin');
// }
await djangoConnector.register(credentials.username, credentials.password);
router.replace('signin');
} catch (ex: any) {
console.error(ex);
setError(ex.message || 'Could not register');
Expand Down
2 changes: 2 additions & 0 deletions demos/django-react-native-todolist/app/signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ export default function Signin() {
<TextInputWidget
style={SignInStyles.input}
placeholder="Username"
autoCapitalize='none'
onChangeText={(value) => setCredentials({ ...credentials, username: value.toLowerCase().trim() })}
/>
<TextInputWidget
style={SignInStyles.input}
placeholder="Password"
secureTextEntry={true}
autoCapitalize='none'
onChangeText={(value) => setCredentials({ ...credentials, password: value })}
/>
{error ? <Text style={{ color: 'red' }}>{error}</Text> : null}
Expand Down
3 changes: 1 addition & 2 deletions demos/django-react-native-todolist/app/views/signout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { router } from 'expo-router';
import { useSystem } from '../../library/stores/system';

export default function Signout() {
const { powersync, supabaseConnector } = useSystem();
const { powersync } = useSystem();

React.useEffect(() => {
(async () => {
await powersync.disconnectAndClear();
await supabaseConnector.supabaseClient.auth.signOut();
router.replace('signin');
})();
}, []);
Expand Down
77 changes: 15 additions & 62 deletions demos/django-react-native-todolist/app/views/todos/edit/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,29 @@
import * as React from 'react';
import { StatusBar } from 'expo-status-bar';
import { ScrollView, View, Text } from 'react-native';
import { FAB } from 'react-native-elements';
import { observer } from 'mobx-react-lite';
import { Stack, useLocalSearchParams } from 'expo-router';
import prompt from 'react-native-prompt-android';
import { View, Text, ActivityIndicator } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import { useQuery } from '@powersync/react';
import { ListTodosWidget } from '../../../../library/widgets/ListTodosWidget';
import { LIST_TABLE, ListRecord } from '../../../../library/powersync/AppSchema';

import { useSystem } from '../../../../library/stores/system';
import { TodoItemWidget } from '../../../../library/widgets/TodoItemWidget';

const TodoView = observer(() => {
const { listStore, todoStore } = useSystem();
const TodoView = () => {
const params = useLocalSearchParams<{ id: string }>();

const id = params.id;
const listModel = React.useMemo(() => {
if (!id) {
return null;
}
const listModel = listStore.getById(id);

return listModel;
}, [id]);
const { data: result, isLoading } = useQuery<ListRecord>(`SELECT * FROM ${LIST_TABLE} WHERE id = ?`, [id]);
const listRecord = result[0];

if (!listModel) {
if (!listRecord && !isLoading) {
return (
<View>
<Text>No matching List found</Text>
<Text>No matching list found</Text>
</View>
);
}

return (
<View style={{ flexGrow: 1 }}>
<Stack.Screen
options={{
title: listModel.record.name
}}
/>
<FAB
style={{ zIndex: 99, bottom: 0 }}
icon={{ name: 'add', color: 'white' }}
size="small"
placement="right"
onPress={() => {
prompt(
'Add a new Todo',
'',
(text) => {
if (!text) {
return;
}

todoStore.createModel({
list_id: listModel.id,
description: text,
completed: false
});
},
{ placeholder: 'Todo description', style: 'shimo' }
);
}}
/>
<ScrollView style={{ maxHeight: '90%' }}>
{listModel.todos.map((t) => {
return <TodoItemWidget key={t.id} model={t} />;
})}
</ScrollView>
if (isLoading) {
return <ActivityIndicator />;
}

<StatusBar style={'light'} />
</View>
);
});
return <ListTodosWidget record={listRecord} />;
};

export default TodoView;
27 changes: 16 additions & 11 deletions demos/django-react-native-todolist/app/views/todos/lists.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import * as React from 'react';
import { StatusBar } from 'expo-status-bar';
import { ScrollView, View } from 'react-native';
import { FAB } from 'react-native-elements';
import { observer } from 'mobx-react-lite';
import prompt from 'react-native-prompt-android';

import { useSystem } from '../../../library/stores/system';
import { ListItemWidget } from '../../../library/widgets/ListItemWidget';
import { Stack } from 'expo-router';
import { useQuery } from '@powersync/react';
import { LIST_TABLE } from '../../../library/powersync/AppSchema';

const App = () => {
const system = useSystem();
const { data: lists } = useQuery(`SELECT * FROM ${LIST_TABLE}`);

const App = observer(() => {
const { listStore } = useSystem();
return (
<View style={{ flex: 1, flexGrow: 1 }}>
<Stack.Screen
Expand All @@ -27,28 +29,31 @@ const App = observer(() => {
prompt(
'Add a new list',
'',
(text) => {
async (text) => {
if (!text) {
return;
}

listStore.createModel({
name: text
});
const { userID } = await system.djangoConnector.fetchCredentials();

await system.powersync.execute(
`INSERT INTO ${LIST_TABLE} (id, created_at, name, owner_id) VALUES (uuid(), datetime(), ?, ?)`,
[text, userID]
);
},
{ placeholder: 'List name', style: 'shimo' }
);
}}
/>
<ScrollView style={{ maxHeight: '90%' }}>
{listStore.collection.map((t) => (
<ListItemWidget key={t.id} model={t} />
{lists.map((t) => (
<ListItemWidget key={t.id} record={t} />
))}
</ScrollView>

<StatusBar style={'light'} />
</View>
);
});
};

export default App;
2 changes: 1 addition & 1 deletion demos/django-react-native-todolist/database.sql
Original file line number Diff line number Diff line change
@@ -1 +1 @@
create publication powersync for table api_list, api_todo;
create publication powersync for table lists, todos;
Loading
Loading