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
19 changes: 0 additions & 19 deletions apps/native/src/components/common/Container.tsx

This file was deleted.

65 changes: 65 additions & 0 deletions apps/native/src/components/common/ContentInset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useState, type ReactNode } from 'react';
import { View, type LayoutChangeEvent, type ViewProps, useWindowDimensions } from 'react-native';

interface ContentInsetProps extends ViewProps {
className?: string;
children: ReactNode;
}

const MIN_HORIZONTAL_PADDING = 16;
const MAX_HORIZONTAL_PADDING = 128;
const START_ANCHOR_WIDTH = 768;
const START_ANCHOR_PADDING = 60;
const END_ANCHOR_WIDTH = 1024;
const END_ANCHOR_PADDING = 128;

const clampValue = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));

const interpolateValue = (
value: number,
inputMin: number,
inputMax: number,
outputMin: number,
outputMax: number
) => {
const ratio = (value - inputMin) / (inputMax - inputMin);
return outputMin + (outputMax - outputMin) * ratio;
};

const getResponsiveHorizontalPadding = (width: number) => {
const interpolatedPadding = interpolateValue(
width,
START_ANCHOR_WIDTH,
END_ANCHOR_WIDTH,
START_ANCHOR_PADDING,
END_ANCHOR_PADDING
);

return clampValue(interpolatedPadding, MIN_HORIZONTAL_PADDING, MAX_HORIZONTAL_PADDING);
};

const ContentInset = ({ className, children, style, onLayout, ...props }: ContentInsetProps) => {
const { width: windowWidth } = useWindowDimensions();
const [layoutWidth, setLayoutWidth] = useState<number | null>(null);
const targetWidth = layoutWidth ?? windowWidth;
const paddingHorizontal = getResponsiveHorizontalPadding(targetWidth);

const handleLayout = (event: LayoutChangeEvent) => {
const nextWidth = Math.round(event.nativeEvent.layout.width);

setLayoutWidth((prevWidth) => (prevWidth === nextWidth ? prevWidth : nextWidth));
onLayout?.(event);
};

return (
<View
className={`w-full ${className ?? ''}`}
style={[{ paddingHorizontal }, style]}
onLayout={handleLayout}
{...props}>
{children}
</View>
);
};

export default ContentInset;
4 changes: 2 additions & 2 deletions apps/native/src/components/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import AnimatedPressable from './AnimatedPressable';
import Container from './Container';
import ContentInset from './ContentInset';
import LoadingScreen from './LoadingScreen';
import NotificationItem from './NotificationItem';
import TextButton from './TextButton';
Expand All @@ -8,7 +8,7 @@ import { ImageWithSkeleton } from './ImageWithSkeleton';

export {
AnimatedPressable,
Container,
ContentInset,
LoadingScreen,
NotificationItem,
TextButton,
Expand Down
6 changes: 3 additions & 3 deletions apps/native/src/features/auth/login/screens/LoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MailIcon } from 'lucide-react-native';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';

import { AnimatedPressable, Container } from '@components/common';
import { AnimatedPressable, ContentInset } from '@components/common';
import { GoogleIcon, KakaoIcon, PointerLogo, AppleIcon } from '@components/system/icons';
import { colors } from '@theme/tokens';
import type { AuthStackParamList } from '@navigation/auth/AuthNavigator';
Expand All @@ -27,7 +27,7 @@ const LoginScreen = () => {

return (
<SafeAreaView className='flex-1' edges={['top', 'bottom']}>
<Container className='flex-1'>
<ContentInset className='flex-1'>
<View className='flex-1 items-center justify-center gap-[12px]'>
<PointerLogo />
<Text className='typo-body-1-regular text-gray-700'>
Expand Down Expand Up @@ -87,7 +87,7 @@ const LoginScreen = () => {
<Text className='typo-body-1-medium text-primary-600'>이메일로 시작하기</Text>
</AnimatedPressable>
</View>
</Container>
</ContentInset>
</SafeAreaView>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Text, View } from 'react-native';
import Svg, { Path } from 'react-native-svg';

import { Container } from '@components/common';
import { ContentInset } from '@components/common';
import { TeacherIcon } from '@components/system/icons';

interface TextBubbleProps {
Expand Down Expand Up @@ -37,7 +37,7 @@ interface LearningStatusProps {

const LearningStatus = ({ studentName, date, content }: LearningStatusProps) => {
return (
<Container className='gap-[16px] pt-[26px] pb-[32px]'>
<ContentInset className='gap-[16px] pt-[26px] pb-[32px]'>
<Text className='text-20b text-gray-900'>{`${studentName}님의 학습 상태`}</Text>
<View className='flex-row items-start gap-[18px] px-[10px]'>
{/* Learning Status Icon */}
Expand All @@ -50,7 +50,7 @@ const LearningStatus = ({ studentName, date, content }: LearningStatusProps) =>

<TextBubble date={date} content={content} />
</View>
</Container>
</ContentInset>
);
};

Expand Down
6 changes: 3 additions & 3 deletions apps/native/src/features/student/home/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useGetLastDiagnosis, useGetMonthlyPublish, useGetPublishDetail } from '
import { type StudentRootStackParamList } from '@navigation/student/types';
import { colors } from '@theme/tokens';
import { PointerSymbol } from '@components/system/icons';
import { AnimatedPressable, Container } from '@components/common';
import { AnimatedPressable, ContentInset } from '@components/common';
import { useInvalidateAll } from '@hooks';
import { formatDateKey } from '@utils/date';

Expand Down Expand Up @@ -61,7 +61,7 @@ const HomeScreen = () => {
<ScrollView
contentContainerStyle={{ paddingBottom: 80 }}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
<Container className='flex-col gap-[12px] pt-[20px] md:flex-row'>
<ContentInset className='flex-col gap-[12px] pt-[20px] md:flex-row'>
<View className='flex-1 flex-col'>
<View className='mb-[10px] w-full flex-row items-center gap-[8px] p-[8px]'>
<View className='bg-primary-500 size-[42px] items-center justify-center rounded-full'>
Expand Down Expand Up @@ -125,7 +125,7 @@ const HomeScreen = () => {
onDateChange={handleDateChange}
/>
</View>
</Container>
</ContentInset>

<CalendarModal
visible={isCalendarModalVisible}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { View, Text, ScrollView } from 'react-native';
import { type NativeStackScreenProps } from '@react-navigation/native-stack';

import ProblemViewer from '@features/student/problem/components/ProblemViewer';
import { Container } from '@components/common';
import { ContentInset } from '@components/common';
import { type StudentRootStackParamList } from '@navigation/student/types';

type Props = NativeStackScreenProps<StudentRootStackParamList, 'NotificationDetail'>;
Expand All @@ -18,13 +18,13 @@ const NotificationDetailScreen = ({ route }: Props) => {
return (
<ScrollView contentContainerStyle={{ paddingBottom: 80 }}>
<View className='mx-auto w-full'>
<Container className='gap-[32px] pt-[16px]'>
<ContentInset className='gap-[32px] pt-[16px]'>
<View className='flex-col gap-0.5'>
<Text className='text-20b text-gray-900'>{title}</Text>
<Text className='text-12m text-gray-700'>{formatDate(date)}</Text>
</View>
<ProblemViewer problemContent={content} minHeight={200} />
</Container>
</ContentInset>
</View>
</ScrollView>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { View, Text, ScrollView, Alert } from 'react-native';
import { useQueryClient } from '@tanstack/react-query';
import { ChevronRight } from 'lucide-react-native';

import { AnimatedPressable, Container, NotificationItem } from '@components/common';
import { AnimatedPressable, ContentInset, NotificationItem } from '@components/common';
import { NoNotificationBellIcon } from '@components/system/icons';
import { type StudentRootStackParamList } from '@navigation/student/types';
import {
Expand Down Expand Up @@ -146,7 +146,7 @@ const NotificationScreen = () => {
return (
<ScrollView contentContainerStyle={{ paddingBottom: 80 }}>
<View className='mx-auto w-full'>
<Container className='gap-[10px] pt-[16px]'>
<ContentInset className='gap-[10px] pt-[16px]'>
<Text className='text-20b text-gray-900'>공지</Text>
{notices.map((notice) => (
<NotificationItem
Expand Down Expand Up @@ -179,8 +179,8 @@ const NotificationScreen = () => {
<Text className='text-14m text-gray-600'>공지사항이 없어요.</Text>
</View>
)}
</Container>
<Container className='gap-[10px] pt-[26px]'>
</ContentInset>
<ContentInset className='gap-[10px] pt-[26px]'>
<View className='flex-row items-center justify-between'>
<Text className='text-20b text-gray-900'>알림</Text>
<AnimatedPressable
Expand Down Expand Up @@ -222,11 +222,11 @@ const NotificationScreen = () => {
<Text className='text-20b text-gray-800'>받은 알림이 없어요.</Text>
</View>
)}
</Container>
</ContentInset>
</View>
<Container className='flex-1 items-center justify-center gap-[10px] pt-[20px] pb-[100px]'>
<ContentInset className='flex-1 items-center justify-center gap-[10px] pt-[20px] pb-[100px]'>
<Text className='text-14m text-gray-600'>7일 전 알림까지 확인할 수 있어요.</Text>
</Container>
</ContentInset>
</ScrollView>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useNavigation } from '@react-navigation/native';
import { ChevronLeftIcon } from 'lucide-react-native';

import { colors } from '@theme/tokens';
import { AnimatedPressable, Container } from '@components/common';
import { AnimatedPressable, ContentInset } from '@components/common';

type Props = {
title?: string;
Expand Down Expand Up @@ -91,7 +91,7 @@ export const EditScreenLayout = ({
<View className='h-[20px]' />
)}
</View>
<Container className='flex-1 pt-[6px]'>
<ContentInset className='flex-1 pt-[6px]'>
{isScrollable ? (
<ScrollView
className='flex-1 overflow-visible'
Expand Down Expand Up @@ -127,7 +127,7 @@ export const EditScreenLayout = ({
containerStyle={{ marginBottom: inset.bottom + 18 }}>
<Text className='text-18sb text-center text-white'>{ctaLabel}</Text>
</AnimatedPressable>
</Container>
</ContentInset>
</KeyboardAvoidingView>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { View, Text, TextInput, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

import { AnimatedPressable, Container } from '@components/common';
import { AnimatedPressable, ContentInset } from '@components/common';
import { colors } from '@theme/tokens';
import { usePostFeedback } from '@apis';
import { showToast } from '@features/student/scrap/components/Notification';
Expand Down Expand Up @@ -43,7 +43,7 @@ const FeedbackScreen = () => {
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={0}>
<ScreenLayout title='피드백 보내기'>
<Container className='flex-1 pt-[10px]'>
<ContentInset className='flex-1 pt-[10px]'>
<ScrollView
className='flex-1'
showsVerticalScrollIndicator={false}
Expand Down Expand Up @@ -80,7 +80,7 @@ const FeedbackScreen = () => {
<Text className='text-16m text-white'>보내기</Text>
</AnimatedPressable>
</SafeAreaView>
</Container>
</ContentInset>
</ScreenLayout>
</KeyboardAvoidingView>
);
Expand Down
10 changes: 5 additions & 5 deletions apps/native/src/features/student/menu/screens/MenuScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Bell, Headset, Megaphone, ThumbsUp, History } from 'lucide-react-native

import { TanstackQueryClient, useGetMe, useGetNoticeCount } from '@apis';
import { useAuthStore } from '@stores';
import { Container } from '@components/common';
import { ContentInset } from '@components/common';
import { type MenuStackParamList } from '@navigation/student/MenuNavigator';

import {
Expand Down Expand Up @@ -52,10 +52,10 @@ const MenuScreen = () => {

return (
<View className='w-full flex-1'>
<Container className='h-[52px] justify-center bg-gray-100 py-[2px]'>
<ContentInset className='h-[52px] justify-center bg-gray-100 py-[2px]'>
<Text className='text-20b text-black'>전체 메뉴</Text>
</Container>
<Container className='flex-1'>
</ContentInset>
<ContentInset className='flex-1'>
<ScrollView showsVerticalScrollIndicator={false} contentContainerClassName='pb-[40px]'>
<View className='h-[20px]' />
<View className='gap-[10px]'>
Expand Down Expand Up @@ -118,7 +118,7 @@ const MenuScreen = () => {
</MenuSection>
</View>
</ScrollView>
</Container>
</ContentInset>
<ConfirmationModal
visible={isLogoutVisible}
onClose={() => setIsLogoutVisible(false)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { View, Text, FlatList } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { type NativeStackNavigationProp } from '@react-navigation/native-stack';

import { Container, NotificationItem } from '@components/common';
import { ContentInset, NotificationItem } from '@components/common';
import { putReadNotice, useGetNotice, useInvalidateNoticeData } from '@apis';
import { type StudentRootStackParamList } from '@/navigation/student/types';

Expand Down Expand Up @@ -97,7 +97,7 @@ const NoticeScreen = () => {
<Text className='text-14m text-gray-600'>공지사항이 없어요.</Text>
</View>
) : (
<Container>
<ContentInset>
<FlatList
data={allNotices}
renderItem={renderItem}
Expand All @@ -106,7 +106,7 @@ const NoticeScreen = () => {
onEndReachedThreshold={0.3}
contentContainerStyle={NOTICE_LIST_CONTENT_STYLE}
/>
</Container>
</ContentInset>
)}
</ScreenLayout>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef, useCallback } from 'react';
import { View, ScrollView, Linking, AppState } from 'react-native';
import messaging from '@react-native-firebase/messaging';

import { Container } from '@components/common';
import { ContentInset } from '@components/common';
import { usePutAllowPush, useGetPushSetting } from '@apis';
import { showToast } from '@features/student/scrap/components/Notification';

Expand Down Expand Up @@ -211,7 +211,7 @@ const NotificationSettingsScreen = () => {
return (
<ScreenLayout title='알림 설정'>
<ScrollView className='flex-1 pt-[10px]' showsVerticalScrollIndicator={false}>
<Container className='gap-[16px]'>
<ContentInset className='gap-[16px]'>
<SettingsToggleItem
title='푸시 알림'
value={pushEnabled}
Expand Down Expand Up @@ -243,7 +243,7 @@ const NotificationSettingsScreen = () => {
onValueChange={handleEventNotificationChange}
disabled={!pushEnabled}
/>
</Container>
</ContentInset>
</ScrollView>
</ScreenLayout>
);
Expand Down
Loading
Loading