mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-10 16:53:10 +00:00
feat: integrate offline indicators in app components
This commit is contained in:
parent
52a28deb96
commit
5a1ddd43ad
31
src/App.tsx
31
src/App.tsx
@ -17,7 +17,8 @@ import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import { AuthProvider } from "@/contexts/AuthContext";
|
||||
import { ForumProvider } from "@/contexts/ForumContext";
|
||||
import { ForumProvider, useForum } from "@/contexts/ForumContext";
|
||||
import { OfflineIndicator } from "@/components/ui/offline-indicator";
|
||||
import CellPage from "./pages/CellPage";
|
||||
import PostPage from "./pages/PostPage";
|
||||
import NotFound from "./pages/NotFound";
|
||||
@ -26,6 +27,27 @@ import Dashboard from "./pages/Dashboard";
|
||||
// Create a client
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
// Inner component that uses the Forum context
|
||||
const AppContent = () => {
|
||||
const { isNetworkConnected, isSyncing, outboxCount } = useForum();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Routes>
|
||||
<Route path="/" element={<Dashboard />} />
|
||||
<Route path="/cell/:cellId" element={<CellPage />} />
|
||||
<Route path="/post/:postId" element={<PostPage />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
<OfflineIndicator
|
||||
isNetworkConnected={isNetworkConnected}
|
||||
isSyncing={isSyncing}
|
||||
outboxCount={outboxCount}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router>
|
||||
@ -34,12 +56,7 @@ const App = () => (
|
||||
<TooltipProvider>
|
||||
<Toaster />
|
||||
<Sonner />
|
||||
<Routes>
|
||||
<Route path="/" element={<Dashboard />} />
|
||||
<Route path="/cell/:cellId" element={<CellPage />} />
|
||||
<Route path="/post/:postId" element={<PostPage />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
<AppContent />
|
||||
</TooltipProvider>
|
||||
</ForumProvider>
|
||||
</AuthProvider>
|
||||
|
||||
@ -9,6 +9,7 @@ import { formatDistanceToNow } from 'date-fns';
|
||||
import { Comment } from '@/types';
|
||||
import { CypherImage } from './ui/CypherImage';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { PendingIndicator } from './ui/pending-indicator';
|
||||
|
||||
const PostDetail = () => {
|
||||
const { postId } = useParams<{ postId: string }>();
|
||||
@ -27,7 +28,8 @@ const PostDetail = () => {
|
||||
isRefreshing,
|
||||
refreshData,
|
||||
moderateComment,
|
||||
moderateUser
|
||||
moderateUser,
|
||||
isMessagePending
|
||||
} = useForum();
|
||||
const { currentUser, isAuthenticated, verificationStatus } = useAuth();
|
||||
const [newComment, setNewComment] = useState('');
|
||||
@ -165,6 +167,7 @@ const PostDetail = () => {
|
||||
<span className="truncate max-w-[150px]">
|
||||
{post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
|
||||
</span>
|
||||
{isMessagePending(post.id) && <PendingIndicator />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -252,9 +255,12 @@ const PostDetail = () => {
|
||||
{comment.authorAddress.slice(0, 6)}...{comment.authorAddress.slice(-4)}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(comment.timestamp, { addSuffix: true })}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(comment.timestamp, { addSuffix: true })}
|
||||
</span>
|
||||
{isMessagePending(comment.id) && <PendingIndicator />}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm break-words">{comment.content}</p>
|
||||
{isCellAdmin && !comment.moderated && (
|
||||
|
||||
@ -10,6 +10,7 @@ import { ArrowLeft, MessageSquare, MessageCircle, ArrowUp, ArrowDown, Clock, Ref
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { CypherImage } from './ui/CypherImage';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { PendingIndicator } from './ui/pending-indicator';
|
||||
|
||||
const PostList = () => {
|
||||
const { cellId } = useParams<{ cellId: string }>();
|
||||
@ -26,7 +27,8 @@ const PostList = () => {
|
||||
isVoting,
|
||||
posts,
|
||||
moderatePost,
|
||||
moderateUser
|
||||
moderateUser,
|
||||
isMessagePending
|
||||
} = useForum();
|
||||
const { isAuthenticated, currentUser, verificationStatus } = useAuth();
|
||||
const [newPostTitle, setNewPostTitle] = useState('');
|
||||
@ -259,6 +261,7 @@ const PostList = () => {
|
||||
<div className="flex items-center gap-4 text-xs text-cyber-neutral">
|
||||
<span>{formatDistanceToNow(post.timestamp, { addSuffix: true })}</span>
|
||||
<span>by {post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}</span>
|
||||
{isMessagePending(post.id) && <PendingIndicator />}
|
||||
</div>
|
||||
</Link>
|
||||
{isCellAdmin && !post.moderated && (
|
||||
|
||||
@ -32,7 +32,11 @@ interface ForumContextType {
|
||||
isRefreshing: boolean;
|
||||
// Network status
|
||||
isNetworkConnected: boolean;
|
||||
isSyncing: boolean;
|
||||
outboxCount: number;
|
||||
pendingMessageIds: string[];
|
||||
error: string | null;
|
||||
isMessagePending: (messageId: string) => boolean;
|
||||
getCellById: (id: string) => Cell | undefined;
|
||||
getPostsByCell: (cellId: string) => Post[];
|
||||
getCommentsByPost: (postId: string) => Comment[];
|
||||
@ -75,11 +79,34 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
||||
const [isVoting, setIsVoting] = useState(false);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [isNetworkConnected, setIsNetworkConnected] = useState(false);
|
||||
const [isSyncing, setIsSyncing] = useState(false);
|
||||
const [outboxCount, setOutboxCount] = useState(0);
|
||||
const [pendingMessageIds, setPendingMessageIds] = useState<string[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const { toast } = useToast();
|
||||
const { currentUser, isAuthenticated, messageSigning } = useAuth();
|
||||
|
||||
// Function to update outbox count and pending message IDs
|
||||
const updateOutboxCount = async () => {
|
||||
try {
|
||||
const count = await messageManager.getOutboxCount();
|
||||
setOutboxCount(count);
|
||||
|
||||
// Also update pending message IDs
|
||||
const { db } = await import('@/lib/storage/db');
|
||||
const pendingIds = await db.getPendingMessageIds();
|
||||
setPendingMessageIds(pendingIds);
|
||||
} catch (err) {
|
||||
console.warn("Failed to get outbox count:", err);
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to check if a message is pending
|
||||
const isMessagePending = (messageId: string) => {
|
||||
return pendingMessageIds.includes(messageId);
|
||||
};
|
||||
|
||||
// Transform message cache data to the expected types
|
||||
const updateStateFromCache = () => {
|
||||
// Use the verifyMessage function from messageSigning if available
|
||||
@ -129,13 +156,31 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
// Monitor network connection status
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = monitorNetworkHealth(setIsNetworkConnected, toast);
|
||||
const { unsubscribe } = monitorNetworkHealth(
|
||||
(isConnected) => {
|
||||
setIsNetworkConnected(isConnected);
|
||||
if (isConnected) {
|
||||
// When coming back online, sync will happen automatically
|
||||
// but we should update the outbox count
|
||||
updateOutboxCount();
|
||||
setIsSyncing(true);
|
||||
// Reset syncing state after a short delay and update outbox again
|
||||
setTimeout(async () => {
|
||||
setIsSyncing(false);
|
||||
await updateOutboxCount();
|
||||
}, 3000);
|
||||
}
|
||||
},
|
||||
toast
|
||||
);
|
||||
return unsubscribe;
|
||||
}, [toast]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
setIsInitialLoading(true);
|
||||
// Load initial outbox count
|
||||
await updateOutboxCount();
|
||||
await initializeNetwork(toast, updateStateFromCache, setError);
|
||||
setIsInitialLoading(false);
|
||||
};
|
||||
@ -175,6 +220,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
||||
messageSigning
|
||||
);
|
||||
setIsPostingPost(false);
|
||||
// Update outbox count in case the message was queued offline
|
||||
await updateOutboxCount();
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -190,6 +237,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
||||
messageSigning
|
||||
);
|
||||
setIsPostingComment(false);
|
||||
// Update outbox count in case the message was queued offline
|
||||
await updateOutboxCount();
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -309,7 +358,11 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
|
||||
isVoting,
|
||||
isRefreshing,
|
||||
isNetworkConnected,
|
||||
isSyncing,
|
||||
outboxCount,
|
||||
pendingMessageIds,
|
||||
error,
|
||||
isMessagePending,
|
||||
getCellById,
|
||||
getPostsByCell,
|
||||
getCommentsByPost,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user