feat: Add Flight Recorder, Timeline, ABV Curve and Offline Bottle Caching with Draft Notes support
This commit is contained in:
83
src/hooks/useBottleData.ts
Normal file
83
src/hooks/useBottleData.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { db, type CachedBottle, type CachedTasting } from '@/lib/db';
|
||||
import { useLiveQuery } from 'dexie-react-hooks';
|
||||
|
||||
export function useBottleData(bottleId: string) {
|
||||
const supabase = createClient();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Live queries from Dexie
|
||||
const cachedBottle = useLiveQuery(() => db.cache_bottles.get(bottleId), [bottleId]);
|
||||
const cachedTastings = useLiveQuery(() =>
|
||||
db.cache_tastings.where('bottle_id').equals(bottleId).sortBy('created_at'),
|
||||
[bottleId]
|
||||
);
|
||||
|
||||
const refreshData = useCallback(async () => {
|
||||
if (!navigator.onLine) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Fetch Bottle
|
||||
const { data: bottle, error: bottleError } = await supabase
|
||||
.from('bottles')
|
||||
.select('*')
|
||||
.eq('id', bottleId)
|
||||
.single();
|
||||
|
||||
if (bottleError) throw bottleError;
|
||||
|
||||
// 2. Fetch Tastings
|
||||
const { data: tastings, error: tastingsError } = await supabase
|
||||
.from('tastings')
|
||||
.select(`
|
||||
*,
|
||||
tasting_sessions (id, name),
|
||||
tasting_tags (
|
||||
tags (id, name, category)
|
||||
)
|
||||
`)
|
||||
.eq('bottle_id', bottleId)
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (tastingsError) throw tastingsError;
|
||||
|
||||
// 3. Update Dexie Cache
|
||||
await db.cache_bottles.put({
|
||||
...bottle,
|
||||
last_updated: Date.now()
|
||||
});
|
||||
|
||||
// Clear old cache for this bottle and put new
|
||||
await db.cache_tastings.where('bottle_id').equals(bottleId).delete();
|
||||
if (tastings && tastings.length > 0) {
|
||||
await db.cache_tastings.bulkAdd(tastings as any);
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('Error refreshing bottle data:', err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [bottleId, supabase]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshData();
|
||||
}, [refreshData]);
|
||||
|
||||
return {
|
||||
bottle: cachedBottle,
|
||||
tastings: cachedTastings ? [...cachedTastings].reverse() : [], // Dexie sorted asc, we want desc
|
||||
loading: loading && !cachedBottle, // Only show loading if we have nothing at all
|
||||
error,
|
||||
refresh: refreshData,
|
||||
isOffline: !navigator.onLine
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user