@@ -6,19 +6,30 @@ import {
66} from "@gorhom/bottom-sheet"
77import type { Note } from "@memoneo/shared"
88import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query"
9- import { useAtom } from "jotai"
9+ import { useAtom , useAtomValue } from "jotai"
1010import type React from "react"
1111import { useCallback , useRef , useState } from "react"
1212import { Alert , Dimensions , StyleSheet } from "react-native"
1313import { Drawer } from "react-native-drawer-layout"
1414
15+ import { authAtom , tokenAtom } from "@/lib/auth/state"
1516import { loadNoteCache } from "@/lib/notes/cache"
1617import { deleteLocalNote } from "@/lib/notes/local"
17- import { NOTES_CACHE_QUERY_KEY , NOTES_LOCAL_QUERY_KEY } from "@/lib/notes/query"
18+ import {
19+ NOTES_CACHE_QUERY_KEY ,
20+ NOTES_FOLDERS_QUERY_KEY ,
21+ NOTES_LOCAL_QUERY_KEY ,
22+ } from "@/lib/notes/query"
1823import {
1924 selectedNoteIdAtom ,
2025 useNotesState ,
2126} from "@/lib/notes/state"
27+ import {
28+ syncLocalNote ,
29+ uploadLocalNote ,
30+ type SingleNoteOverwriteWarning ,
31+ type SingleNoteSyncAction ,
32+ } from "@/lib/notes/sync"
2233
2334import { AppDrawerContext } from "./appDrawerContext"
2435import { DrawerContent } from "./DrawerContent"
@@ -31,6 +42,8 @@ const DRAWER_WIDTH = Math.min(340, WINDOW_WIDTH * 0.86)
3142
3243export function AppDrawer ( { children } : { children : React . ReactNode } ) {
3344 const queryClient = useQueryClient ( )
45+ const auth = useAtomValue ( authAtom )
46+ const token = useAtomValue ( tokenAtom )
3447 const [ drawerOpen , setDrawerOpen ] = useState ( false )
3548 const [ optionsNote , setOptionsNote ] = useState < Note | null > ( null )
3649 const [ selectedNoteId , setSelectedNoteId ] = useAtom ( selectedNoteIdAtom )
@@ -85,6 +98,57 @@ export function AppDrawer({ children }: { children: React.ReactNode }) {
8598 } ,
8699 } )
87100
101+ const singleNoteSyncMutation = useMutation ( {
102+ mutationFn : async ( {
103+ action,
104+ note,
105+ } : {
106+ action : SingleNoteSyncAction
107+ note : Note
108+ } ) => {
109+ if ( ! auth . isAuthenticated || ! token ) {
110+ throw new Error ( "Sign in from Settings before syncing notes." )
111+ }
112+
113+ const syncAuth = { token, userId : auth . user . id }
114+ const options = { confirmOverwrite : confirmSingleNoteOverwrite }
115+
116+ if ( action === "upload" ) {
117+ return uploadLocalNote ( syncAuth , note , options )
118+ }
119+
120+ return syncLocalNote ( syncAuth , note , action , options )
121+ } ,
122+ onSuccess : async ( ) => {
123+ await Promise . all ( [
124+ queryClient . invalidateQueries ( { queryKey : NOTES_LOCAL_QUERY_KEY } ) ,
125+ queryClient . invalidateQueries ( { queryKey : NOTES_FOLDERS_QUERY_KEY } ) ,
126+ queryClient . invalidateQueries ( { queryKey : NOTES_CACHE_QUERY_KEY } ) ,
127+ ] )
128+ await Promise . all ( [
129+ queryClient . refetchQueries ( {
130+ queryKey : NOTES_LOCAL_QUERY_KEY ,
131+ type : "active" ,
132+ } ) ,
133+ queryClient . refetchQueries ( {
134+ queryKey : NOTES_FOLDERS_QUERY_KEY ,
135+ type : "active" ,
136+ } ) ,
137+ queryClient . refetchQueries ( {
138+ queryKey : NOTES_CACHE_QUERY_KEY ,
139+ type : "active" ,
140+ } ) ,
141+ ] )
142+ closeNoteOptions ( )
143+ } ,
144+ onError : error => {
145+ Alert . alert (
146+ "Sync failed" ,
147+ error instanceof Error ? error . message : String ( error )
148+ )
149+ } ,
150+ } )
151+
88152 const confirmDeleteNote = useCallback (
89153 ( note : Note ) => {
90154 Alert . alert (
@@ -103,6 +167,17 @@ export function AppDrawer({ children }: { children: React.ReactNode }) {
103167 [ deleteNoteMutation ]
104168 )
105169
170+ const syncSingleNote = useCallback (
171+ ( note : Note , action : SingleNoteSyncAction ) => {
172+ if ( singleNoteSyncMutation . isPending ) {
173+ return
174+ }
175+
176+ singleNoteSyncMutation . mutate ( { note, action } )
177+ } ,
178+ [ singleNoteSyncMutation ]
179+ )
180+
106181 const renderBackdrop = useCallback (
107182 ( props : BottomSheetBackdropProps ) => (
108183 < BottomSheetBackdrop
@@ -146,14 +221,16 @@ export function AppDrawer({ children }: { children: React.ReactNode }) {
146221 backgroundStyle = { styles . sheetBackground }
147222 handleIndicatorStyle = { styles . sheetHandle }
148223 ref = { noteOptionsSheetRef }
149- snapPoints = { [ "46 %" ] } >
224+ snapPoints = { [ "64 %" ] } >
150225 < BottomSheetView style = { styles . flex } >
151226 { optionsNote && (
152227 < NoteOptionsSheet
153228 isDeleting = { deleteNoteMutation . isPending }
229+ isSyncing = { singleNoteSyncMutation . isPending }
154230 lastSync = { lastSync }
155231 note = { optionsNote }
156232 onDelete = { confirmDeleteNote }
233+ onSync = { syncSingleNote }
157234 />
158235 ) }
159236 </ BottomSheetView >
@@ -162,6 +239,23 @@ export function AppDrawer({ children }: { children: React.ReactNode }) {
162239 )
163240}
164241
242+ function confirmSingleNoteOverwrite ( warning : SingleNoteOverwriteWarning ) {
243+ return new Promise < boolean > ( resolve => {
244+ Alert . alert ( warning . title , warning . message , [
245+ {
246+ text : "Cancel" ,
247+ style : "cancel" ,
248+ onPress : ( ) => resolve ( false ) ,
249+ } ,
250+ {
251+ text : warning . confirmText ,
252+ style : "destructive" ,
253+ onPress : ( ) => resolve ( true ) ,
254+ } ,
255+ ] )
256+ } )
257+ }
258+
165259const styles = StyleSheet . create ( {
166260 drawer : {
167261 backgroundColor : "hsl(240 10% 3.9%)" ,
0 commit comments