Skip to content

feat(react): add useQuery and useStatus hooks #142

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 21 commits into from
Apr 30, 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
11 changes: 11 additions & 0 deletions .changeset/strange-spiders-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"react-native-supabase-group-chat": minor
"react-native-supabase-todolist": minor
"yjs-react-supabase-text-collab": minor
"django-react-native-todolist": minor
"react-supabase-todolist": minor
"example-nextjs": minor
"@powersync/react": minor
---

Deprecate usePowerSyncStatus, usePowerSyncQuery and usePowerSyncWatchedQuery in favor of useQuery and useStatus
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Alert, Text } from 'react-native';
import { Icon } from 'react-native-elements';
import { useNavigation } from 'expo-router';
import { useSystem } from '../stores/system';
import { usePowerSyncStatus } from '@powersync/react';
import { useStatus } from '@powersync/react';
import { Header } from 'react-native-elements';
import { observer } from 'mobx-react-lite';
import { DrawerActions } from '@react-navigation/native';
Expand All @@ -13,7 +13,7 @@ export const HeaderWidget: React.FC<{
}> = observer((props) => {
const { title } = props;
const { powersync } = useSystem();
const status = usePowerSyncStatus();
const status = useStatus();
const navigation = useNavigation();

return (
Expand Down
10 changes: 6 additions & 4 deletions demos/example-nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import React, { useEffect } from 'react';
import { CircularProgress, Grid, ListItem, styled } from '@mui/material';
import { useRouter } from 'next/navigation';
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react';
import { usePowerSync, useQuery } from '@powersync/react';

export default function EntryPage() {
const router = useRouter();
const db = usePowerSync();
const customers = usePowerSyncWatchedQuery('SELECT id, name FROM customers');
const { data: customers, isLoading } = useQuery('SELECT id, name FROM customers');

useEffect(() => {
// Insert some test data
Expand All @@ -18,6 +16,10 @@ export default function EntryPage() {
return () => {};
}, []);

if (isLoading) {
return <CircularProgress />;
}

return (
<S.MainGrid container>
<S.CenteredGrid item xs={12} md={6} lg={5}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker';
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { FlashList } from '@shopify/flash-list';
import { Stack, useLocalSearchParams } from 'expo-router';
import { useEffect, useState } from 'react';
Expand All @@ -13,12 +13,12 @@ export default function ChatsChatIndex() {
const { profile: profileId } = useLocalSearchParams<{ profile: string }>();
const { user } = useAuth();
const powerSync = usePowerSync();
const profiles = usePowerSyncWatchedQuery('SELECT id, name, handle, demo FROM profiles WHERE id = ?', [profileId]);
const { data: profiles } = useQuery('SELECT id, name, handle, demo FROM profiles WHERE id = ?', [profileId]);
const profile = profiles.length ? profiles[0] : undefined;
const [draftId, setDraftId] = useState<string>();
const [listMessages, setListMessages] = useState<any[]>([]);

const messages = usePowerSyncWatchedQuery(
const { data: messages } = useQuery(
'SELECT sender_id, content, created_at FROM messages WHERE (((sender_id = ?1 AND recipient_id = ?2) OR (sender_id = ?2 AND recipient_id = ?1)) AND NOT (sender_id = ?1 AND sent_at IS NULL)) ORDER BY created_at ASC',
[user?.id, profile?.id]
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { FlashList } from '@shopify/flash-list';
import { Stack, useLocalSearchParams } from 'expo-router';
import { useState } from 'react';
Expand All @@ -12,10 +12,10 @@ export default function ChatsChatIndex() {
const { group: groupId } = useLocalSearchParams<{ group: string }>();
const { user } = useAuth();
const powerSync = usePowerSync();
const groups = usePowerSyncWatchedQuery('SELECT id, name FROM groups WHERE id = ?', [groupId]);
const { data: groups } = useQuery('SELECT id, name FROM groups WHERE id = ?', [groupId]);
const group = groups.length ? groups[0] : undefined;

const messages = usePowerSyncWatchedQuery(
const { data: messages } = useQuery(
'SELECT sender_id, content, created_at FROM messages WHERE group_id = ? ORDER BY created_at ASC',
[group?.id]
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { Save, Trash, XCircle } from '@tamagui/lucide-icons';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
Expand All @@ -16,8 +16,8 @@ export default function GroupSettings() {
const [selectedContacts, setSelectedContacts] = useState<Set<string>>(new Set());
const powerSync = usePowerSync();

const groups = usePowerSyncWatchedQuery('SELECT name FROM groups WHERE id = ?', [groupId]);
const groupMembers = usePowerSyncWatchedQuery('SELECT profile_id FROM memberships WHERE group_id = ?', [groupId]);
const { data: groups } = useQuery('SELECT name FROM groups WHERE id = ?', [groupId]);
const { data: groupMembers } = useQuery('SELECT profile_id FROM memberships WHERE group_id = ?', [groupId]);

useEffect(() => {
if (groups.length > 0) {
Expand Down Expand Up @@ -74,16 +74,13 @@ export default function GroupSettings() {

await powerSync.writeTransaction(async (tx) => {
try {
await tx.executeAsync('UPDATE groups SET name= ? WHERE id = ?', [name, groupId]);
await tx.execute('UPDATE groups SET name= ? WHERE id = ?', [name, groupId]);
for (const profileId of removedContacts) {
const result = await tx.executeAsync('DELETE FROM memberships WHERE group_id = ? AND profile_id = ?', [
groupId,
profileId
]);
await tx.execute('DELETE FROM memberships WHERE group_id = ? AND profile_id = ?', [groupId, profileId]);
}
for (const profileId of addedContacts) {
const membershipId = uuid();
const result = await tx.executeAsync(
await tx.execute(
'INSERT INTO memberships (id, group_id, profile_id, created_at) VALUES (?, ?, ?, datetime())',
[membershipId, groupId, profileId]
);
Expand All @@ -99,9 +96,9 @@ export default function GroupSettings() {
async function deleteTransaction() {
await powerSync.writeTransaction(async (tx) => {
try {
await tx.executeAsync('DELETE FROM memberships WHERE group_id = ?', [groupId]);
await tx.executeAsync('DELETE FROM messages WHERE group_id = ?', [groupId]);
await tx.executeAsync('DELETE FROM groups WHERE id = ?', [groupId]);
await tx.execute('DELETE FROM memberships WHERE group_id = ?', [groupId]);
await tx.execute('DELETE FROM messages WHERE group_id = ?', [groupId]);
await tx.execute('DELETE FROM groups WHERE id = ?', [groupId]);

router.back();
} catch (error) {
Expand Down Expand Up @@ -148,8 +145,7 @@ export default function GroupSettings() {
backgroundColor="$red10"
color="white"
onPress={handleDelete}
margin="$3"
>
margin="$3">
Delete group
</Button>
</YStack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { MessageSquare, Plus } from '@tamagui/lucide-icons';
import { Link, useNavigation } from 'expo-router';
import { useEffect, useState } from 'react';
Expand All @@ -24,7 +24,7 @@ export default function ChatsIndex() {
return unsubscribe;
}, [navigation]);

const chats = usePowerSyncWatchedQuery(
const { data: chats } = useQuery(
`SELECT profiles.id as partner_id, profiles.name as name, profiles.handle as handle, 'contact' as type, m.created_at as last_message_at FROM chats LEFT JOIN profiles on chats.id = profiles.id LEFT JOIN (SELECT * FROM messages WHERE (sender_id, recipient_id, created_at) IN (SELECT sender_id, recipient_id, MAX(created_at) FROM messages GROUP BY group_id)) as m ON m.recipient_id = chats.id OR m.sender_id = chats.id WHERE (name LIKE '%' || ?1 || '%' OR handle LIKE '%' || ?1 || '%') GROUP BY profiles.id UNION
SELECT groups.id as partner_id, groups.name as name, '' as handle, 'group' as type, m.created_at as last_message_at FROM groups LEFT JOIN (SELECT * FROM messages WHERE (group_id, created_at) IN (SELECT group_id, MAX(created_at) FROM messages GROUP BY group_id)) as m ON m.group_id = groups.id WHERE (name LIKE '%' || ?1 || '%') GROUP BY groups.id ORDER BY last_message_at DESC`,
[search]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker';
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { Search, Shuffle } from '@tamagui/lucide-icons';
import { useState } from 'react';
import { Button, Input, XStack, YStack } from 'tamagui';
Expand All @@ -17,7 +17,7 @@ export default function ContactsIndex() {
const [search, setSearch] = useState<string>('');
const [profiles, setProfiles] = useState<any[]>([]);

const contacts = usePowerSyncWatchedQuery(
const { data: contacts } = useQuery(
"SELECT contacts.id, profiles.id as profile_id, profiles.name, profiles.handle, 'contact' as type FROM contacts LEFT JOIN profiles ON contacts.profile_id = profiles.id WHERE (profiles.name LIKE '%' || ?1 || '%' OR profiles.handle LIKE '%' || ?1 || '%') ORDER BY name ASC",
[search]
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { useEffect, useState } from 'react';
import { Button, Input, Label, Switch, Text, XStack, YStack } from 'tamagui';

Expand All @@ -10,7 +10,7 @@ export default function SettingsIndex() {
const [name, setName] = useState('');
const [handle, setHandle] = useState('');

const profiles = usePowerSyncWatchedQuery('SELECT * FROM profiles WHERE id = ?', [user?.id]);
const { data: profiles } = useQuery('SELECT * FROM profiles WHERE id = ?', [user?.id]);

useEffect(() => {
if (profiles.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { usePowerSyncWatchedQuery } from '@powersync/react-native';
import { useQuery } from '@powersync/react-native';
import { CheckCircle2, Circle } from '@tamagui/lucide-icons';
import { useState } from 'react';
import { Input, ListItem, XStack, YStack } from 'tamagui';
Expand All @@ -15,7 +15,7 @@ export function MemberSelector({
}) {
const [search, setSearch] = useState<string>('');

const contacts = usePowerSyncWatchedQuery(
const { data: contacts } = useQuery(
"SELECT contacts.id, profiles.id as profile_id, profiles.name, profiles.handle, 'contact' as type FROM contacts LEFT JOIN profiles ON contacts.profile_id = profiles.id WHERE (profiles.name LIKE '%' || ?1 || '%' OR profiles.handle LIKE '%' || ?1 || '%') ORDER BY name ASC",
[search]
);
Expand Down
16 changes: 11 additions & 5 deletions demos/react-native-supabase-todolist/app/views/todos/edit/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ATTACHMENT_TABLE, AttachmentRecord } from '@powersync/attachments';
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react-native';
import { usePowerSync, useQuery } from '@powersync/react-native';
import { CameraCapturedPicture } from 'expo-camera';
import _ from 'lodash';
import * as React from 'react';
Expand Down Expand Up @@ -34,11 +34,11 @@ const TodoView: React.FC = () => {
const params = useLocalSearchParams<{ id: string }>();
const listID = params.id;

const [listRecord] = usePowerSyncWatchedQuery<{ name: string }>(`SELECT name FROM ${LIST_TABLE} WHERE id = ?`, [
listID
]);
const {
data: [listRecord]
} = useQuery<{ name: string }>(`SELECT name FROM ${LIST_TABLE} WHERE id = ?`, [listID]);

const todos = usePowerSyncWatchedQuery<TodoEntry>(
const { data: todos, isLoading } = useQuery<TodoEntry>(
`
SELECT
${TODO_TABLE}.id AS todo_id,
Expand Down Expand Up @@ -105,6 +105,12 @@ const TodoView: React.FC = () => {
});
};

if (isLoading) {
<View>
<Text>Loading...</Text>
</View>;
}

if (listRecord == null) {
return (
<View>
Expand Down
37 changes: 21 additions & 16 deletions demos/react-native-supabase-todolist/app/views/todos/lists.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import prompt from 'react-native-prompt-android';
import { router, Stack } from 'expo-router';
import { LIST_TABLE, TODO_TABLE, ListRecord } from '../../../library/powersync/AppSchema';
import { useSystem } from '../../../library/powersync/system';
import { usePowerSyncWatchedQuery } from '@powersync/react-native';
import { useQuery, useStatus } from '@powersync/react-native';
import { ListItemWidget } from '../../../library/widgets/ListItemWidget';

const description = (total: number, completed: number = 0) => {
Expand All @@ -16,7 +16,8 @@ const description = (total: number, completed: number = 0) => {

const ListsViewWidget: React.FC = () => {
const system = useSystem();
const listRecords = usePowerSyncWatchedQuery<ListRecord & { total_tasks: number; completed_tasks: number }>(`
const status = useStatus();
const { data: listRecords } = useQuery<ListRecord & { total_tasks: number; completed_tasks: number }>(`
SELECT
${LIST_TABLE}.*, COUNT(${TODO_TABLE}.id) AS total_tasks, SUM(CASE WHEN ${TODO_TABLE}.completed = true THEN 1 ELSE 0 END) as completed_tasks
FROM
Expand Down Expand Up @@ -77,20 +78,24 @@ const ListsViewWidget: React.FC = () => {
}}
/>
<ScrollView key={'lists'} style={{ maxHeight: '90%' }}>
{listRecords.map((r) => (
<ListItemWidget
key={r.id}
title={r.name}
description={description(r.total_tasks, r.completed_tasks)}
onDelete={() => deleteList(r.id)}
onPress={() => {
router.push({
pathname: 'views/todos/edit/[id]',
params: { id: r.id }
});
}}
/>
))}
{!status.hasSynced ? (
<p>Busy with sync...</p>
) : (
listRecords.map((r) => (
<ListItemWidget
key={r.id}
title={r.name}
description={description(r.total_tasks, r.completed_tasks)}
onDelete={() => deleteList(r.id)}
onPress={() => {
router.push({
pathname: 'views/todos/edit/[id]',
params: { id: r.id }
});
}}
/>
))
)}
</ScrollView>

<StatusBar style={'light'} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Alert, Text } from 'react-native';
import { Icon } from 'react-native-elements';
import { useNavigation } from 'expo-router';
import { Header } from 'react-native-elements';
import { usePowerSyncStatus } from '@powersync/react';
import { useStatus } from '@powersync/react';
import { DrawerActions } from '@react-navigation/native';
import { useSystem } from '../powersync/system';

Expand All @@ -13,7 +13,7 @@ export const HeaderWidget: React.FC<{
const system = useSystem();
const { powersync } = system;
const navigation = useNavigation();
const status = usePowerSyncStatus();
const status = useStatus();

const { title } = props;
return (
Expand Down
4 changes: 2 additions & 2 deletions demos/react-supabase-todolist/src/app/views/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import React from 'react';

import { useNavigationPanel } from '@/components/navigation/NavigationPanelContext';
import { useSupabase } from '@/components/providers/SystemProvider';
import { usePowerSync, usePowerSyncStatus } from '@powersync/react';
import { usePowerSync, useStatus } from '@powersync/react';
import { useNavigate } from 'react-router-dom';
import { LOGIN_ROUTE, SQL_CONSOLE_ROUTE, TODO_LISTS_ROUTE } from '@/app/router';

export default function ViewsLayout({ children }: { children: React.ReactNode }) {
const powerSync = usePowerSync();
const status = usePowerSyncStatus();
const status = useStatus();
const supabase = useSupabase();
const navigate = useNavigate();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { usePowerSyncWatchedQuery } from '@powersync/react';
import { useQuery } from '@powersync/react';
import { Box, Button, Grid, TextField, styled } from '@mui/material';
import { DataGrid } from '@mui/x-data-grid';
import { NavigationPage } from '@/components/navigation/NavigationPage';
Expand All @@ -14,7 +14,7 @@ const DEFAULT_QUERY = 'SELECT * FROM lists';
export default function SQLConsolePage() {
const inputRef = React.useRef<HTMLInputElement>();
const [query, setQuery] = React.useState(DEFAULT_QUERY);
const querySQLResult = usePowerSyncWatchedQuery(query);
const { data: querySQLResult } = useQuery(query);

const queryDataGridResult = React.useMemo(() => {
const firstItem = querySQLResult?.[0];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useSupabase } from '@/components/providers/SystemProvider';
import { TodoItemWidget } from '@/components/widgets/TodoItemWidget';
import { LISTS_TABLE, TODOS_TABLE, TodoRecord } from '@/library/powersync/AppSchema';
import { usePowerSync, usePowerSyncWatchedQuery } from '@powersync/react';
import { usePowerSync, useQuery } from '@powersync/react';
import AddIcon from '@mui/icons-material/Add';
import {
Box,
Expand Down Expand Up @@ -32,11 +32,11 @@ const TodoEditSection = () => {
const supabase = useSupabase();
const { id: listID } = useParams();

const [listRecord] = usePowerSyncWatchedQuery<{ name: string }>(`SELECT name FROM ${LISTS_TABLE} WHERE id = ?`, [
listID
]);
const {
data: [listRecord]
} = useQuery<{ name: string }>(`SELECT name FROM ${LISTS_TABLE} WHERE id = ?`, [listID]);

const todos = usePowerSyncWatchedQuery<TodoRecord>(
const { data: todos } = useQuery<TodoRecord>(
`SELECT * FROM ${TODOS_TABLE} WHERE list_id=? ORDER BY created_at DESC, id`,
[listID]
);
Expand Down
Loading