Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
135e3bb
style: ui 개선-탭 구성 변경
Monixc Sep 13, 2025
43520c7
style: UI 색상 및 스타일 개선
Monixc Sep 13, 2025
6907c9a
style: FloatingButton 컴포넌트의 배경 스타일 수정
Monixc Sep 13, 2025
79f697a
fix: useRunModals에서 navigation 타입 수정
Monixc Sep 13, 2025
79338ba
feat: 추천 경로 기능 추가 및 UI 개선
Monixc Sep 13, 2025
6f996b7
fix: RecommendationContainer에서 departure 좌표 형식 수정
Monixc Sep 13, 2025
6de1cf0
fix: useLongPress 훅에서 불필요한 상태 제거
Monixc Sep 13, 2025
2762037
feat: useRunModals에서 코스 선택 시 최소 이동 거리 체크 및 API 응답 로그 추가
Monixc Sep 13, 2025
ddfa5cb
style: UI 요소의 위치 및 스타일 조정
Monixc Sep 13, 2025
d51d4ab
style: RunTabNavigator 및 Detail 페이지의 배경색 변경
Monixc Sep 13, 2025
328fc6d
feat: 런닝 기록 및 대시보드 API 훅 추가
Monixc Sep 13, 2025
3c38d36
feat: 추천 경로 카드에 안내 카드 추가 및 UI 개선
Monixc Sep 13, 2025
72f025e
style: Card 및 RecommendationContainer 컴포넌트의 스타일 수정
Monixc Sep 14, 2025
249229d
fix: RunTabNavigator 및 Detail 페이지의 텍스트 수정
Monixc Sep 14, 2025
87cfe76
feat: 지도 컴포넌트에 onMapReady 콜백 추가 및 그리기 기능 개선
Monixc Sep 14, 2025
ea1d043
feat: LoadingIndicator 컴포넌트에 메시지 프로퍼티 추가 및 RouteSave 페이지 내비게이션 수정
Monixc Sep 14, 2025
f4beefa
feat: Draw 탭 이탈 시 그린 선 초기화 로직 수정 및 RunTabNavigator 리셋 컴포넌트 추가
Monixc Sep 14, 2025
ad265da
feat: 캡처 기능 개선 및 그리기 소스 표시 로직 추가
Monixc Sep 14, 2025
06ab1c0
feat: eas.json에 Android 빌드 타입 추가
Monixc Sep 14, 2025
a29e436
feat: eas.json에 Android 빌드 타입 추가 및 불필요한 공백 제거
Monixc Sep 14, 2025
7413ab5
feat: Google 로그인 기능 개선 및 오류 처리 로깅 추가
Monixc Sep 14, 2025
996f747
로그 메시지 영어로 변경 및 오류 메시지 개선
Monixc Sep 14, 2025
dc3b876
fix: 상태 및 api 관련 오류 수정
Monixc Sep 14, 2025
6a62317
refactor: 로딩 상태 확인 및 API 호출 로직 개선
Monixc Sep 15, 2025
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
9 changes: 8 additions & 1 deletion eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
"preview": {
"distribution": "internal"
},
"production": {},
"production": {
"android": {
"buildType": "apk"
}
},
"development-simulator": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk"
},
"environment": "development"
}
},
Expand Down
94 changes: 59 additions & 35 deletions src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface CardProps {
onPress?: () => void;
style?: ViewStyle;
fullWidth?: boolean;
variant?: 'default' | 'light';
}

export default function Card({
Expand All @@ -31,6 +32,7 @@ export default function Card({
onPress,
style,
fullWidth = false,
variant = 'default',
}: CardProps) {
const { width: screenWidth } = Dimensions.get('window');
const [imageError, setImageError] = useState(false);
Expand All @@ -52,6 +54,7 @@ export default function Card({
<CardContainer
fullWidth={fullWidth}
mode={finalMode}
variant={variant}
onPress={onPress}
style={style}
activeOpacity={0.8}
Expand All @@ -77,7 +80,7 @@ export default function Card({
</LoadingContainer>
)}
{content?.hasStar && (
<StarIcon size={20} color="#FFD700" fill="#FFD700" />
<StarIcon size={20} color="#2d2d2d" fill="#2d2d2d" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

스타일 가이드에 따르면 색상 값은 theme 또는 constants 파일로 관리해야 합니다.1 #2d2d2d와 같은 색상 값을 직접 사용하기보다, colors.ts에 정의된 테마 색상을 사용하도록 수정하는 것이 좋겠습니다.

Style Guide References

Footnotes

  1. 재사용 가능한 색상, 폰트, spacing은 theme 또는 constants 파일로 관리해야 합니다.

)}
</ImageContainer>
</CardContainer>
Expand All @@ -86,16 +89,19 @@ export default function Card({

if (finalMode === 'image-with-text') {
const contentWidth = screenWidth - 64;
const imageSize = contentWidth * 0.45;
const imageSize = contentWidth * 0.36; // 0.45에서 0.36으로 변경 (약 20% 감소)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

스타일 가이드에서는 매직 넘버 사용을 지양하고 이름 있는 상수로 치환할 것을 권장합니다.1 0.36이라는 값을 IMAGE_SIZE_RATIO와 같은 명확한 이름의 상수로 정의하여 사용하면 코드의 가독성과 유지보수성을 높일 수 있습니다.

    const IMAGE_SIZE_RATIO = 0.36;
    const imageSize = contentWidth * IMAGE_SIZE_RATIO; // 0.45에서 0.36으로 변경 (약 20% 감소)

Style Guide References

Footnotes

  1. 매직 넘버(예: 200, 0.5 등)는 이름 있는 상수로 치환해야 합니다.

return (
<CardContainer
fullWidth={fullWidth}
mode={finalMode}
variant={variant}
onPress={onPress}
style={style}
activeOpacity={0.8}
>
{content?.cardTitle && <CardTitle>{content.cardTitle}</CardTitle>}
{content?.cardTitle && (
<CardTitle variant={variant}>{content.cardTitle}</CardTitle>
)}
<ContentRow>
{imageSource && (
<CardImage
Expand All @@ -107,14 +113,18 @@ export default function Card({
)}
{content && (
<CardContentContainer>
{content.title && <Title>{content.title}</Title>}
{content.subtitle && <Subtitle>{content.subtitle}</Subtitle>}
{content.title && (
<Title variant={variant}>{content.title}</Title>
)}
{content.subtitle && (
<Subtitle variant={variant}>{content.subtitle}</Subtitle>
)}
{content.stats && (
<StatsContainer>
{content.stats.map((stat, index) => (
<StatItem key={index}>
<StatLabel>{stat.label}</StatLabel>
<StatValue>{stat.value}</StatValue>
<StatLabel variant={variant}>{stat.label}</StatLabel>
<StatValue variant={variant}>{stat.value}</StatValue>
</StatItem>
))}
</StatsContainer>
Expand All @@ -130,6 +140,7 @@ export default function Card({
<CardContainer
fullWidth={fullWidth}
mode={finalMode}
variant={variant}
onPress={onPress}
style={style}
activeOpacity={0.8}
Expand All @@ -142,9 +153,11 @@ export default function Card({
const CardContainer = styled(TouchableOpacity)<{
fullWidth: boolean;
mode: CardMode;
}>(({ fullWidth, mode }) => ({
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderColor: 'rgba(255, 255, 255, 0.2)',
variant: 'default' | 'light';
}>(({ fullWidth, mode, variant }) => ({
backgroundColor: variant === 'light' ? '#ffffff' : 'rgba(255, 255, 255, 0.1)',
borderColor:
variant === 'light' ? 'rgba(0, 0, 0, 0.1)' : 'rgba(255, 255, 255, 0.2)',
Comment on lines +157 to +160

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

스타일 가이드에 따라 색상 값은 theme 또는 constants 파일에서 관리하는 것이 좋습니다.1 variant에 따라 색상을 분기 처리하는 로직이 여러 컴포넌트에 걸쳐 반복되고 있습니다. Emotion의 theme 객체와 ThemeProvider를 활용하면 이 로직을 중앙에서 관리하고 코드를 더 간결하게 만들 수 있습니다.

예시:

// theme.ts
export const theme = {
  light: {
    background: '#ffffff',
    borderColor: 'rgba(0, 0, 0, 0.1)',
    text: '#000000',
  },
  dark: {
    background: 'rgba(255, 255, 255, 0.1)',
    borderColor: 'rgba(255, 255, 255, 0.2)',
    text: '#ffffff',
  }
}

// CardContainer.tsx
const CardContainer = styled(TouchableOpacity)<...>(({ theme, variant }) => ({
  backgroundColor: theme[variant].background,
  borderColor: theme[variant].borderColor,
  // ...
}));

Style Guide References

Footnotes

  1. 재사용 가능한 색상, 폰트, spacing은 theme 또는 constants 파일로 관리해야 합니다.

borderWidth: 1,
borderRadius: 12,
padding: mode === 'only-image' ? 0 : 16,
Expand All @@ -153,12 +166,14 @@ const CardContainer = styled(TouchableOpacity)<{
aspectRatio: fullWidth ? undefined : 1,
}));

const CardTitle = styled.Text({
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
marginBottom: 8,
});
const CardTitle = styled.Text<{ variant?: 'default' | 'light' }>(
({ variant }) => ({
color: variant === 'light' ? '#000000' : '#ffffff',
fontSize: 16,
fontWeight: '600',
marginBottom: 8,
}),
);

const ContentRow = styled.View({
flexDirection: 'row',
Expand All @@ -185,18 +200,21 @@ const CardContentContainer = styled.View({
flex: 1,
});

const Title = styled.Text({
color: '#ffffff',
const Title = styled.Text<{ variant?: 'default' | 'light' }>(({ variant }) => ({
color: variant === 'light' ? '#000000' : '#ffffff',
fontSize: 18,
fontWeight: 'bold',
marginBottom: 4,
});
}));

const Subtitle = styled.Text({
color: 'rgba(255, 255, 255, 0.7)',
fontSize: 14,
marginBottom: 8,
});
const Subtitle = styled.Text<{ variant?: 'default' | 'light' }>(
({ variant }) => ({
color:
variant === 'light' ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)',
fontSize: 14,
marginBottom: 8,
}),
);

const StatsContainer = styled.View({
flexDirection: 'row',
Expand All @@ -207,17 +225,23 @@ const StatItem = styled.View({
alignItems: 'center',
});

const StatLabel = styled.Text({
color: 'rgba(255, 255, 255, 0.8)',
fontSize: 12,
marginBottom: 2,
});
const StatLabel = styled.Text<{ variant?: 'default' | 'light' }>(
({ variant }) => ({
color:
variant === 'light' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
fontSize: 12,
marginBottom: 2,
}),
);

const StatValue = styled.Text({
color: 'rgba(255, 255, 255, 0.8)',
fontSize: 14,
fontWeight: '600',
});
const StatValue = styled.Text<{ variant?: 'default' | 'light' }>(
({ variant }) => ({
color:
variant === 'light' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
fontSize: 14,
fontWeight: '600',
}),
);

const StarIcon = styled(Star)({
position: 'absolute',
Expand Down Expand Up @@ -254,7 +278,7 @@ const LoadingContainer = styled.View({
});

const LoadingText = styled.Text({
color: '#007AFF',
color: '#2d2d2d',
fontSize: 12,
fontWeight: '500',
});
8 changes: 7 additions & 1 deletion src/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface MapProps {
cameraRef: RefObject<Mapbox.Camera | null>;
initialLocation: Position | null;
onUserLocationUpdate?: (location: Mapbox.Location) => void;
onMapReady?: () => void;
children?: React.ReactNode;
showUserLocation?: boolean;
}
Expand All @@ -18,14 +19,19 @@ export default function Map({
cameraRef,
initialLocation,
onUserLocationUpdate,
onMapReady,
children,
showUserLocation = true,
}: MapProps) {
// initialLocation이 없으면 기본 서울 좌표 사용
const safeInitialLocation = initialLocation || [127.0276, 37.4979];

return (
<StyledMapView ref={mapRef} styleURL={Mapbox.StyleURL.Street}>
<StyledMapView
ref={mapRef}
styleURL={Mapbox.StyleURL.Street}
onDidFinishLoadingMap={onMapReady}
>
<Mapbox.Camera
ref={cameraRef}
defaultSettings={{
Expand Down
29 changes: 24 additions & 5 deletions src/components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Modal as RNModal, Text, TouchableOpacity, View } from 'react-native';
import styled from '@emotion/native';
import { LinearGradient } from 'expo-linear-gradient';
import { theme } from '@/styles/theme';

interface ModalProps {
Expand Down Expand Up @@ -38,13 +39,23 @@ export default function Modal({
<ModalMessage>{message}</ModalMessage>
<ButtonContainer>
<ModalButton onPress={onCancel} disabled={disabled || loading}>
<ModalButtonGradient
colors={['#1a1a1a', '#2d2d2d', '#404040']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
/>
Comment on lines +42 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

스타일 가이드에 따라 색상 값은 theme 또는 constants 파일로 관리하는 것이 좋습니다.1 그라데이션에 사용된 색상 값들을 colors.ts와 같은 상수 파일로 분리하여 관리하면 일관성 유지 및 추후 수정이 용이해집니다.

Style Guide References

Footnotes

  1. 재사용 가능한 색상, 폰트, spacing은 theme 또는 constants 파일로 관리해야 합니다.

<ModalButtonText>{cancelText}</ModalButtonText>
</ModalButton>
<ModalButton
onPress={onConfirm}
isPrimary
disabled={disabled || loading}
>
<ModalButtonGradient
colors={['#1a1a1a', '#2d2d2d', '#404040']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
/>
<ModalButtonText isPrimary>
{loading ? '저장 중...' : confirmText}
</ModalButtonText>
Expand Down Expand Up @@ -101,16 +112,24 @@ const ModalButton = styled(TouchableOpacity)<{
flex: 1;
padding: 12px 16px;
border-radius: 8px;
background-color: ${({ isPrimary, disabled }) => {
if (disabled) return theme.colors.gray[300];
return isPrimary ? theme.colors.primary[500] : theme.colors.gray[100];
}};
align-items: center;
position: relative;
overflow: hidden;
opacity: ${({ disabled }) => (disabled ? 0.6 : 1)};
`;

const ModalButtonGradient = styled(LinearGradient)`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 8px;
`;

const ModalButtonText = styled(Text)<{ isPrimary?: boolean }>`
font-size: 16px;
font-weight: 500;
color: ${({ isPrimary }) => (isPrimary ? '#ffffff' : theme.colors.gray[700])};
color: #ffffff;
z-index: 1;
`;
4 changes: 2 additions & 2 deletions src/components/TabNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ const TabButton = styled.TouchableOpacity<{
alignItems: 'center',
paddingVertical: 8,
borderBottomWidth: isActive ? 2 : 0,
borderBottomColor: '#ff6b35',
borderBottomColor: '#2d2d2d',
}));

const TabText = styled.Text<{
isActive: boolean;
}>(({ isActive }) => ({
fontSize: 14,
fontWeight: isActive ? '600' : '400',
color: isActive ? '#ff6b35' : '#666666',
color: isActive ? '#2d2d2d' : '#666666',
}));
Loading