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 64f4a17e 995f9fb3

+174 -4
+52
src/app/feed/feed.module.css
··· 149 149 margin-bottom: 0.75rem; 150 150 } 151 151 152 + .headerRight { 153 + display: flex; 154 + align-items: center; 155 + gap: 0.75rem; 156 + } 157 + 158 + .editButton { 159 + background: none; 160 + border: 1px solid var(--tile-border); 161 + color: var(--text-color); 162 + padding: 6px; 163 + cursor: pointer; 164 + display: flex; 165 + align-items: center; 166 + justify-content: center; 167 + transition: all 0.2s; 168 + width: 32px; 169 + height: 32px; 170 + border-radius: 4px; 171 + } 172 + 173 + .editButton svg { 174 + width: 16px; 175 + height: 16px; 176 + } 177 + 178 + .editButton:hover { 179 + border-color: var(--primary-color); 180 + color: var(--primary-color); 181 + background: rgba(91, 173, 240, 0.05); 182 + } 183 + 184 + .actionError { 185 + background: var(--error-background); 186 + border: 1px solid var(--error-color); 187 + color: var(--error-color); 188 + padding: 1rem; 189 + margin-bottom: 1rem; 190 + border-radius: 4px; 191 + font-size: 0.9rem; 192 + } 193 + 194 + .actionSuccess { 195 + background: var(--success-background); 196 + border: 1px solid var(--success-color); 197 + color: var(--success-text); 198 + padding: 1rem; 199 + margin-bottom: 1rem; 200 + border-radius: 4px; 201 + font-size: 0.9rem; 202 + } 203 + 152 204 .authorLink { 153 205 color: var(--primary-color); 154 206 font-weight: 600;
+122 -4
src/app/feed/page.tsx
··· 5 5 import styles from './feed.module.css'; 6 6 import { formatRelativeTime } from '@/lib/time-utils'; 7 7 import { useAuth } from '@/lib/auth-context'; 8 + import EditFlushModal from '@/components/EditFlushModal'; 8 9 9 10 // Types for our feed entries 10 11 interface FlushingEntry { ··· 22 23 const [entries, setEntries] = useState<FlushingEntry[]>([]); 23 24 const [loading, setLoading] = useState(true); 24 25 const [error, setError] = useState<string | null>(null); 25 - const { isAuthenticated, handle } = useAuth(); 26 + const { isAuthenticated, session } = useAuth(); 27 + const [editingFlush, setEditingFlush] = useState<FlushingEntry | null>(null); 28 + const [actionError, setActionError] = useState<string | null>(null); 29 + const [actionSuccess, setActionSuccess] = useState<string | null>(null); 26 30 27 31 useEffect(() => { 28 32 // Fetch the latest entries when the component mounts ··· 133 137 } 134 138 }; 135 139 140 + // Check if the current user owns this flush 141 + const isOwnFlush = (authorDid: string) => { 142 + if (!session) return false; 143 + return session.sub === authorDid; 144 + }; 145 + 146 + // Handle updating a flush 147 + const handleUpdateFlush = async (text: string, emoji: string) => { 148 + if (!session || !editingFlush) { 149 + setActionError('You must be logged in to update a flush'); 150 + return; 151 + } 152 + 153 + try { 154 + setActionError(null); 155 + setActionSuccess(null); 156 + 157 + const { updateFlushRecord } = await import('@/lib/api-client'); 158 + 159 + await updateFlushRecord( 160 + session, 161 + editingFlush.uri, 162 + text, 163 + emoji, 164 + editingFlush.createdAt 165 + ); 166 + 167 + setActionSuccess('Flush updated successfully!'); 168 + 169 + // Update the local state 170 + setEntries(entries.map(entry => 171 + entry.uri === editingFlush.uri 172 + ? { ...entry, text, emoji } 173 + : entry 174 + )); 175 + 176 + // Clear success message after 3 seconds 177 + setTimeout(() => setActionSuccess(null), 3000); 178 + } catch (error: any) { 179 + console.error('Error updating flush:', error); 180 + setActionError(error.message || 'Failed to update flush'); 181 + } 182 + }; 183 + 184 + // Handle deleting a flush 185 + const handleDeleteFlush = async () => { 186 + if (!session || !editingFlush) { 187 + setActionError('You must be logged in to delete a flush'); 188 + return; 189 + } 190 + 191 + try { 192 + setActionError(null); 193 + setActionSuccess(null); 194 + 195 + const { deleteFlushRecord } = await import('@/lib/api-client'); 196 + 197 + await deleteFlushRecord(session, editingFlush.uri); 198 + 199 + setActionSuccess('Flush deleted successfully!'); 200 + 201 + // Remove from local state 202 + setEntries(entries.filter(entry => entry.uri !== editingFlush.uri)); 203 + 204 + // Clear success message after 3 seconds 205 + setTimeout(() => setActionSuccess(null), 3000); 206 + } catch (error: any) { 207 + console.error('Error deleting flush:', error); 208 + setActionError(error.message || 'Failed to delete flush'); 209 + } 210 + }; 211 + 136 212 // No longer needed - using formatRelativeTime from time-utils 137 213 138 214 return ( 139 215 <div className={styles.container}> 140 216 217 + {/* Action messages */} 218 + {actionError && ( 219 + <div className={styles.actionError}> 220 + {actionError} 221 + </div> 222 + )} 223 + 224 + {actionSuccess && ( 225 + <div className={styles.actionSuccess}> 226 + {actionSuccess} 227 + </div> 228 + )} 229 + 230 + {/* Edit Modal */} 231 + <EditFlushModal 232 + isOpen={editingFlush !== null} 233 + flushData={editingFlush ? { 234 + uri: editingFlush.uri, 235 + text: editingFlush.text, 236 + emoji: editingFlush.emoji, 237 + created_at: editingFlush.createdAt 238 + } : null} 239 + onSave={handleUpdateFlush} 240 + onDelete={handleDeleteFlush} 241 + onClose={() => setEditingFlush(null)} 242 + /> 243 + 141 244 <header className={styles.header}> 142 245 <h1>Flushing Feed</h1> 143 246 <p className={styles.subtitle}> ··· 185 288 > 186 289 @{entry.authorHandle} 187 290 </a> 188 - <span className={styles.timestamp}> 189 - {formatRelativeTime(entry.createdAt)} 190 - </span> 291 + <div className={styles.headerRight}> 292 + <span className={styles.timestamp}> 293 + {formatRelativeTime(entry.createdAt)} 294 + </span> 295 + {isOwnFlush(entry.authorDid) && isAuthenticated && ( 296 + <button 297 + className={styles.editButton} 298 + onClick={() => setEditingFlush(entry)} 299 + aria-label="Edit flush" 300 + title="Edit or delete this flush" 301 + > 302 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> 303 + <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path> 304 + <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path> 305 + </svg> 306 + </button> 307 + )} 308 + </div> 191 309 </div> 192 310 <div className={styles.content}> 193 311 <span className={styles.emoji}>{entry.emoji}</span>