11import { configureFts } from '@/app/utils/fts_setup' ;
2- import { AppSchema } from '@/library/powersync/AppSchema' ;
2+ import { AppSchema , ListRecord , LISTS_TABLE , TODOS_TABLE } from '@/library/powersync/AppSchema' ;
33import { SupabaseConnector } from '@/library/powersync/SupabaseConnector' ;
44import { CircularProgress } from '@mui/material' ;
55import { PowerSyncContext } from '@powersync/react' ;
6- import { createBaseLogger , LogLevel , PowerSyncDatabase } from '@powersync/web' ;
6+ import {
7+ ArrayComparator ,
8+ createBaseLogger ,
9+ GetAllQuery ,
10+ IncrementalWatchMode ,
11+ LogLevel ,
12+ PowerSyncDatabase ,
13+ WatchedQuery
14+ } from '@powersync/web' ;
715import React , { Suspense } from 'react' ;
816import { NavigationPanelContextProvider } from '../navigation/NavigationPanelContext' ;
917
@@ -17,10 +25,65 @@ export const db = new PowerSyncDatabase({
1725 }
1826} ) ;
1927
28+ export type EnhancedListRecord = ListRecord & { total_tasks : number ; completed_tasks : number } ;
29+
30+ export type QueryStore = {
31+ lists : WatchedQuery < EnhancedListRecord [ ] > ;
32+ } ;
33+
34+ const QueryStore = React . createContext < QueryStore | null > ( null ) ;
35+ export const useQueryStore = ( ) => React . useContext ( QueryStore ) ;
36+
2037export const SystemProvider = ( { children } : { children : React . ReactNode } ) => {
21- const [ connector ] = React . useState ( new SupabaseConnector ( ) ) ;
38+ const [ connector ] = React . useState ( ( ) => new SupabaseConnector ( ) ) ;
2239 const [ powerSync ] = React . useState ( db ) ;
2340
41+ const [ queryStore ] = React . useState < QueryStore > ( ( ) => {
42+ const listsQuery = db . incrementalWatch ( { mode : IncrementalWatchMode . COMPARISON } ) . build ( {
43+ comparator : new ArrayComparator ( {
44+ compareBy : ( item ) => JSON . stringify ( item )
45+ } ) ,
46+ watch : {
47+ // This provides instant caching of the query results.
48+ // SQLite calls are asynchronous - therefore on page refresh the placeholder data will be used until the query is resolved.
49+ // This uses localStorage to synchronously display a cached version while loading.
50+ // Note that the TodoListsWidget is wraped by a GuardBySync component, which will prevent rendering until the query is resolved.
51+ // Disable GuardBySync to see the placeholder data in action.
52+ placeholderData : JSON . parse ( localStorage . getItem ( 'listscache' ) ?? '[]' ) as EnhancedListRecord [ ] ,
53+ query : new GetAllQuery < EnhancedListRecord > ( {
54+ sql : /* sql */ `
55+ SELECT
56+ ${ LISTS_TABLE } .*,
57+ COUNT(${ TODOS_TABLE } .id) AS total_tasks,
58+ SUM(
59+ CASE
60+ WHEN ${ TODOS_TABLE } .completed = true THEN 1
61+ ELSE 0
62+ END
63+ ) as completed_tasks
64+ FROM
65+ ${ LISTS_TABLE }
66+ LEFT JOIN ${ TODOS_TABLE } ON ${ LISTS_TABLE } .id = ${ TODOS_TABLE } .list_id
67+ GROUP BY
68+ ${ LISTS_TABLE } .id;
69+ `
70+ } )
71+ }
72+ } ) ;
73+
74+ // This updates a cache in order to display results instantly on page load.
75+ listsQuery . subscribe ( {
76+ onData : ( data ) => {
77+ // Store the data in localStorage for instant caching
78+ localStorage . setItem ( 'listscache' , JSON . stringify ( data ) ) ;
79+ }
80+ } ) ;
81+
82+ return {
83+ lists : listsQuery
84+ } ;
85+ } ) ;
86+
2487 React . useEffect ( ( ) => {
2588 const logger = createBaseLogger ( ) ;
2689 logger . useDefaults ( ) ; // eslint-disable-line
@@ -30,7 +93,7 @@ export const SystemProvider = ({ children }: { children: React.ReactNode }) => {
3093
3194 powerSync . init ( ) ;
3295 const l = connector . registerListener ( {
33- initialized : ( ) => { } ,
96+ initialized : ( ) => { } ,
3497 sessionStarted : ( ) => {
3598 powerSync . connect ( connector ) ;
3699 }
@@ -47,11 +110,13 @@ export const SystemProvider = ({ children }: { children: React.ReactNode }) => {
47110
48111 return (
49112 < Suspense fallback = { < CircularProgress /> } >
50- < PowerSyncContext . Provider value = { powerSync } >
51- < SupabaseContext . Provider value = { connector } >
52- < NavigationPanelContextProvider > { children } </ NavigationPanelContextProvider >
53- </ SupabaseContext . Provider >
54- </ PowerSyncContext . Provider >
113+ < QueryStore . Provider value = { queryStore } >
114+ < PowerSyncContext . Provider value = { powerSync } >
115+ < SupabaseContext . Provider value = { connector } >
116+ < NavigationPanelContextProvider > { children } </ NavigationPanelContextProvider >
117+ </ SupabaseContext . Provider >
118+ </ PowerSyncContext . Provider >
119+ </ QueryStore . Provider >
55120 </ Suspense >
56121 ) ;
57122} ;
0 commit comments