init
This commit is contained in:
82
src/hooks/use-feed-list.ts
Normal file
82
src/hooks/use-feed-list.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { FeedItem } from '@/types/feed';
|
||||
import { getAllFeedItems } from '@/services/database';
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
export function useFeedList() {
|
||||
const [localFeedItems, setLocalFeedItems] = useState<FeedItem[]>([]);
|
||||
const [page, setPage] = useState(0);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const loadMore = useCallback(async () => {
|
||||
if (loadingMore || !hasMore || isRefreshing) return;
|
||||
|
||||
setLoadingMore(true);
|
||||
|
||||
try {
|
||||
const offset = page * PAGE_SIZE;
|
||||
const items = await getAllFeedItems(PAGE_SIZE + 1, offset);
|
||||
|
||||
if (items.length > PAGE_SIZE) {
|
||||
const newItems = items.slice(0, PAGE_SIZE);
|
||||
setLocalFeedItems(prev => [...prev, ...newItems]);
|
||||
setHasMore(true);
|
||||
} else {
|
||||
setLocalFeedItems(prev => [...prev, ...items]);
|
||||
setHasMore(false);
|
||||
}
|
||||
|
||||
setPage(prev => prev + 1);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
console.error('Failed to load more items:', err);
|
||||
setError('Failed to load more items. Please try again.');
|
||||
} finally {
|
||||
setLoadingMore(false);
|
||||
}
|
||||
}, [loadingMore, hasMore, isRefreshing, page]);
|
||||
|
||||
const refreshFeed = useCallback(async () => {
|
||||
if (isRefreshing) return;
|
||||
|
||||
setIsRefreshing(true);
|
||||
setPage(0);
|
||||
setHasMore(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const items = await getAllFeedItems(PAGE_SIZE + 1);
|
||||
|
||||
if (items.length > PAGE_SIZE) {
|
||||
setLocalFeedItems(items.slice(0, PAGE_SIZE));
|
||||
setHasMore(true);
|
||||
} else {
|
||||
setLocalFeedItems(items);
|
||||
setHasMore(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to refresh feed:', err);
|
||||
setError('Failed to refresh feed. Please try again.');
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
}, [isRefreshing]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshFeed();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
feedItems: localFeedItems,
|
||||
loading: loadingMore,
|
||||
hasMore,
|
||||
loadMore,
|
||||
refreshFeed,
|
||||
isRefreshing,
|
||||
error,
|
||||
};
|
||||
}
|
||||
99
src/hooks/use-offline.ts
Normal file
99
src/hooks/use-offline.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// Offline/Online Network Hook
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { hasLocalData, getAllLocalFeedItems, getLocalFeedItems, syncAllFeeds } from '@/services/sync-service';
|
||||
import { FeedItem } from '@/types/feed';
|
||||
|
||||
type NetworkStatus = 'online' | 'offline' | 'unknown';
|
||||
|
||||
interface UseOfflineReturn {
|
||||
isOnline: boolean;
|
||||
networkStatus: NetworkStatus;
|
||||
hasOfflineData: boolean;
|
||||
isSyncing: boolean;
|
||||
lastSyncTime: Date | null;
|
||||
syncNow: () => Promise<void>;
|
||||
getOfflineFeedItems: (subscriptionId?: string, limit?: number, offset?: number) => Promise<FeedItem[]>;
|
||||
}
|
||||
|
||||
export function useOffline(): UseOfflineReturn {
|
||||
const [networkStatus, setNetworkStatus] = useState<NetworkStatus>('unknown');
|
||||
const [hasOfflineData, setHasOfflineData] = useState(false);
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const [lastSyncTime, setLastSyncTime] = useState<Date | null>(null);
|
||||
|
||||
// Check network status (simplified - in production would use @react-native-community/netinfo)
|
||||
const checkNetworkStatus = useCallback(async () => {
|
||||
try {
|
||||
// Try to fetch from a known endpoint to test connectivity
|
||||
const response = await fetch('https://www.google.com/favicon.ico', {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
});
|
||||
setNetworkStatus('online');
|
||||
return true;
|
||||
} catch {
|
||||
setNetworkStatus('offline');
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Check if we have offline data
|
||||
const checkOfflineData = useCallback(async () => {
|
||||
const hasData = await hasLocalData();
|
||||
setHasOfflineData(hasData);
|
||||
return hasData;
|
||||
}, []);
|
||||
|
||||
// Sync now
|
||||
const syncNow = useCallback(async () => {
|
||||
if (networkStatus === 'offline') {
|
||||
console.log('[Offline] Cannot sync while offline');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSyncing(true);
|
||||
try {
|
||||
await syncAllFeeds();
|
||||
setLastSyncTime(new Date());
|
||||
await checkOfflineData();
|
||||
} finally {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
}, [networkStatus, checkOfflineData]);
|
||||
|
||||
// Get offline feed items
|
||||
const getOfflineFeedItems = useCallback(async (
|
||||
subscriptionId?: string,
|
||||
limit: number = 50,
|
||||
offset: number = 0
|
||||
): Promise<FeedItem[]> => {
|
||||
if (subscriptionId) {
|
||||
return getLocalFeedItems(subscriptionId, limit, offset);
|
||||
}
|
||||
return getAllLocalFeedItems(limit);
|
||||
}, []);
|
||||
|
||||
// Initial checks
|
||||
useEffect(() => {
|
||||
checkNetworkStatus();
|
||||
checkOfflineData();
|
||||
|
||||
// Periodic network check
|
||||
const interval = setInterval(() => {
|
||||
checkNetworkStatus();
|
||||
}, 30000); // Every 30 seconds
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [checkNetworkStatus, checkOfflineData]);
|
||||
|
||||
return {
|
||||
isOnline: networkStatus === 'online',
|
||||
networkStatus,
|
||||
hasOfflineData,
|
||||
isSyncing,
|
||||
lastSyncTime,
|
||||
syncNow,
|
||||
getOfflineFeedItems,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user