Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 183 additions & 77 deletions frontend/app/capture/details.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, KeyboardAvoidingView, Platform, Pressable } from 'react-native';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n '\bPlatform\b' frontend/app/capture/details.tsx

Repository: fortune710/keepsafe

Length of output: 192


Remove unused Platform import.

Platform is imported on line 2 but never used in the file.

🤖 Prompt for AI Agents
In @frontend/app/capture/details.tsx at line 2, Remove the unused import symbol
Platform from the import list in the top-level import statement (the line that
imports View, Text, TouchableOpacity, StyleSheet, ScrollView,
KeyboardAvoidingView, Platform, Pressable); update that import to exclude
Platform so there are no unused imports remaining.

import { router, useLocalSearchParams } from 'expo-router';
import { X, Sticker } from 'lucide-react-native';
import { X, Sticker, UserPlus, UserPlus2 } from 'lucide-react-native';
import { useEntryOperations } from '@/hooks/use-entry-operations';
import { useDeviceLocation } from '@/hooks/use-device-location';
import { useAuthContext } from '@/providers/auth-provider';
Expand All @@ -27,6 +27,8 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { Colors } from '@/lib/constants';
import AudioEntry from '@/components/audio/audio-entry';
import EntryShareList from '@/components/friends/entry-share-list';
import EntryAttachmentList from '@/components/capture/entry-attachment-list';
import { MediaCanvasItemType } from '@/types/capture';

interface Friend {
id: string;
Expand Down Expand Up @@ -91,6 +93,11 @@ export default function DetailsScreen() {
const { toast } = useToast();

const [showEditorPopover, setShowEditorPopover] = useState<boolean>(false);
const [showAttachmentList, setShowAttachmentList] = useState<boolean>(false);
const [editorDefaultTab, setEditorDefaultTab] = useState<MediaCanvasItemType | undefined>(undefined);
const [pendingTextItemId, setPendingTextItemId] = useState<number | null>(null);
const [pendingTextValue, setPendingTextValue] = useState<string>("");
const [attachmentListStateBeforeEditor, setAttachmentListStateBeforeEditor] = useState<boolean>(false);



Expand Down Expand Up @@ -139,7 +146,95 @@ export default function DetailsScreen() {



const { viewShotRef, items, addText, addSticker, saveImage, addMusic, addLocation, removeElement } = useMediaCanvas();
const { viewShotRef, items, addText, addSticker, addMusic, addLocation, removeElement, updateTextItem } = useMediaCanvas();

// Custom addText handler that handles pending text items
const handleAddText = (text: string, style: { color: string; fontFamily?: string; backgroundColor?: string }) => {
// If there's a pending text item, remove it first
if (pendingTextItemId !== null) {
removeElement(pendingTextItemId);
setPendingTextItemId(null);
setPendingTextValue("");
}
// Add the new text item
addText(text, style);
};

// Handle attachment type selection
const handleAttachmentSelect = (type: MediaCanvasItemType) => {
// Save the current attachment list state before opening editor
setAttachmentListStateBeforeEditor(showAttachmentList);

if (type === "text") {
// Auto-add text with default value
const defaultText = "Enter text";
const defaultStyle = {
color: "#FFFFFF",
fontFamily: "Arial",
backgroundColor: "#000000",
};
const tempId = addText(defaultText, defaultStyle); // Returns the ID
setPendingTextItemId(tempId);
setPendingTextValue(defaultText);
// Open editor with text tab
setEditorDefaultTab("text");
setShowEditorPopover(true);
} else {
// For other types, just open the editor with the selected tab
setEditorDefaultTab(type);
setShowEditorPopover(true);
}
setShowAttachmentList(false);
};

// Handle editor popover close
const handleEditorClose = (currentText?: string) => {
// If there's a pending text item and it hasn't been changed or is empty, remove it
const textValue = currentText !== undefined ? currentText : pendingTextValue;
if (pendingTextItemId !== null && (textValue === "Enter text" || !textValue.trim())) {
removeElement(pendingTextItemId);
setPendingTextItemId(null);
setPendingTextValue("");
}
setShowEditorPopover(false);
setEditorDefaultTab(undefined);
// Restore the attachment list state to what it was before opening the editor
setShowAttachmentList(attachmentListStateBeforeEditor);
};

// Handle text changes in editor - update in real-time
const handleTextChange = (text: string) => {
if (pendingTextItemId !== null) {
setPendingTextValue(text);
// Find the current style from the item
const currentItem = items.find(item => item.id === pendingTextItemId);
if (currentItem && currentItem.type === "text") {
updateTextItem(pendingTextItemId, text, currentItem.style || {
color: "#FFFFFF",
fontFamily: "Arial",
backgroundColor: "#000000",
});
}
}
};

// Handle style changes in real-time
const handleStyleChange = (styleUpdates: { color?: string; fontFamily?: string; backgroundColor?: string }) => {
if (pendingTextItemId !== null) {
const currentItem = items.find(item => item.id === pendingTextItemId);
if (currentItem && currentItem.type === "text") {
const updatedStyle = {
...currentItem.style,
...styleUpdates,
};
updateTextItem(
pendingTextItemId,
currentItem.text ?? pendingTextValue,
updatedStyle as { color: string; fontFamily?: string; backgroundColor?: string }
);
}
}
};



Expand Down Expand Up @@ -255,58 +350,65 @@ export default function DetailsScreen() {
};

return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity
style={styles.cancelButton}
onPress={() => router.back()}
>
<X color="#64748B" size={24} />
</TouchableOpacity>

<Text style={styles.title}>Add Details</Text>

<TouchableOpacity
style={styles.cancelButton}
onPress={() => setShowEditorPopover(true)}
>
<Sticker color="#64748B" size={24} />
</TouchableOpacity>
</View>

<ScrollView style={styles.scrollContent} showsVerticalScrollIndicator={false}>
<Animated.View
style={[styles.mediaContainer, capture?.type === 'audio' && styles.borderContainer]}
>
{capture?.type === 'photo' && capture.uri ? (
<MediaCanvas
uri={capture.uri}
type='photo'
ref={viewShotRef}
items={items}
transformsRef={transformsRef}
removeElement={removeElement}
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<TouchableOpacity
style={styles.cancelButton}
onPress={() => router.back()}
>
<X color="#64748B" size={24} />
</TouchableOpacity>

<Text style={styles.title}>Add Details</Text>

<TouchableOpacity
style={styles.cancelButton}
onPress={() => setShowAttachmentList(!showAttachmentList)}
>
{
showAttachmentList ? (
<UserPlus2 color="#64748B" size={24} />
) : (
<Sticker color="#64748B" size={24} />
)
}
</TouchableOpacity>
</View>

<ScrollView style={styles.scrollContent} showsVerticalScrollIndicator={false}>
<Animated.View
style={[styles.mediaContainer, capture?.type === 'audio' && styles.borderContainer]}
>
{capture?.type === 'photo' && capture.uri ? (
<MediaCanvas
uri={capture.uri}
type='photo'
ref={viewShotRef}
items={items}
transformsRef={transformsRef}
removeElement={removeElement}
/>
) :
capture?.type === 'video' ? (
<Pressable onPress={() => videPlaying ? player.pause() : player.play()}>
<VideoView
style={styles.mediaPreview}
player={player}
contentFit='cover'
/>
) :
capture?.type === 'video' ? (
<Pressable onPress={() => videPlaying ? player.pause() : player.play()}>
<VideoView
style={styles.mediaPreview}
player={player}
contentFit='cover'
/>
</Pressable>
) :
capture?.type === 'audio' ? (
<AudioEntry entry={capture}/>
) : null}
</Animated.View>

<View style={styles.form}>
</Pressable>
) :
capture?.type === 'audio' ? (
<AudioEntry entry={capture}/>
) : null}
</Animated.View>

<View style={styles.form}>
{showAttachmentList ? (
<EntryAttachmentList
onSelectAttachment={handleAttachmentSelect}
/>
) : (
<EntryShareList
isPrivate={isPrivate}
isEveryone={isEveryone}
Expand All @@ -316,30 +418,34 @@ export default function DetailsScreen() {
handleFriendToggle={handleFriendToggle}
friends={realFriends}
/>
)}

<TouchableOpacity
style={[
styles.saveButton,
(isLoading || !hasSelectedSharing()) && styles.saveButtonDisabled
]}
onPress={handleSave}
disabled={isLoading || !hasSelectedSharing()}
>
<Text style={styles.saveButtonText}>{getSaveButtonText()}</Text>
</TouchableOpacity>
</View>
</ScrollView>

<EditorPopover
isVisible={showEditorPopover}
onClose={() => setShowEditorPopover(false)}
addText={addText}
addSticker={addSticker}
addMusic={addMusic}
addLocation={addLocation}
/>
</SafeAreaView>
</KeyboardAvoidingView>
<TouchableOpacity
style={[
styles.saveButton,
(isLoading || !hasSelectedSharing()) && styles.saveButtonDisabled
]}
onPress={handleSave}
disabled={isLoading || !hasSelectedSharing()}
>
<Text style={styles.saveButtonText}>{getSaveButtonText()}</Text>
</TouchableOpacity>
</View>
</ScrollView>

<EditorPopover
isVisible={showEditorPopover}
onClose={handleEditorClose}
addText={handleAddText}
addSticker={addSticker}
addMusic={addMusic}
addLocation={addLocation}
defaultTab={editorDefaultTab}
onTextChange={handleTextChange}
onStyleChange={handleStyleChange}
initialText={pendingTextItemId !== null ? pendingTextValue : undefined}
/>
</SafeAreaView>
);
}

Expand Down
8 changes: 3 additions & 5 deletions frontend/components/capture/canvas/text-canvas-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { View, Text, StyleSheet } from "react-native";

interface TextCanvasItemProps {
text: string,
textStyle?: { color: string; fontFamily?: string }
textStyle?: { color: string; fontFamily?: string; backgroundColor?: string }
}

export function TextCanvasItem({ text, textStyle }: TextCanvasItemProps) {
if (!text) return null;

console.log({ textStyle })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log before merging.

This debug statement will pollute the console in production.

🔎 Proposed fix
-    console.log({ textStyle })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log({ textStyle })
🤖 Prompt for AI Agents
In @frontend/components/capture/canvas/text-canvas-item.tsx at line 11, Remove
the stray debug console.log({ textStyle }) in text-canvas-item.tsx to avoid
noisy production logs; locate the console.log call near the top of the
TextCanvasItem component (or where textStyle is used) and delete it (or replace
with a gated debug/logger call that only runs in development, e.g., using
process.env.NODE_ENV === 'development' if you need retained dev-only logging).

return (
<View style={styles.textContainer}>
<Text testID="canvas-text" style={[styles.textStyle, textStyle]}>
<View style={[styles.textContainer, { backgroundColor: textStyle?.backgroundColor || "#000000" }]}>
<Text testID="canvas-text" style={[styles.textStyle, { color: textStyle?.color, fontFamily: textStyle?.fontFamily }]}>
{text}
</Text>
</View>
Expand All @@ -22,12 +22,10 @@ const styles = StyleSheet.create({
textContainer: {
paddingVertical: 6,
paddingHorizontal: 12,
backgroundColor: "black",
borderRadius: 45
},
textStyle: {
fontSize: 12,
color: "white",
fontWeight: "500"
},
})
Loading