zioinfo-mail/workspace/guardia-messenger/hooks/useOfflineCache.ts
DESKTOP-TKLFCPR\ython 777e027553 refactor(structure): move app -> workspace/guardia-messenger
Permission denied on git mv, used robocopy instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:53:57 +09:00

63 lines
1.9 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react'
import * as SecureStore from 'expo-secure-store'
import NetInfo from '@react-native-community/netinfo'
interface CacheOptions {
ttlMs?: number // 캐시 유효 시간 (ms), 기본 5분
}
export function useOfflineCache<T>(
cacheKey: string,
fetcher: () => Promise<{ data: T }>,
options: CacheOptions = {}
) {
const { ttlMs = 5 * 60 * 1000 } = options
const [data, setData] = useState<T | null>(null)
const [isOffline, setOffline] = useState(false)
const [loading, setLoading] = useState(true)
const [cachedAt, setCachedAt] = useState<Date | null>(null)
useEffect(() => {
const unsub = NetInfo.addEventListener(state => {
setOffline(!(state.isConnected ?? true))
})
return () => unsub()
}, [])
const load = useCallback(async () => {
setLoading(true)
try {
if (!isOffline) {
const result = await fetcher()
setData(result.data)
const now = new Date()
setCachedAt(now)
await SecureStore.setItemAsync(cacheKey, JSON.stringify({
data: result.data, ts: now.toISOString(),
}))
} else {
const raw = await SecureStore.getItemAsync(cacheKey)
if (raw) {
const { data: cached, ts } = JSON.parse(raw)
const age = Date.now() - new Date(ts).getTime()
setData(cached)
setCachedAt(new Date(ts))
if (age > ttlMs) {
console.warn(`[OfflineCache] ${cacheKey} 캐시 만료 (${Math.floor(age / 60000)}분 전)`)
}
}
}
} catch {
const raw = await SecureStore.getItemAsync(cacheKey)
if (raw) {
const { data: cached, ts } = JSON.parse(raw)
setData(cached); setCachedAt(new Date(ts))
}
} finally { setLoading(false) }
}, [cacheKey, fetcher, isOffline, ttlMs])
useEffect(() => { load() }, [load])
return { data, isOffline, loading, cachedAt, reload: load }
}