Compare commits
3 Commits
355c9d7743
...
982e307482
| Author | SHA1 | Date | |
|---|---|---|---|
| 982e307482 | |||
| 1c60a450b9 | |||
| 81897144c6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ yarn-error.*
|
||||
/android
|
||||
.env.*
|
||||
.idea
|
||||
package-lock.json
|
||||
|
||||
5
app.json
5
app.json
@@ -27,6 +27,9 @@
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"@react-native-google-signin/google-signin"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
9221
package-lock.json
generated
9221
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-async-storage/async-storage": "2.2.0",
|
||||
"@react-native-google-signin/google-signin": "^16.1.1",
|
||||
"@react-navigation/bottom-tabs": "^7.9.0",
|
||||
"@react-navigation/native": "^7.1.26",
|
||||
"@react-navigation/native-stack": "^7.9.0",
|
||||
"expo": "~54.0.30",
|
||||
"expo-apple-authentication": "~8.0.8",
|
||||
"expo-status-bar": "~3.0.9",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
|
||||
@@ -14,5 +14,14 @@ export type LoginData = {
|
||||
export type LoginResponse = ApiResponse<LoginData>;
|
||||
|
||||
// 회원가입 api
|
||||
export type SignUpRequest = { loginId: string; password: string };
|
||||
export type SignUpResponse = { userId: number };
|
||||
export type SignUpRequest = {
|
||||
loginId: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type SignUpData = {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
export type SignUpResponse = ApiResponse<SignUpData>;
|
||||
|
||||
233
src/components/ui/AudioBookCard.tsx
Normal file
233
src/components/ui/AudioBookCard.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
import React from "react";
|
||||
import { ActivityIndicator, Pressable, StyleSheet, View } from "react-native";
|
||||
import { Theme } from "../../theme/theme";
|
||||
import AppText from "../ui/AppText";
|
||||
import Card from "../ui/Card";
|
||||
|
||||
export type AudioStatus = "QUEUED" | "GENERATING" | "READY" | "FAILED";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
meta: string;
|
||||
status: AudioStatus;
|
||||
progress?: number;
|
||||
onPress?: () => void;
|
||||
onRetry?: () => void;
|
||||
};
|
||||
|
||||
const STATUS_UI: Record<
|
||||
AudioStatus,
|
||||
{ label: string; accent: string; badgeBg: string; badgeText: string }
|
||||
> = {
|
||||
QUEUED: {
|
||||
label: "대기중",
|
||||
accent: "#94a3b8",
|
||||
badgeBg: "rgba(148,163,184,0.15)",
|
||||
badgeText: "#64748b",
|
||||
},
|
||||
GENERATING: {
|
||||
label: "생성중",
|
||||
accent: "#60a5fa",
|
||||
badgeBg: "rgba(96,165,250,0.15)",
|
||||
badgeText: "#3b82f6",
|
||||
},
|
||||
READY: {
|
||||
label: "완료",
|
||||
accent: "#34d399",
|
||||
badgeBg: "rgba(52,211,153,0.15)",
|
||||
badgeText: "#10b981",
|
||||
},
|
||||
FAILED: {
|
||||
label: "실패",
|
||||
accent: "#fb7185",
|
||||
badgeBg: "rgba(251,113,133,0.15)",
|
||||
badgeText: "#e11d48",
|
||||
},
|
||||
};
|
||||
|
||||
export default function AudioBookCard({
|
||||
title,
|
||||
meta,
|
||||
status,
|
||||
progress,
|
||||
onPress,
|
||||
onRetry,
|
||||
}: Props) {
|
||||
const ui = STATUS_UI[status];
|
||||
|
||||
const canPress = status === "READY" && !onPress;
|
||||
const showRetry = status === "FAILED" && !onRetry;
|
||||
|
||||
return (
|
||||
<Card
|
||||
onPress={canPress ? onPress : undefined}
|
||||
disabled={!canPress}
|
||||
accentColor={ui.accent}
|
||||
wrapperStyle={styles.wrapper}
|
||||
>
|
||||
{/* Top row */}
|
||||
<View style={styles.topRow}>
|
||||
<View style={styles.titleBlock}>
|
||||
<AppText variant="title" numberOfLines={1}>
|
||||
{title}
|
||||
</AppText>
|
||||
<View style={{ height: Theme.Spacing.xs }} />
|
||||
<AppText variant="muted" numberOfLines={1}>
|
||||
{meta}
|
||||
</AppText>
|
||||
</View>
|
||||
|
||||
<View style={styles.rightBlock}>
|
||||
<Badge text={ui.label} bg={ui.badgeBg} color={ui.badgeText} />
|
||||
|
||||
{status === "GENERATING" ? (
|
||||
<View style={{ height: Theme.Spacing.sm }} />
|
||||
) : (
|
||||
<View style={{ height: Theme.Spacing.sm }} />
|
||||
)}
|
||||
|
||||
{status === "GENERATING" ? (
|
||||
<ActivityIndicator />
|
||||
) : status === "READY" ? (
|
||||
<PlayPill />
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Middle: generating progress */}
|
||||
{status === "GENERATING" ? (
|
||||
<View style={styles.progressWrap}>
|
||||
<View style={{ height: Theme.Spacing.sm }} />
|
||||
<ProgressBar progress={progress} accent={ui.accent} />
|
||||
<View style={{ height: Theme.Spacing.xs }} />
|
||||
<AppText variant="muted">
|
||||
{typeof progress === "number"
|
||||
? `${Math.round(progress * 100)}% 진행`
|
||||
: "작업 중…"}
|
||||
</AppText>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{/* Bottom: failed actions */}
|
||||
{showRetry ? (
|
||||
<View style={styles.bottomRow}>
|
||||
<AppText variant="muted" style={{ flex: 1 }}>
|
||||
생성에 실패했어요. 다시 시도할까요?
|
||||
</AppText>
|
||||
<Pressable onPress={onRetry} style={styles.retryBtn}>
|
||||
<AppText>재시도</AppText>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : null}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function Badge({
|
||||
text,
|
||||
bg,
|
||||
color,
|
||||
}: {
|
||||
text: string;
|
||||
bg: string;
|
||||
color: string;
|
||||
}) {
|
||||
return (
|
||||
<View style={[styles.badge, { backgroundColor: bg }]}>
|
||||
<AppText style={{ color }}>{text}</AppText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function PlayPill() {
|
||||
return (
|
||||
<View style={styles.playPill}>
|
||||
<AppText>▶︎</AppText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function ProgressBar({
|
||||
progress,
|
||||
accent,
|
||||
}: {
|
||||
progress?: number;
|
||||
accent: string;
|
||||
}) {
|
||||
const pct =
|
||||
typeof progress === "number"
|
||||
? Math.max(0, Math.min(1, progress))
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<View style={styles.progressTrack}>
|
||||
<View
|
||||
style={[
|
||||
styles.progressFill,
|
||||
{
|
||||
backgroundColor: accent,
|
||||
width: pct == null ? "60%" : `${pct * 100}%`,
|
||||
},
|
||||
pct == null && { opacity: 0.55 },
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
marginHorizontal: Theme.Spacing.lg,
|
||||
marginTop: Theme.Spacing.md,
|
||||
},
|
||||
topRow: {
|
||||
flexDirection: "row",
|
||||
gap: Theme.Spacing.md,
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
titleBlock: {
|
||||
flex: 1,
|
||||
},
|
||||
rightBlock: {
|
||||
alignItems: "flex-end",
|
||||
gap: Theme.Spacing.xs,
|
||||
},
|
||||
badge: {
|
||||
paddingHorizontal: Theme.Spacing.sm,
|
||||
paddingVertical: Theme.Spacing.xs,
|
||||
borderRadius: 999,
|
||||
},
|
||||
playPill: {
|
||||
paddingHorizontal: Theme.Spacing.md,
|
||||
paddingVertical: Theme.Spacing.xs,
|
||||
borderRadius: 999,
|
||||
borderWidth: 1,
|
||||
borderColor: Theme.Colors.border,
|
||||
},
|
||||
progressWrap: {
|
||||
marginTop: Theme.Spacing.md,
|
||||
},
|
||||
progressTrack: {
|
||||
height: 8,
|
||||
borderRadius: 999,
|
||||
backgroundColor: "rgba(148,163,184,0.18)",
|
||||
overflow: "hidden",
|
||||
},
|
||||
progressFill: {
|
||||
height: "100%",
|
||||
borderRadius: 999,
|
||||
},
|
||||
bottomRow: {
|
||||
marginTop: Theme.Spacing.md,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Theme.Spacing.sm,
|
||||
},
|
||||
retryBtn: {
|
||||
paddingHorizontal: Theme.Spacing.md,
|
||||
paddingVertical: Theme.Spacing.xs,
|
||||
borderRadius: 999,
|
||||
borderWidth: 1,
|
||||
borderColor: Theme.Colors.border,
|
||||
},
|
||||
});
|
||||
131
src/components/ui/Card.tsx
Normal file
131
src/components/ui/Card.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
View,
|
||||
type StyleProp,
|
||||
type ViewProps,
|
||||
type ViewStyle,
|
||||
} from "react-native";
|
||||
import { Theme } from "../../theme/theme";
|
||||
|
||||
type Props = Omit<ViewProps, "style"> & {
|
||||
style?: StyleProp<ViewStyle>;
|
||||
wrapperStyle?: StyleProp<ViewStyle>;
|
||||
onPress?: () => void;
|
||||
disabled?: boolean;
|
||||
accentColor?: string;
|
||||
accentWidth?: number;
|
||||
};
|
||||
|
||||
export default function Card({
|
||||
style,
|
||||
wrapperStyle,
|
||||
onPress,
|
||||
disabled,
|
||||
accentColor,
|
||||
accentWidth = 4,
|
||||
children,
|
||||
...props
|
||||
}: Props) {
|
||||
const hasAccent = !!accentColor;
|
||||
|
||||
const contentPad: ViewStyle | undefined = hasAccent
|
||||
? { paddingLeft: Theme.Spacing.lg + accentWidth + Theme.Spacing.sm }
|
||||
: undefined;
|
||||
|
||||
const Inner = (pressed?: boolean) => (
|
||||
<View
|
||||
{...props}
|
||||
style={[styles.inner, contentPad, disabled && styles.disabled, style]}
|
||||
>
|
||||
{hasAccent && (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.accent,
|
||||
{ backgroundColor: accentColor, width: accentWidth },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{pressed && !disabled && (
|
||||
<View pointerEvents="none" style={styles.pressOverlay} />
|
||||
)}
|
||||
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
if (onPress) {
|
||||
return (
|
||||
<View style={[styles.shadowWrap, wrapperStyle]}>
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
style={({ pressed }) => [
|
||||
styles.pressableClip,
|
||||
pressed && !disabled && styles.pressed,
|
||||
]}
|
||||
android_ripple={{ color: "rgba(0,0,0,0.06)" }}
|
||||
>
|
||||
{({ pressed }) => Inner(pressed)}
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.shadowWrap, wrapperStyle]}>
|
||||
<View style={styles.pressableClip}>{Inner(false)}</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
shadowWrap: {
|
||||
borderRadius: Theme.Radius.lg,
|
||||
|
||||
shadowColor: "#0B1220",
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 18,
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
|
||||
elevation: 3,
|
||||
},
|
||||
|
||||
// 안쪽: 라운드/리플/오버레이 클립 담당
|
||||
pressableClip: {
|
||||
borderRadius: Theme.Radius.lg,
|
||||
overflow: "hidden",
|
||||
},
|
||||
|
||||
// 실제 카드 표면
|
||||
inner: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
padding: Theme.Spacing.lg,
|
||||
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(15, 23, 42, 0.10)", // 지금보다 살짝 진하게
|
||||
},
|
||||
|
||||
accent: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
|
||||
pressOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: "rgba(0,0,0,0.04)",
|
||||
},
|
||||
|
||||
pressed: {
|
||||
transform: [{ translateY: 1 }],
|
||||
},
|
||||
|
||||
disabled: {
|
||||
opacity: 0.55,
|
||||
},
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import React from "react";
|
||||
import HomeScreen from "../screens/HomeScreen";
|
||||
import AppTabs from "./AppTabs";
|
||||
|
||||
export type AppStackParamList = {
|
||||
Home: undefined;
|
||||
MainTabs: undefined;
|
||||
};
|
||||
|
||||
const Stack = createNativeStackNavigator<AppStackParamList>();
|
||||
@@ -11,7 +11,7 @@ const Stack = createNativeStackNavigator<AppStackParamList>();
|
||||
export default function AppStack() {
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name="Home" component={HomeScreen} />
|
||||
<Stack.Screen name="MainTabs" component={AppTabs} />
|
||||
</Stack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
30
src/navigation/AppTabs.tsx
Normal file
30
src/navigation/AppTabs.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||
import React from "react";
|
||||
import HomeScreen from "../screens/HomeScreen";
|
||||
import { Colors } from "../theme/colors";
|
||||
|
||||
export type AppTabParamList = {
|
||||
Library: undefined;
|
||||
Home: undefined;
|
||||
Settings: undefined;
|
||||
};
|
||||
|
||||
const Tab = createBottomTabNavigator<AppTabParamList>();
|
||||
|
||||
export default function AppTabs() {
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarStyle: {
|
||||
backgroundColor: Colors.surface,
|
||||
borderTopColor: Colors.border,
|
||||
},
|
||||
tabBarActiveTintColor: Colors.text,
|
||||
tabBarInactiveTintColor: Colors.mutedText,
|
||||
}}
|
||||
>
|
||||
<Tab.Screen name="Home" component={HomeScreen} />
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { Button, StyleSheet, View } from "react-native";
|
||||
import AppText from "../components/ui/AppText";
|
||||
import AudioBookCard from "../components/ui/AudioBookCard";
|
||||
import Card from "../components/ui/Card";
|
||||
import { useAuth } from "../store/auth";
|
||||
import { Colors } from "../theme/colors";
|
||||
|
||||
export default function HomeScreen() {
|
||||
const { signOut } = useAuth();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button
|
||||
title="Debug Storage"
|
||||
onPress={async () => {
|
||||
const keys = await AsyncStorage.getAllKeys();
|
||||
const entries = await AsyncStorage.multiGet(keys);
|
||||
console.log(entries);
|
||||
}}
|
||||
/>
|
||||
<Card>
|
||||
<AppText>feafwea</AppText>
|
||||
<AppText>feafwea</AppText>
|
||||
</Card>
|
||||
<AudioBookCard
|
||||
meta="ff"
|
||||
title="fefe"
|
||||
status="READY"
|
||||
progress={0.91}
|
||||
></AudioBookCard>
|
||||
<Button title="logout" onPress={() => signOut()}></Button>
|
||||
<AppText>fewfeaw</AppText>
|
||||
<AppText>fewfeaw</AppText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -22,8 +29,15 @@ export default function HomeScreen() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "red",
|
||||
backgroundColor: Colors.bg,
|
||||
alignContent: "center",
|
||||
justifyContent: "center",
|
||||
padding: 8,
|
||||
},
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
gap: 12,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
});
|
||||
|
||||
5
src/screens/LibraryScreen.tsx
Normal file
5
src/screens/LibraryScreen.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function LibraryScreen() {
|
||||
return <View>Library</View>;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
||||
import React, { useState } from "react";
|
||||
import { Pressable, StyleSheet, View } from "react-native";
|
||||
import { Platform, Pressable, StyleSheet, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
import AppText from "../components/ui/AppText";
|
||||
import Button from "../components/ui/Button";
|
||||
import Input from "../components/ui/Input";
|
||||
@@ -11,13 +13,26 @@ import { useAuth } from "../store/auth";
|
||||
import { Theme } from "../theme/theme";
|
||||
|
||||
export default function LoginScreen() {
|
||||
const { signIn } = useAuth();
|
||||
const { signIn /*, signInWithGoogle, signInWithApple */ } = useAuth();
|
||||
const [loginId, setLoginId] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const canSubmit = loginId.trim().length > 0 && password.length > 0;
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<AuthStackParamList>>();
|
||||
|
||||
const onGooglePress = () => {
|
||||
// TODO: 구현 후 연결
|
||||
// signInWithGoogle();
|
||||
console.log("Google login");
|
||||
};
|
||||
|
||||
const onApplePress = () => {
|
||||
// TODO: 구현 후 연결
|
||||
// signInWithApple();
|
||||
console.log("Apple login");
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.safe}>
|
||||
<View style={styles.container}>
|
||||
@@ -57,10 +72,53 @@ export default function LoginScreen() {
|
||||
<Button
|
||||
title="Continue"
|
||||
onPress={() => {
|
||||
signIn(loginId, password);
|
||||
signIn(loginId.trim(), password);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* divider */}
|
||||
<View style={{ height: Theme.Spacing.md }} />
|
||||
<View style={styles.dividerRow}>
|
||||
<View style={styles.dividerLine} />
|
||||
<AppText variant="muted" style={styles.dividerText}>
|
||||
or
|
||||
</AppText>
|
||||
<View style={styles.dividerLine} />
|
||||
</View>
|
||||
|
||||
{/* Google */}
|
||||
<Pressable
|
||||
onPress={onGooglePress}
|
||||
style={({ pressed }) => [
|
||||
styles.socialBtn,
|
||||
pressed && styles.socialBtnPressed,
|
||||
]}
|
||||
>
|
||||
<View style={styles.socialRow}>
|
||||
<AntDesign name="google" size={18} color={Theme.Colors.text} />
|
||||
<AppText style={styles.socialText}>Continue with Google</AppText>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
{/* Apple (iOS only) */}
|
||||
{Platform.OS === "ios" && (
|
||||
<Pressable
|
||||
onPress={onApplePress}
|
||||
style={({ pressed }) => [
|
||||
styles.socialBtn,
|
||||
styles.appleBtn,
|
||||
pressed && styles.appleBtnPressed,
|
||||
]}
|
||||
>
|
||||
<View style={styles.socialRow}>
|
||||
<Ionicons name="logo-apple" size={20} color="#fff" />
|
||||
<AppText style={[styles.socialText, styles.appleText]}>
|
||||
Continue with Apple
|
||||
</AppText>
|
||||
</View>
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
<View style={{ height: Theme.Spacing.md }} />
|
||||
|
||||
<Pressable
|
||||
@@ -89,6 +147,55 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "center",
|
||||
gap: Theme.Spacing.sm,
|
||||
},
|
||||
|
||||
dividerRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Theme.Spacing.sm,
|
||||
},
|
||||
dividerLine: {
|
||||
flex: 1,
|
||||
height: 1,
|
||||
backgroundColor: Theme.Colors.border,
|
||||
opacity: 0.7,
|
||||
},
|
||||
dividerText: {
|
||||
textAlign: "center",
|
||||
},
|
||||
|
||||
socialBtn: {
|
||||
borderWidth: 1,
|
||||
borderColor: Theme.Colors.border,
|
||||
borderRadius: Theme.Radius.lg,
|
||||
paddingVertical: Theme.Spacing.md,
|
||||
paddingHorizontal: Theme.Spacing.lg,
|
||||
backgroundColor: Theme.Colors.card,
|
||||
},
|
||||
socialBtnPressed: {
|
||||
opacity: 0.9,
|
||||
transform: [{ scale: 0.99 }],
|
||||
},
|
||||
socialRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: Theme.Spacing.sm,
|
||||
},
|
||||
socialText: {
|
||||
fontWeight: Theme.FontWeight.semibold,
|
||||
},
|
||||
|
||||
appleBtn: {
|
||||
backgroundColor: "#000",
|
||||
borderColor: "#000",
|
||||
},
|
||||
appleBtnPressed: {
|
||||
opacity: 0.92,
|
||||
},
|
||||
appleText: {
|
||||
color: "#fff",
|
||||
},
|
||||
|
||||
signupLine: {
|
||||
textAlign: "center",
|
||||
},
|
||||
|
||||
@@ -1,22 +1,68 @@
|
||||
// export const Colors = {
|
||||
// // base
|
||||
// bg: "#0B1220", // 깊은 네이비
|
||||
// surface: "#111B2E", // 기본 표면
|
||||
// card: "#162442", // 카드/리스트 아이템
|
||||
|
||||
// // text
|
||||
// text: "#EAF0FF",
|
||||
// mutedText: "#A8B3CF",
|
||||
// placeholder: "#6F7AA3",
|
||||
|
||||
// // brand
|
||||
// primary: "#2DD4BF", // 청록 포인트 (오디오앱에 잘 맞음)
|
||||
// primaryPressed: "#14B8A6",
|
||||
|
||||
// // status
|
||||
// danger: "#F87171",
|
||||
|
||||
// // lines
|
||||
// border: "#223055",
|
||||
// inputBg: "#0E1730",
|
||||
// } as const;
|
||||
|
||||
// export const Colors = {
|
||||
// // base
|
||||
// bg: "#0B0B14",
|
||||
// surface: "#121225",
|
||||
// card: "#1A1B33",
|
||||
|
||||
// // text
|
||||
// text: "#F1F2FF",
|
||||
// mutedText: "#B0B3D6",
|
||||
// placeholder: "#7A7FA8",
|
||||
|
||||
// // brand
|
||||
// primary: "#8B5CF6",
|
||||
// primaryPressed: "#7C3AED",
|
||||
|
||||
// // status
|
||||
// danger: "#FB7185",
|
||||
|
||||
// // lines
|
||||
// border: "#2A2D52",
|
||||
// inputBg: "#0F1022",
|
||||
// } as const;
|
||||
|
||||
export const Colors = {
|
||||
// base
|
||||
bg: "#0B1220",
|
||||
surface: "#111827",
|
||||
card: "#0F172A",
|
||||
bg: "#F7F4EE",
|
||||
surface: "#FFFFFF",
|
||||
card: "#FFFFFF",
|
||||
|
||||
// text
|
||||
text: "#FFFFFF",
|
||||
mutedText: "#9CA3AF",
|
||||
placeholder: "#6B7280",
|
||||
text: "#1F2937",
|
||||
mutedText: "#6B7280",
|
||||
placeholder: "#9CA3AF",
|
||||
|
||||
// brand
|
||||
primary: "#4F46E5",
|
||||
primaryPressed: "#4338CA",
|
||||
primary: "#0EA5A4", // 청록 포인트
|
||||
primaryPressed: "#0B8B8A",
|
||||
|
||||
// status
|
||||
danger: "#EF4444",
|
||||
danger: "#DC2626",
|
||||
|
||||
// lines
|
||||
border: "#22304A",
|
||||
inputBg: "#0B1220",
|
||||
border: "#E6E0D6",
|
||||
inputBg: "#F1EEE7",
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user