import { useEffect, useState } from "react" import { Alert, KeyboardAvoidingView, Platform, Pressable, ScrollView, Text, TextInput, View, } from "react-native" import { router, useLocalSearchParams } from "expo-router" import { BlurView } from "expo-blur" import { useHeaderHeight } from "@react-navigation/elements" import { useSafeAreaInsets } from "react-native-safe-area-context" import NoteImagePanel from "@/components/note-image-panel" import UploadProgressBar from "@/components/upload-progress-bar" import { useAuthContext } from "@/hooks/use-auth-context" import { NoteImageChange, useNotes } from "@/src/notes/NotesContext" import { StagedNoteImage, validateStagedNoteImage } from "@/src/notes/image-utils" import { pickImageFromCamera, pickImageFromLibrary } from "@/src/notes/native-image-picker" import { detailScreenStyles as styles } from "@/src/styles/app-styles" import { useAppTheme } from "@/src/theme/AppThemeProvider" export default function DetailScreen() { const { id } = useLocalSearchParams<{ id?: string }>() const { claims } = useAuthContext() const { deleteNote, errorMessage, notes, updateNote } = useNotes() const note = notes.find((entry) => entry.id === id) const canEdit = note?.createdBy === claims?.sub const [title, setTitle] = useState(note?.title ?? "") const [content, setContent] = useState(note?.content ?? "") const [stagedImage, setStagedImage] = useState(null) const [imageChange, setImageChange] = useState({ type: "keep" }) const [isSaving, setIsSaving] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const [uploadProgress, setUploadProgress] = useState(null) const [localErrorMessage, setLocalErrorMessage] = useState(null) const [statusMessage, setStatusMessage] = useState(null) const insets = useSafeAreaInsets() const headerHeight = useHeaderHeight() const { colorScheme, palette } = useAppTheme() const formatTimestamp = (value: string) => { const parsed = new Date(value) if (Number.isNaN(parsed.getTime())) { return "Unknown" } return parsed.toLocaleString() } useEffect(() => { setTitle(note?.title ?? "") setContent(note?.content ?? "") setStagedImage(null) setImageChange({ type: "keep" }) }, [note?.content, note?.id, note?.title]) const attachFromCamera = async () => { try { const image = await pickImageFromCamera() if (image) { validateStagedNoteImage(image) setStagedImage(image) setImageChange({ type: "replace", image }) setLocalErrorMessage(null) setStatusMessage(null) } } catch (error) { setLocalErrorMessage(error instanceof Error ? error.message : "The camera could not be opened.") } } const attachFromGallery = async () => { try { const image = await pickImageFromLibrary() if (image) { validateStagedNoteImage(image) setStagedImage(image) setImageChange({ type: "replace", image }) setLocalErrorMessage(null) setStatusMessage(null) } } catch (error) { setLocalErrorMessage(error instanceof Error ? error.message : "The gallery could not be opened.") } } const clearImage = () => { setStagedImage(null) setStatusMessage(null) setLocalErrorMessage(null) setImageChange(note?.imageUrl ? { type: "remove" } : { type: "keep" }) } const onSave = async () => { if (!id) { setLocalErrorMessage("This note could not be found.") return } if (!title.trim() || !content.trim()) { setLocalErrorMessage("Title and content are required.") return } setIsSaving(true) setUploadProgress(null) setLocalErrorMessage(null) setStatusMessage(null) const wasSaved = await updateNote(id, title, content, imageChange, { onImageUploadProgress: (progress) => { setUploadProgress(progress.progress) }, }) setIsSaving(false) setUploadProgress(null) if (wasSaved) { setStagedImage(null) setImageChange({ type: "keep" }) setStatusMessage("Note updated.") } } const confirmDelete = () => { Alert.alert( "Delete note", "Are you sure you want to delete this note?", [ { text: "Cancel", style: "cancel", }, { text: "Delete", style: "destructive", onPress: () => { void onDelete() }, }, ] ) } const onDelete = async () => { if (!id) { setLocalErrorMessage("This note could not be found.") return } setIsDeleting(true) setLocalErrorMessage(null) setStatusMessage(null) const wasDeleted = await deleteNote(id) setIsDeleting(false) if (wasDeleted) { router.replace("/") } } if (!note) { return ( Note not found The note may have been deleted. ) } const currentImageUrl = imageChange.type === "remove" ? null : note.imageUrl const currentImageMimeType = imageChange.type === "remove" ? null : note.imageMimeType const currentImageSizeBytes = imageChange.type === "remove" ? null : note.imageSizeBytes const isUploading = uploadProgress !== null const imageActionsDisabled = isSaving || isUploading const saveDisabled = isSaving const deleteDisabled = isDeleting || isUploading const primaryButtonStyle = saveDisabled ? styles.disabledButton : styles.enabledButtonShadow const deleteButtonStyle = deleteDisabled ? styles.disabledButton : styles.enabledButtonShadow return ( Created by {note.creatorLabel} Last changed {formatTimestamp(note.lastChangedAt)} { void attachFromCamera() }} onChooseFromLibrary={() => { void attachFromGallery() }} onRemoveImage={clearImage} /> {uploadProgress !== null ? : null} {!canEdit ? ( Only the creator of this note can update or delete it. ) : null} {localErrorMessage ? {localErrorMessage} : null} {!localErrorMessage && errorMessage ? {errorMessage} : null} {statusMessage ? {statusMessage} : null} {canEdit ? ( { void onSave() }} style={[styles.primaryButton, primaryButtonStyle, { backgroundColor: palette.accent }]} > {isSaving ? "Saving..." : "Save changes"} {isDeleting ? "Deleting..." : "Delete note"} ) : null} ) }