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
188 changes: 95 additions & 93 deletions app/(tabs-user)/diary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,118 +293,120 @@ export default function DiaryScreen() {
>
{/* 상단 질문 컴포넌트 */}
<GradientBanner className="px-4 items-center">
<Text className="text-xl font-bold text-center flex-wrap px-8 text-wrap w-3/4">
<Text className="text-2xl font-bold text-center flex-wrap px-8 text-wrap w-3/4">
{isFetchingRandomQuestion
? "질문을 불러오는 중입니다"
: (randomQuestion?.question?.content ?? "")}
</Text>
</GradientBanner>

<View className="w-full mt-8 px-5">
{/* 녹음 타이머 - 비주얼라이저 위쪽 */}
<View className="flex items-center justify-center mb-3">
<RecordingTimer
duration={recordingState.durationMillis / 1000}
isRecording={recordingState.isRecording}
isPaused={!recordingState.isRecording}
/>
</View>
{/* 녹음 파형 컴포넌트 */}
<View className="flex flex-row items-center justify-center h-[60px] bg-gray10 rounded-[20px] w-full">
<WaveformVisualizer
isRecording={recordingState.isRecording}
isPaused={
!recordingState.isRecording && recordingState.durationMillis > 0
}
durationMillis={recordingState.durationMillis}
metering={recordingState.metering}
heightAnimValue={waveHeightAnim}
/>
</View>
</View>

<View className="flex-row items-center justify-center px-4 mt-10 relative w-full">
{!recordingState.isRecording &&
recordingState.durationMillis > 0 &&
!isSaving && (
<View className="absolute left-8">
<Button size="lg" variant="filled" onPress={handleReset}>
<Text className="text-white text-[15px] font-bold px-5">
취소
</Text>
</Button>
</View>
)}
{/* 녹음 조정 버튼 - 항상 가운데 */}
<View className="items-center justify-center" style={{ width: BUTTON_SIZE * MAX_RING_SCALE, height: BUTTON_SIZE * MAX_RING_SCALE }}>
{/* 뒤쪽 링 - 녹음 중일 때만 표시되고 비주얼라이저에 비례해 커짐 */}
{recordingState.isRecording && (
<Animated.View
style={{
position: 'absolute',
width: BUTTON_SIZE,
height: BUTTON_SIZE,
borderRadius: BUTTON_SIZE / 2,
backgroundColor: ringColor,
opacity: 0.3,
transform: [{ scale: ringScale }],
}}
<View className="h-full flex items-center justify-center">
<View className="w-full mt-8 px-5">
{/* 녹음 타이머 - 비주얼라이저 위쪽 */}
<View className="flex items-center justify-center mb-3">
<RecordingTimer
duration={recordingState.durationMillis / 1000}
isRecording={recordingState.isRecording}
isPaused={!recordingState.isRecording}
/>
)}
{/* 실제 버튼 - 크기 고정 */}
<TouchableOpacity
className="rounded-full flex items-center justify-center bg-main500"
style={{ width: BUTTON_SIZE, height: BUTTON_SIZE }}
onPress={handleMainButtonPress}
>
<Icon
name={recordingState.isRecording ? "Stop" : "Mic"}
width={40}
height={40}
</View>
Comment on lines +306 to +312
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

isPaused 로직 오류 — 녹음 시작 전에도 타이머가 항상 표시됩니다.

isPaused={!recordingState.isRecording}으로 설정하면 isRecordingisPaused 중 하나가 항상 true이므로, RecordingTimernull을 반환하지 않습니다. 결과적으로 녹음을 시작하지 않은 초기 상태에서도 "00:00" 타이머가 표시됩니다.

durationMillis > 0 조건을 추가하여 실제로 녹음이 일시정지된 경우에만 타이머를 표시해야 합니다.

🐛 수정 제안
              <RecordingTimer
                duration={recordingState.durationMillis / 1000}
                isRecording={recordingState.isRecording}
-               isPaused={!recordingState.isRecording}
+               isPaused={!recordingState.isRecording && recordingState.durationMillis > 0}
              />
📝 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
<View className="flex items-center justify-center mb-3">
<RecordingTimer
duration={recordingState.durationMillis / 1000}
isRecording={recordingState.isRecording}
isPaused={!recordingState.isRecording}
/>
)}
{/* 실제 버튼 - 크기 고정 */}
<TouchableOpacity
className="rounded-full flex items-center justify-center bg-main500"
style={{ width: BUTTON_SIZE, height: BUTTON_SIZE }}
onPress={handleMainButtonPress}
>
<Icon
name={recordingState.isRecording ? "Stop" : "Mic"}
width={40}
height={40}
</View>
<View className="flex items-center justify-center mb-3">
<RecordingTimer
duration={recordingState.durationMillis / 1000}
isRecording={recordingState.isRecording}
isPaused={!recordingState.isRecording && recordingState.durationMillis > 0}
/>
</View>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(tabs-user)/diary.tsx around lines 306 - 312, The RecordingTimer is
always shown because isPaused is set to !recordingState.isRecording; change the
logic so the timer only appears when there's an actual recorded duration by
using recordingState.durationMillis > 0. Update the RecordingTimer
props/conditional render around RecordingTimer (referencing RecordingTimer,
recordingState.durationMillis, recordingState.isRecording) so isPaused becomes
(!recordingState.isRecording && recordingState.durationMillis > 0) and/or wrap
the <RecordingTimer ... /> render in a guard that only renders when
recordingState.durationMillis > 0.

{/* 녹음 파형 컴포넌트 */}
<View className="flex flex-row items-center justify-center h-[60px] bg-gray10 rounded-[20px] w-full">
<WaveformVisualizer
isRecording={recordingState.isRecording}
isPaused={
!recordingState.isRecording && recordingState.durationMillis > 0
}
durationMillis={recordingState.durationMillis}
metering={recordingState.metering}
heightAnimValue={waveHeightAnim}
/>
</TouchableOpacity>
</View>
</View>

{!recordingState.isRecording &&
recordingState.durationMillis > 0 &&
!isSaving && (
<View className="flex-row items-center justify-center px-4 mt-10 relative w-full">
{!recordingState.isRecording &&
recordingState.durationMillis > 0 &&
!isSaving && (
<View className="absolute left-8">
<Button size="lg" variant="filled" onPress={handleReset}>
<Text className="text-white text-[15px] font-bold px-5">
취소
</Text>
</Button>
</View>
)}
{/* 녹음 조정 버튼 - 항상 가운데 */}
<View className="items-center justify-center" style={{ width: BUTTON_SIZE * MAX_RING_SCALE, height: BUTTON_SIZE * MAX_RING_SCALE }}>
{/* 뒤쪽 링 - 녹음 중일 때만 표시되고 비주얼라이저에 비례해 커짐 */}
{recordingState.isRecording && (
<Animated.View
style={{
position: 'absolute',
width: BUTTON_SIZE,
height: BUTTON_SIZE,
borderRadius: BUTTON_SIZE / 2,
backgroundColor: ringColor,
opacity: 0.3,
transform: [{ scale: ringScale }],
}}
/>
)}
{/* 실제 버튼 - 크기 고정 */}
<TouchableOpacity
className="rounded-full flex items-center justify-center bg-main500"
style={{ width: BUTTON_SIZE, height: BUTTON_SIZE }}
onPress={handleMainButtonPress}
>
<Icon
name={recordingState.isRecording ? "Stop" : "Mic"}
size={40}
/>
Comment on lines 361 to 364
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the Icon/SvgIcon component definition
echo "=== Finding Icon/SvgIcon component ==="
find . -type f -name "*.tsx" -o -name "*.ts" | xargs grep -l "export.*Icon" | grep -E "(SvgIcon|Icon)" | head -20

echo ""
echo "=== Searching for SvgIcon.tsx ==="
find . -type f -name "SvgIcon.tsx"

echo ""
echo "=== Searching for Icon component definition ==="
find . -type f \( -name "*.tsx" -o -name "*.ts" \) | xargs grep -l "interface IconProps\|type IconProps" 2>/dev/null

Repository: safori-team/CARING-front

Length of output: 330


🏁 Script executed:

#!/bin/bash
# Check the specific file and lines mentioned
echo "=== Checking diary.tsx file ==="
if [ -f "app/(tabs-user)/diary.tsx" ]; then
  echo "File exists. Showing context around lines 361-365:"
  sed -n '355,370p' "app/(tabs-user)/diary.tsx"
  echo ""
  echo "Showing context around line 380:"
  sed -n '375,385p' "app/(tabs-user)/diary.tsx"
else
  echo "File not found at app/(tabs-user)/diary.tsx"
  find . -name "diary.tsx" -type f
fi

Repository: safori-team/CARING-front

Length of output: 1300


🏁 Script executed:

#!/bin/bash
# Search for Icon component usage patterns and definition
echo "=== Searching for Icon/SvgIcon import and definition ==="
rg -n "type IconProps|interface IconProps" --type=ts --type=tsx -B2 -A10

echo ""
echo "=== Searching for Icon component export ==="
rg -n "export.*Icon" --type=ts --type=tsx | grep -E "(function|const|class)" | head -20

Repository: safori-team/CARING-front

Length of output: 242


🏁 Script executed:

#!/bin/bash
echo "=== SvgIcon.tsx component definition ==="
cat -n "./shared/ui/svg/SvgIcon.tsx"

echo ""
echo "=== icon.d.ts type definitions ==="
cat -n "./shared/ui/svg/icon.d.ts"

echo ""
echo "=== Icon import in diary.tsx ==="
rg -n "import.*Icon" "app/(tabs-user)/diary.tsx" -A2

Repository: safori-team/CARING-front

Length of output: 1409


🏁 Script executed:

#!/bin/bash
# Check for Icon/SvgIcon usage patterns in the entire codebase
echo "=== Icon component usage patterns in codebase ==="
rg -n "Icon\s+\w+=" --type=tsx | head -30

Repository: safori-team/CARING-front

Length of output: 162


🏁 Script executed:

#!/bin/bash
# Check Icon import in diary.tsx more carefully
echo "=== Icon import in diary.tsx ==="
head -50 "app/(tabs-user)/diary.tsx" | grep -n "Icon"

echo ""
echo "=== Check if there are multiple Icon imports ==="
rg -n "^import.*Icon" "app/(tabs-user)/diary.tsx"

echo ""
echo "=== Verify SvgProps type to understand full interface ==="
rg -n "SvgProps" "./shared/ui/svg/" -A5

Repository: safori-team/CARING-front

Length of output: 657


🏁 Script executed:

#!/bin/bash
# Let's verify how Icon is imported in diary.tsx to ensure it's the same component
echo "=== Full import statement context in diary.tsx ==="
head -40 "app/(tabs-user)/diary.tsx" | tail -20

Repository: safori-team/CARING-front

Length of output: 595


Icon 컴포넌트에 width/height props가 전달되지만 무시됩니다.

Icon 컴포넌트는 { name, size, onPress }만 받으므로, 여기서 전달하는 width={40} height={40}은 무시되어 아이콘이 의도한 크기로 렌더링되지 않습니다. Line 380의 width={20} height={20}도 동일한 문제입니다.

수정 방법

Line 361-365:

              <Icon
                name={recordingState.isRecording ? "Stop" : "Mic"}
-               width={40}
-               height={40}
+               size={40}
              />

Line 380:

-                   <Icon name="CheckIcon" width={20} height={20} />
+                   <Icon name="CheckIcon" size={20} />
📝 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
<Icon
name={recordingState.isRecording ? "Stop" : "Mic"}
width={40}
height={40}
/>
<Icon
name={recordingState.isRecording ? "Stop" : "Mic"}
size={40}
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(tabs-user)/diary.tsx around lines 361 - 365, The Icon component is
being passed width and height props which are ignored because Icon only accepts
{ name, size, onPress }; update the usages where Icon is rendered (the Icon with
name computed via recordingState.isRecording and the smaller Icon at the later
usage) to pass a single numeric size prop (e.g., size={40} and size={20})
instead of width/height, or alternatively update the Icon component signature to
accept and map width/height into size; locate the Icon calls by the name prop
usage (name={recordingState.isRecording ? "Stop" : "Mic"}) and change to
size={40} (and the other Icon to size={20}) so the icons render at the intended
dimensions.

</TouchableOpacity>
</View>

{!recordingState.isRecording &&
recordingState.durationMillis > 0 &&
!isSaving && (
<View className="absolute right-8">
<Button
size="lg"
variant="outlined"
className="flex-row items-center justify-center"
innerClassName="flex-row items-center justify-center pl-4"
onPress={handleSaveServer}
>
<Icon name="CheckIcon" size={20} />
<Text className="text-main500 text-[15px] font-bold pl-2 pr-4">
완료
</Text>
</Button>
</View>
)}
{isSaving && (
<View className="absolute right-8">
<Button
size="lg"
variant="outlined"
className="flex-row items-center justify-center"
innerClassName="flex-row items-center justify-center pl-4"
onPress={handleSaveServer}
size="lg"
variant="filled"
disabled
>
<Icon name="CheckIcon" width={20} height={20} />
<Text className="text-main500 text-[15px] font-bold pl-2 pr-4">
완료
<ActivityIndicator size="small" color="white" />
<Text className="text-white text-[15px] font-bold pl-2 pr-4">
처리중
</Text>
</Button>
</View>
)}
{isSaving && (
<View className="absolute right-8">
<Button
className="flex-row items-center justify-center gap-x-2"
size="lg"
variant="filled"
disabled
>
<ActivityIndicator size="small" color="white" />
<Text className="text-white text-[15px] font-bold px-5">
처리중
</Text>
</Button>
</View>
)}
</View>
<View className="flex items-center justify-center w-full h-fit">
<SpeechBubble className="h-fit w-3/4 px-0 flex items-center justify-center">
<Text className="text-gray90 text-base text-center">
{currentBubbleText}
</Text>
</SpeechBubble>
</View>
<View className="flex items-center justify-center w-full h-fit">
<SpeechBubble className="h-fit w-3/4 px-0 flex items-center justify-center">
<Text className="text-gray90 text-base text-center">
{currentBubbleText}
</Text>
</SpeechBubble>
</View>
</View>
</ScrollView >
</MainLayout >
Expand Down
2 changes: 1 addition & 1 deletion shared/ui/audio/recording-timer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function RecordingTimer({
style={{ width: 18, height: 18 }}
/>
)}
<Text className="text-black text-lg font-normal">
<Text className="text-black text-[21px] font-normal">
{formatTime(duration)}
</Text>
</View>
Expand Down