this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: misc updates

- mobile sync component
- greater bottom scroll length for mobile view
- use hamburger icon for sidebar trigger
- multiple rooms in sidebar

+144 -56
+3 -27
mast-react-vite/src/App.tsx
··· 370 370 <section className="flex-1 container py-12 h-[calc(100vh-theme(spacing.4))] overflow-hidden relative"> 371 371 <SidebarTrigger className="absolute top-4 left-8 border border-foreground" /> 372 372 <div className="absolute top-4 right-12 flex gap-2"> 373 - <button 374 - onClick={() => { 375 - console.log("Manual sync requested"); 376 - syncWorker.postMessage({ 377 - type: 'SYNC_CHANGES', 378 - dbname 379 - }); 380 - }} 381 - className="px-3 py-1 bg-blue-600 text-white rounded-md text-xs hover:bg-blue-700" 382 - > 383 - Sync Now 384 - </button> 385 373 {selectedItems.size > 0 && ( 386 374 <Badge variant="default"> {selectedItems.size} selected </Badge> 387 375 )} ··· 421 409 filterContext={filterContext} 422 410 setFilterContext={setFilterContext} /> 423 411 424 - <section className="flex-1 py-12 h-[calc(100vh-theme(spacing.4))] w-full overflow-hidden relative"> 425 - <div className="flex-1 bg-muted border-b border-foreground h-14 absolute inset-x-0 top-0 " /> 426 - <SidebarTrigger className="fixed top-4 border border-foreground left-8" /> 412 + <div className="bg-muted h-14 w-full absolute top-0 " /> 413 + <section className="flex-1 container py-12 h-[calc(100vh-theme(spacing.4))] overflow-hidden relative"> 414 + <SidebarTrigger className="absolute top-4 left-8 border border-foreground" /> 427 415 <div className="absolute top-4 right-12 flex gap-2"> 428 - <button 429 - onClick={() => { 430 - console.log("Manual sync requested (mobile)"); 431 - syncWorker.postMessage({ 432 - type: 'SYNC_CHANGES', 433 - dbname 434 - }); 435 - }} 436 - className="px-3 py-1 bg-blue-600 text-white rounded-md text-xs hover:bg-blue-700" 437 - > 438 - Sync 439 - </button> 440 416 {selectedItems.size > 0 && ( 441 417 <Badge variant="default"> {selectedItems.size} selected </Badge> 442 418 )}
+72
mast-react-vite/src/components/ui/app-sidebar.tsx
··· 9 9 SidebarMenuItem, 10 10 SidebarMenuButton, 11 11 } from "@/components/ui/sidebar"; 12 + import { useState, useEffect } from "react"; 12 13 import { useQuery } from "@vlcn.io/react"; 13 14 15 + // Helper function to load all visited rooms 16 + function loadAllRooms(): Set<string> { 17 + const allRoomsJSON = localStorage.getItem('allRooms'); 18 + if (allRoomsJSON) { 19 + try { 20 + return new Set(JSON.parse(allRoomsJSON)); 21 + } catch (error) { 22 + console.error('Error parsing allRooms from localStorage:', error); 23 + } 24 + } 25 + return new Set<string>(); 26 + } 27 + 28 + // Helper function to update URL with room ID 29 + function updateUrlWithRoom(roomId: string) { 30 + const hash = window.location.hash.substring(1); 31 + const params = new URLSearchParams(hash); 32 + params.set('room', roomId); 33 + window.location.hash = params.toString(); 34 + } 35 + 14 36 export function AppSidebar({ctx, filterContext, setFilterContext}) { 37 + // Load the visited rooms 38 + const [visitedRooms, setVisitedRooms] = useState<string[]>([]); 39 + const [currentRoom, setCurrentRoom] = useState<string>(""); 40 + 41 + useEffect(() => { 42 + // Load rooms from localStorage 43 + const rooms = Array.from(loadAllRooms()); 44 + setVisitedRooms(rooms); 45 + 46 + // Get current room from URL or localStorage 47 + const hash = window.location.hash.substring(1); 48 + const params = new URLSearchParams(hash); 49 + const roomFromHash = params.get('room'); 50 + const storedRoom = localStorage.getItem("room"); 51 + 52 + setCurrentRoom(roomFromHash || storedRoom || ""); 53 + }, []); 54 + 55 + // Handle switching to another room 56 + const handleRoomSwitch = (roomId: string) => { 57 + if (roomId === currentRoom) return; 58 + 59 + // Save current room to localStorage 60 + localStorage.setItem("room", roomId); 61 + 62 + // Update URL with the new room ID 63 + updateUrlWithRoom(roomId); 64 + 65 + // Reload the page to apply the new room 66 + window.location.reload(); 67 + }; 15 68 const projects = useQuery( 16 69 ctx, 17 70 `SELECT DISTINCT project FROM active_todos WHERE project != '' ORDER BY project` ··· 55 108 <Sidebar> 56 109 <SidebarHeader /> 57 110 <SidebarContent> 111 + {visitedRooms.length > 0 && ( 112 + <SidebarGroup> 113 + <SidebarGroupLabel>Rooms</SidebarGroupLabel> 114 + <SidebarMenu> 115 + <div className="flex flex-wrap gap-1.5 p-2 max-h-40 overflow-y-auto"> 116 + {visitedRooms.map((roomId) => ( 117 + <SidebarMenuItem key={roomId}> 118 + <SidebarMenuButton 119 + isActive={currentRoom === roomId} 120 + onClick={() => handleRoomSwitch(roomId)} 121 + > 122 + {roomId.substring(0, 8)}{currentRoom === roomId ? " (current)" : ""} 123 + </SidebarMenuButton> 124 + </SidebarMenuItem> 125 + ))} 126 + </div> 127 + </SidebarMenu> 128 + </SidebarGroup> 129 + )} 58 130 <SidebarGroup> 59 131 <SidebarGroupLabel>Projects</SidebarGroupLabel> 60 132 <SidebarMenu>
+1 -1
mast-react-vite/src/components/ui/data-table.tsx
··· 39 39 No results. 40 40 </div> 41 41 )} 42 - <div className="h-[10rem]" /> 42 + <div className="h-[15rem]" /> 43 43 </ScrollArea> 44 44 </div> 45 45 </>
+3 -3
mast-react-vite/src/components/ui/sidebar.tsx
··· 20 20 TooltipProvider, 21 21 TooltipTrigger, 22 22 } from "@/components/ui/tooltip"; 23 - import { ViewVerticalIcon } from "@radix-ui/react-icons"; 23 + import { HamburgerMenuIcon } from "@radix-ui/react-icons"; 24 24 25 25 const SIDEBAR_COOKIE_NAME = "sidebar_state"; 26 26 const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; ··· 209 209 <SheetContent 210 210 data-sidebar="sidebar" 211 211 data-mobile="true" 212 - className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" 212 + className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden dark" 213 213 style={ 214 214 { 215 215 "--sidebar-width": SIDEBAR_WIDTH_MOBILE, ··· 293 293 }} 294 294 {...props} 295 295 > 296 - <ViewVerticalIcon /> 296 + <HamburgerMenuIcon /> 297 297 <span className="sr-only">Toggle Sidebar</span> 298 298 </Button> 299 299 );
+27 -19
mast-react-vite/src/components/ui/sync-status.tsx
··· 31 31 : 'Never'; 32 32 33 33 return ( 34 - <div className="fixed bottom-4 right-4 p-3 bg-background border border-border rounded-lg shadow-lg z-50 text-sm"> 35 - <div className="flex items-center gap-2 mb-1"> 34 + <> 35 + {/* Mobile view - just a colored dot in the corner */} 36 + <div className="md:hidden fixed top-4 right-4 p-1 z-50" title={`Sync: ${status}`}> 36 37 <div className={`w-3 h-3 rounded-full ${statusColors[status]}`}></div> 37 - <span className="font-medium">Sync: {status}</span> 38 38 </div> 39 - 40 - <div className="grid grid-cols-2 gap-x-4 gap-y-1 mt-2 text-xs text-muted-foreground"> 41 - <div>Changes sent:</div> 42 - <div>{changesSent}</div> 43 - 44 - <div>Changes received:</div> 45 - <div>{changesReceived}</div> 46 - 47 - <div>Last sync:</div> 48 - <div>{formattedTime}</div> 39 + 40 + {/* Desktop view - full status panel */} 41 + <div className="hidden md:block fixed bottom-4 right-4 p-3 bg-background border border-border rounded-lg shadow-lg z-50 text-sm"> 42 + <div className="flex items-center gap-2 mb-1"> 43 + <div className={`w-3 h-3 rounded-full ${statusColors[status]}`}></div> 44 + <span className="font-medium">Sync: {status}</span> 45 + </div> 49 46 50 - {lastError && ( 51 - <> 52 - <div className="col-span-2 text-red-500 mt-1">Error: {lastError}</div> 53 - </> 54 - )} 47 + <div className="grid grid-cols-2 gap-x-4 gap-y-1 mt-2 text-xs text-muted-foreground"> 48 + <div>Changes sent:</div> 49 + <div>{changesSent}</div> 50 + 51 + <div>Changes received:</div> 52 + <div>{changesReceived}</div> 53 + 54 + <div>Last sync:</div> 55 + <div>{formattedTime}</div> 56 + 57 + {lastError && ( 58 + <> 59 + <div className="col-span-2 text-red-500 mt-1">Error: {lastError}</div> 60 + </> 61 + )} 62 + </div> 55 63 </div> 56 - </div> 64 + </> 57 65 ); 58 66 } 59 67
+38 -6
mast-react-vite/src/main.tsx
··· 18 18 const ctx = dbRegistry.get(dbname); 19 19 if (ctx) { 20 20 console.log(`Notifying listeners for database: ${dbname}`); 21 - 22 - // Get all tables in the database 23 - // For simplicity, we'll just use the todos table since we know it exists 24 21 const tables = ["todos"]; 25 22 26 - // Create synthetic changes for each table 27 - // This tells the system that each table has been updated 28 23 const changes: [number, string, bigint][] = tables.map(table => [ 29 24 UPDATE_TYPE.UPDATE, // We use UPDATE as a general change type 30 25 table, // Table name ··· 60 55 // Store active crypto key pairs (in memory for the session) 61 56 const activeKeyPairs: Record<string, CryptoKeyPair> = {}; 62 57 63 - // Function to generate a WebCrypto key pair 64 58 async function generateKeyPair() { 65 59 try { 66 60 const keyPair = await window.crypto.subtle.generateKey( ··· 95 89 } 96 90 } 97 91 92 + // Helper function to load all visited rooms 93 + function loadAllRooms(): Set<string> { 94 + const allRoomsJSON = localStorage.getItem('allRooms'); 95 + if (allRoomsJSON) { 96 + try { 97 + return new Set(JSON.parse(allRoomsJSON)); 98 + } catch (error) { 99 + console.error('Error parsing allRooms from localStorage:', error); 100 + } 101 + } 102 + return new Set<string>(); 103 + } 104 + 105 + // Helper function to save all visited rooms 106 + function saveAllRooms(rooms: Set<string>): void { 107 + localStorage.setItem('allRooms', JSON.stringify(Array.from(rooms))); 108 + } 109 + 98 110 // Function to get room ID from URL hash or generate a new one 99 111 async function getRoomId(): Promise<{ roomId: string, keyPair?: CryptoKeyPair, publicKeyBase64?: string }> { 100 112 // Check URL hash for room parameter ··· 103 115 const roomFromHash = params.get('room'); 104 116 105 117 if (roomFromHash) { 118 + // Room exists in URL, add it to the list of all visited rooms 119 + const allRooms = loadAllRooms(); 120 + allRooms.add(roomFromHash); 121 + saveAllRooms(allRooms); 122 + console.log(`Added ${roomFromHash} to visited rooms list`); 123 + 106 124 // Room exists in URL, check if we have keys for it in localStorage 107 125 const storedKeys = localStorage.getItem(`keys-${roomFromHash}`); 108 126 if (storedKeys) { ··· 147 165 // Check localStorage for room 148 166 const storedRoom = localStorage.getItem("room"); 149 167 if (storedRoom) { 168 + // Update URL with the room ID from localStorage 169 + updateUrlWithRoom(storedRoom); 170 + 171 + // Add to the list of all visited rooms 172 + const allRooms = loadAllRooms(); 173 + allRooms.add(storedRoom); 174 + saveAllRooms(allRooms); 175 + console.log(`Added ${storedRoom} to visited rooms list`); 150 176 // Room exists in localStorage, check if we have keys for it 151 177 const storedKeys = localStorage.getItem(`keys-${storedRoom}`); 152 178 if (storedKeys) { ··· 190 216 // Generate a new room ID and key pair 191 217 const newRoomId = crypto.randomUUID().replaceAll("-", ""); 192 218 localStorage.setItem("room", newRoomId); 219 + 220 + // Add to the list of all visited rooms 221 + const allRooms = loadAllRooms(); 222 + allRooms.add(newRoomId); 223 + saveAllRooms(allRooms); 224 + console.log(`Added ${newRoomId} to visited rooms list`); 193 225 194 226 // Generate a new key pair for this room 195 227 const { keyPair, publicKeyBase64 } = await generateKeyPair();