+33
src/app/page.module.css
+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
+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
))}