The 1st decentralized social network for sharing when you're on the toilet. Post a "flush" today! Powered by the AT Protocol.

fix

dame-is c8b5a3a6 64f4a17e

Changed files
+155 -3
src
+33
src/app/page.module.css
··· 533 533 min-width: 0; 534 534 } 535 535 536 + .contentRight { 537 + display: flex; 538 + align-items: center; 539 + gap: 0.75rem; 540 + margin-left: auto; 541 + } 542 + 543 + .editButton { 544 + background: none; 545 + border: 1px solid var(--tile-border); 546 + color: var(--text-color); 547 + padding: 6px; 548 + cursor: pointer; 549 + display: flex; 550 + align-items: center; 551 + justify-content: center; 552 + transition: all 0.2s; 553 + width: 32px; 554 + height: 32px; 555 + border-radius: 4px; 556 + } 557 + 558 + .editButton svg { 559 + width: 16px; 560 + height: 16px; 561 + } 562 + 563 + .editButton:hover { 564 + border-color: var(--primary-color); 565 + color: var(--primary-color); 566 + background: rgba(91, 173, 240, 0.05); 567 + } 568 + 536 569 .userLine { 537 570 display: flex; 538 571 align-items: center;
+122 -3
src/app/page.tsx
··· 7 7 import { useAuth } from '@/lib/auth-context'; 8 8 import { containsBannedWords, sanitizeText, isAllowedEmoji } from '@/lib/content-filter'; 9 9 import { formatRelativeTime } from '@/lib/time-utils'; 10 + import EditFlushModal from '@/components/EditFlushModal'; 10 11 11 12 // Types for feed entries 12 13 interface FlushingEntry { ··· 39 40 const [loading, setLoading] = useState(true); 40 41 const [error, setError] = useState<string | null>(null); 41 42 const [newEntryIds, setNewEntryIds] = useState<Set<string>>(new Set()); 43 + const [editingFlush, setEditingFlush] = useState<FlushingEntry | null>(null); 44 + const [actionError, setActionError] = useState<string | null>(null); 45 + const [actionSuccess, setActionSuccess] = useState<string | null>(null); 42 46 43 47 useEffect(() => { 44 48 // Fetch the latest entries when the component mounts ··· 306 310 await signOut(); 307 311 }; 308 312 313 + // Check if the current user owns this flush 314 + const isOwnFlush = (authorDid: string) => { 315 + if (!session) return false; 316 + return session.sub === authorDid; 317 + }; 318 + 319 + // Handle updating a flush 320 + const handleUpdateFlush = async (text: string, emoji: string) => { 321 + if (!session || !editingFlush) { 322 + setActionError('You must be logged in to update a flush'); 323 + return; 324 + } 325 + 326 + try { 327 + setActionError(null); 328 + setActionSuccess(null); 329 + 330 + const { updateFlushRecord } = await import('@/lib/api-client'); 331 + 332 + await updateFlushRecord( 333 + session, 334 + editingFlush.uri, 335 + text, 336 + emoji, 337 + editingFlush.createdAt 338 + ); 339 + 340 + setActionSuccess('Flush updated successfully!'); 341 + 342 + // Update the local state 343 + setEntries(entries.map(entry => 344 + entry.uri === editingFlush.uri 345 + ? { ...entry, text, emoji } 346 + : entry 347 + )); 348 + 349 + // Clear success message after 3 seconds 350 + setTimeout(() => setActionSuccess(null), 3000); 351 + } catch (error: any) { 352 + console.error('Error updating flush:', error); 353 + setActionError(error.message || 'Failed to update flush'); 354 + } 355 + }; 356 + 357 + // Handle deleting a flush 358 + const handleDeleteFlush = async () => { 359 + if (!session || !editingFlush) { 360 + setActionError('You must be logged in to delete a flush'); 361 + return; 362 + } 363 + 364 + try { 365 + setActionError(null); 366 + setActionSuccess(null); 367 + 368 + const { deleteFlushRecord } = await import('@/lib/api-client'); 369 + 370 + await deleteFlushRecord(session, editingFlush.uri); 371 + 372 + setActionSuccess('Flush deleted successfully!'); 373 + 374 + // Remove from local state 375 + setEntries(entries.filter(entry => entry.uri !== editingFlush.uri)); 376 + 377 + // Clear success message after 3 seconds 378 + setTimeout(() => setActionSuccess(null), 3000); 379 + } catch (error: any) { 380 + console.error('Error deleting flush:', error); 381 + setActionError(error.message || 'Failed to delete flush'); 382 + } 383 + }; 384 + 309 385 // List of emojis for status selection 310 386 const EMOJIS = [ 311 387 '🚽', '🧻', '💩', '💨', '🚾', '🧼', '🪠', '🚻', '🩸', '💧', '💦', '😌', ··· 315 391 316 392 return ( 317 393 <div className={styles.container}> 394 + 395 + {/* Action messages */} 396 + {actionError && ( 397 + <div className={styles.error}> 398 + {actionError} 399 + </div> 400 + )} 401 + 402 + {actionSuccess && ( 403 + <div className={styles.success}> 404 + {actionSuccess} 405 + </div> 406 + )} 407 + 408 + {/* Edit Modal */} 409 + <EditFlushModal 410 + isOpen={editingFlush !== null} 411 + flushData={editingFlush ? { 412 + uri: editingFlush.uri, 413 + text: editingFlush.text, 414 + emoji: editingFlush.emoji, 415 + created_at: editingFlush.createdAt 416 + } : null} 417 + onSave={handleUpdateFlush} 418 + onDelete={handleDeleteFlush} 419 + onClose={() => setEditingFlush(null)} 420 + /> 421 + 318 422 <header className={styles.header}> 319 423 <div className={styles.headerContent}> 320 424 {/* New header layout with 4 center-aligned lines */} ··· 546 650 )} 547 651 </span> 548 652 </div> 549 - <span className={styles.timestamp}> 550 - {formatRelativeTime(entry.createdAt)} 551 - </span> 653 + <div className={styles.contentRight}> 654 + <span className={styles.timestamp}> 655 + {formatRelativeTime(entry.createdAt)} 656 + </span> 657 + {isOwnFlush(entry.authorDid) && isAuthenticated && ( 658 + <button 659 + className={styles.editButton} 660 + onClick={() => setEditingFlush(entry)} 661 + aria-label="Edit flush" 662 + title="Edit or delete this flush" 663 + > 664 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> 665 + <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path> 666 + <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path> 667 + </svg> 668 + </button> 669 + )} 670 + </div> 552 671 </div> 553 672 </div> 554 673 ))}