diff --git a/apps/mobile/src/components/common/AnimatedComponents.tsx b/apps/mobile/src/components/common/AnimatedComponents.tsx index 2224c8f2f5..2bbd2ef4e6 100644 --- a/apps/mobile/src/components/common/AnimatedComponents.tsx +++ b/apps/mobile/src/components/common/AnimatedComponents.tsx @@ -6,3 +6,4 @@ export const AnimatedFlatList = Animated.createAnimatedComponent(FlatList) export const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity) export const ReAnimatedPressable = Reanimated.createAnimatedComponent(Pressable) +export const ReAnimatedScrollView = Reanimated.createAnimatedComponent(ScrollView) diff --git a/apps/mobile/src/components/ui/grouped/GroupedList.tsx b/apps/mobile/src/components/ui/grouped/GroupedList.tsx index 0e2e3aa3b6..5f566ce9fd 100644 --- a/apps/mobile/src/components/ui/grouped/GroupedList.tsx +++ b/apps/mobile/src/components/ui/grouped/GroupedList.tsx @@ -1,7 +1,9 @@ import { cn } from "@follow/utils" import type { FC, PropsWithChildren } from "react" +import * as React from "react" +import { Fragment } from "react" import type { ViewProps } from "react-native" -import { Pressable, Text, View } from "react-native" +import { Pressable, StyleSheet, Text, View } from "react-native" import { RightCuteReIcon } from "@/src/icons/right_cute_re" import { useColor } from "@/src/theme/colors" @@ -9,7 +11,20 @@ import { useColor } from "@/src/theme/colors" export const GroupedInsetListCard: FC = ({ children }) => { return ( - {children} + {React.Children.map(children, (child, index) => { + const isLast = index === React.Children.count(children) - 1 + return ( + + {child} + {!isLast && ( + + )} + + ) + })} ) } @@ -48,10 +63,10 @@ export const GroupedInsetListNavigationLink: FC<{ {icon} - {label} + {label} - + diff --git a/apps/mobile/src/icons/bell_ringing_cute_fi.tsx b/apps/mobile/src/icons/bell_ringing_cute_fi.tsx new file mode 100644 index 0000000000..d92a200e16 --- /dev/null +++ b/apps/mobile/src/icons/bell_ringing_cute_fi.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface BellRingingCuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const BellRingingCuteFiIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: BellRingingCuteFiIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/icons/database.tsx b/apps/mobile/src/icons/database.tsx new file mode 100644 index 0000000000..74a0851b6f --- /dev/null +++ b/apps/mobile/src/icons/database.tsx @@ -0,0 +1,19 @@ +import type { SvgProps } from "react-native-svg" +import Svg, { Path } from "react-native-svg" + +export function DatabaseIcon( + props: SvgProps & { + width?: number + height?: number + color?: string + }, +) { + return ( + + + + ) +} diff --git a/apps/mobile/src/icons/information_cute_re copy.tsx b/apps/mobile/src/icons/information_cute_re copy.tsx deleted file mode 100644 index ab3ae38cb6..0000000000 --- a/apps/mobile/src/icons/information_cute_re copy.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import Svg, { Path } from "react-native-svg" - -interface InformationCuteReCopyIconProps { - width?: number - height?: number - color?: string -} - -export const InformationCuteReCopyIcon = ({ - width = 24, - height = 24, - color = "#10161F", -}: InformationCuteReCopyIconProps) => { - return ( - - - - - ) -} diff --git a/apps/mobile/src/icons/time_cute_re copy.tsx b/apps/mobile/src/icons/magic_2_cute_fi.tsx similarity index 62% rename from apps/mobile/src/icons/time_cute_re copy.tsx rename to apps/mobile/src/icons/magic_2_cute_fi.tsx index 597dbf373a..1ce4d02e07 100644 --- a/apps/mobile/src/icons/time_cute_re copy.tsx +++ b/apps/mobile/src/icons/magic_2_cute_fi.tsx @@ -1,17 +1,17 @@ import * as React from "react" import Svg, { Path } from "react-native-svg" -interface TimeCuteReCopyIconProps { +interface Magic2CuteFiIconProps { width?: number height?: number color?: string } -export const TimeCuteReCopyIcon = ({ +export const Magic2CuteFiIcon = ({ width = 24, height = 24, color = "#10161F", -}: TimeCuteReCopyIconProps) => { +}: Magic2CuteFiIconProps) => { return ( @@ -19,8 +19,8 @@ export const TimeCuteReCopyIcon = ({ stroke={color} strokeLinecap="round" strokeLinejoin="round" - strokeWidth={2} - d="M12 7v3.757a3 3 0 0 0 .879 2.122L15 15m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0" + strokeWidth={3} + d="m6.044 6.05 1.413 1.413M12 12l8.186 8.192M15.944 6.05l-1.411 1.412m-7.071 7.07L6.044 15.95M17.994 11h-2.05m-9.9 0h-2.05m7 7v-2.05m0-9.9V4" /> ) diff --git a/apps/mobile/src/icons/palette_cute_fi.tsx b/apps/mobile/src/icons/palette_cute_fi.tsx new file mode 100644 index 0000000000..1e862888c7 --- /dev/null +++ b/apps/mobile/src/icons/palette_cute_fi.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface PaletteCuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const PaletteCuteFiIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: PaletteCuteFiIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/icons/power_outline.tsx b/apps/mobile/src/icons/power_outline.tsx index 7644208476..96ceb8058b 100644 --- a/apps/mobile/src/icons/power_outline.tsx +++ b/apps/mobile/src/icons/power_outline.tsx @@ -20,7 +20,7 @@ export const PowerOutlineIcon = ({ /> diff --git a/apps/mobile/src/icons/round_cute_fi.tsx b/apps/mobile/src/icons/round_cute_fi.tsx new file mode 100644 index 0000000000..ae683c5706 --- /dev/null +++ b/apps/mobile/src/icons/round_cute_fi.tsx @@ -0,0 +1,16 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface RoundCuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const RoundCuteFiIcon = ({ width = 24, height = 24 }: RoundCuteFiIconProps) => { + return ( + + + + ) +} diff --git a/apps/mobile/src/icons/round_cute_re.tsx b/apps/mobile/src/icons/round_cute_re.tsx new file mode 100644 index 0000000000..f8ff0bfe08 --- /dev/null +++ b/apps/mobile/src/icons/round_cute_re.tsx @@ -0,0 +1,16 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface RoundCuteReIconProps { + width?: number + height?: number + color?: string +} + +export const RoundCuteReIcon = ({ width = 24, height = 24 }: RoundCuteReIconProps) => { + return ( + + + + ) +} diff --git a/apps/mobile/src/icons/safe_lock_filled.tsx b/apps/mobile/src/icons/safe_lock_filled.tsx new file mode 100644 index 0000000000..5274f37c45 --- /dev/null +++ b/apps/mobile/src/icons/safe_lock_filled.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface SafeLockFilledIconProps { + width?: number + height?: number + color?: string +} + +export const SafeLockFilledIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: SafeLockFilledIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/icons/settings_7_cute_fi.tsx b/apps/mobile/src/icons/settings_7_cute_fi.tsx new file mode 100644 index 0000000000..a112268ded --- /dev/null +++ b/apps/mobile/src/icons/settings_7_cute_fi.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface Settings7CuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const Settings7CuteFiIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: Settings7CuteFiIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/icons/trophy_cute_fi.tsx b/apps/mobile/src/icons/trophy_cute_fi.tsx new file mode 100644 index 0000000000..0fe2d105a2 --- /dev/null +++ b/apps/mobile/src/icons/trophy_cute_fi.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface TrophyCuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const TrophyCuteFiIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: TrophyCuteFiIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/icons/user_3_cute_fi.tsx b/apps/mobile/src/icons/user_3_cute_fi.tsx new file mode 100644 index 0000000000..f7dd780744 --- /dev/null +++ b/apps/mobile/src/icons/user_3_cute_fi.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import Svg, { Path } from "react-native-svg" + +interface User3CuteFiIconProps { + width?: number + height?: number + color?: string +} + +export const User3CuteFiIcon = ({ + width = 24, + height = 24, + color = "#10161F", +}: User3CuteFiIconProps) => { + return ( + + + + + ) +} diff --git a/apps/mobile/src/modules/settings/SettingNavigationLink.tsx b/apps/mobile/src/modules/settings/SettingNavigationLink.tsx new file mode 100644 index 0000000000..373e08114f --- /dev/null +++ b/apps/mobile/src/modules/settings/SettingNavigationLink.tsx @@ -0,0 +1,19 @@ +import type { FC, PropsWithChildren } from "react" +import { View } from "react-native" + +export const SettingNavigationLinkIcon: FC< + { + backgroundColor: string + } & PropsWithChildren +> = ({ backgroundColor, children }) => { + return ( + + {children} + + ) +} diff --git a/apps/mobile/src/modules/settings/SettingsList.tsx b/apps/mobile/src/modules/settings/SettingsList.tsx index b8fcc4e27a..2b22ee06c6 100644 --- a/apps/mobile/src/modules/settings/SettingsList.tsx +++ b/apps/mobile/src/modules/settings/SettingsList.tsx @@ -1,14 +1,134 @@ +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs" import { useIsFocused } from "@react-navigation/native" import { useContext, useEffect } from "react" import { View } from "react-native" +import { useSafeAreaInsets } from "react-native-safe-area-context" import { GroupedInsetListCard, GroupedInsetListNavigationLink, } from "@/src/components/ui/grouped/GroupedList" import { SetBottomTabBarVisibleContext } from "@/src/contexts/BottomTabBarVisibleContext" +import { BellRingingCuteFiIcon } from "@/src/icons/bell_ringing_cute_fi" +import { CertificateCuteFiIcon } from "@/src/icons/certificate_cute_fi" +import { DatabaseIcon } from "@/src/icons/database" +import { Magic2CuteFiIcon } from "@/src/icons/magic_2_cute_fi" +import { PaletteCuteFiIcon } from "@/src/icons/palette_cute_fi" +import { RadaCuteFiIcon } from "@/src/icons/rada_cute_fi" +import { SafeLockFilledIcon } from "@/src/icons/safe_lock_filled" +import { Settings7CuteFiIcon } from "@/src/icons/settings_7_cute_fi" +import { StarCuteFiIcon } from "@/src/icons/star_cute_fi" +import { TrophyCuteFiIcon } from "@/src/icons/trophy_cute_fi" +import { User3CuteFiIcon } from "@/src/icons/user_3_cute_fi" import { useSettingsNavigation } from "./hooks" +import { SettingNavigationLinkIcon } from "./SettingNavigationLink" + +interface GroupNavigationLink { + label: string + icon: React.ElementType + onPress: (navigation: ReturnType) => void + iconBackgroundColor: string +} +const UserGroupNavigationLinks: GroupNavigationLink[] = [ + { + label: "Profile", + icon: User3CuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Profile") + }, + iconBackgroundColor: "#4F46E5", + }, + { + label: "Achievement", + icon: TrophyCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Achievement") + }, + iconBackgroundColor: "#6366F1", + }, +] + +const SettingGroupNavigationLinks: GroupNavigationLink[] = [ + { + label: "General", + icon: Settings7CuteFiIcon, + onPress: (navigation) => { + navigation.navigate("General") + }, + iconBackgroundColor: "#F59E0B", + }, + { + label: "Notifications", + icon: BellRingingCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Notifications") + }, + iconBackgroundColor: "#FBBF24", + }, + { + label: "Appearance", + icon: PaletteCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Appearance") + }, + iconBackgroundColor: "#FCD34D", + }, + { + label: "Data", + icon: DatabaseIcon, + onPress: (navigation) => { + navigation.navigate("Data") + }, + iconBackgroundColor: "#F59E0B", + }, +] + +const DataGroupNavigationLinks: GroupNavigationLink[] = [ + { + label: "Actions", + icon: Magic2CuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Actions") + }, + iconBackgroundColor: "#059669", + }, + { + label: "Lists", + icon: RadaCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Lists") + }, + iconBackgroundColor: "#10B981", + }, + { + label: "Feeds", + icon: CertificateCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("Feeds") + }, + iconBackgroundColor: "#34D399", + }, +] + +const PrivacyGroupNavigationLinks: GroupNavigationLink[] = [ + { + label: "Privacy", + icon: SafeLockFilledIcon, + onPress: (navigation) => { + navigation.navigate("Privacy") + }, + iconBackgroundColor: "#EF4444", + }, + { + label: "About", + icon: StarCuteFiIcon, + onPress: (navigation) => { + navigation.navigate("About") + }, + iconBackgroundColor: "#F87181", + }, +] export const SettingsList = () => { const navigation = useSettingsNavigation() @@ -20,15 +140,75 @@ export const SettingsList = () => { setTabBarVisible(true) } }, [isVisible, setTabBarVisible]) + + const insets = useSafeAreaInsets() + const tabBarHeight = useBottomTabBarHeight() return ( - + + + {UserGroupNavigationLinks.map((link) => ( + + + + } + onPress={() => link.onPress(navigation)} + /> + ))} + + + + + {DataGroupNavigationLinks.map((link) => ( + + + + } + onPress={() => link.onPress(navigation)} + /> + ))} + + + + + {SettingGroupNavigationLinks.map((link) => ( + + + + } + onPress={() => link.onPress(navigation)} + /> + ))} + + + - { - navigation.navigate("Account") - }} - /> + {PrivacyGroupNavigationLinks.map((link) => ( + + + + } + onPress={() => link.onPress(navigation)} + /> + ))} ) diff --git a/apps/mobile/src/modules/settings/UserHeaderBanner.tsx b/apps/mobile/src/modules/settings/UserHeaderBanner.tsx index c4f2dd5c1e..dbd81365b3 100644 --- a/apps/mobile/src/modules/settings/UserHeaderBanner.tsx +++ b/apps/mobile/src/modules/settings/UserHeaderBanner.tsx @@ -1,8 +1,10 @@ import { cn, getLuminance } from "@follow/utils" import { LinearGradient } from "expo-linear-gradient" import { useEffect, useState } from "react" -import { Animated, Image, StyleSheet, Text, View } from "react-native" +import { Image, StyleSheet, Text, View } from "react-native" import ImageColors from "react-native-image-colors" +import type { SharedValue } from "react-native-reanimated" +import ReAnimated, { interpolate, useAnimatedStyle } from "react-native-reanimated" import { useSafeAreaInsets } from "react-native-safe-area-context" import { useWhoami } from "@/src/store/user/hooks" @@ -10,7 +12,7 @@ import { accentColor } from "@/src/theme/colors" const defaultGradientColors = ["#FF5C00", "#FF8533", "#FFA666"] -export const UserHeaderBanner = ({ scrollY }: { scrollY: Animated.Value }) => { +export const UserHeaderBanner = ({ scrollY }: { scrollY: SharedValue }) => { const whoami = useWhoami() const insets = useSafeAreaInsets() @@ -57,6 +59,22 @@ export const UserHeaderBanner = ({ scrollY }: { scrollY: Animated.Value }) => { extractColors() }, [whoami?.image]) + const styles = useAnimatedStyle(() => { + const translateYValue = interpolate(scrollY.value, [-MAX_PULL, 0], [TRANSLATE_Y, 0], { + extrapolateLeft: "extend", + extrapolateRight: "clamp", + }) + + const scaleValue = interpolate(scrollY.value, [-MAX_PULL, 0], [SCALE_FACTOR, 1], { + extrapolateLeft: "extend", + extrapolateRight: "clamp", + }) + + return { + backgroundColor: gradientColors[0], + transform: [{ translateY: translateYValue }, { scale: scaleValue }], + } + }) if (!whoami) return null return ( @@ -64,36 +82,14 @@ export const UserHeaderBanner = ({ scrollY }: { scrollY: Animated.Value }) => { className="relative h-[200px] items-center justify-center" style={{ marginTop: -insets.top }} > - + - + { - const setTabBarVisible = useContext(SetBottomTabBarVisibleContext) - const hookValue = useNavigation>() - return { - ...hookValue, - navigate: ( - name: keyof RootStackParamList, - params?: RootStackParamList[keyof RootStackParamList], - ) => { - setTabBarVisible(false) - hookValue.navigate(name, params) - }, - } + return useNavigation>() } diff --git a/apps/mobile/src/modules/settings/routes/About.tsx b/apps/mobile/src/modules/settings/routes/About.tsx new file mode 100644 index 0000000000..a5bf7f84f1 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/About.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const AboutScreen = () => { + return ( + + About + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Achievement.tsx b/apps/mobile/src/modules/settings/routes/Achievement.tsx new file mode 100644 index 0000000000..199e3aea61 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Achievement.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const AchievementScreen = () => { + return ( + + Achievement Screen + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Actions.tsx b/apps/mobile/src/modules/settings/routes/Actions.tsx new file mode 100644 index 0000000000..7f161086d4 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Actions.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const ActionsScreen = () => { + return ( + + Actions Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Appearance.tsx b/apps/mobile/src/modules/settings/routes/Appearance.tsx new file mode 100644 index 0000000000..6b6dfc7484 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Appearance.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const AppearanceScreen = () => { + return ( + + Appearance Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Data.tsx b/apps/mobile/src/modules/settings/routes/Data.tsx new file mode 100644 index 0000000000..7bbd240de5 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Data.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const DataScreen = () => { + return ( + + Data Management + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Feeds.tsx b/apps/mobile/src/modules/settings/routes/Feeds.tsx new file mode 100644 index 0000000000..5c928aa923 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Feeds.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const FeedsScreen = () => { + return ( + + Feeds Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/General.tsx b/apps/mobile/src/modules/settings/routes/General.tsx new file mode 100644 index 0000000000..5332b07f30 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/General.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const GeneralScreen = () => { + return ( + + General Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Lists.tsx b/apps/mobile/src/modules/settings/routes/Lists.tsx new file mode 100644 index 0000000000..537f2ec7b6 --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Lists.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const ListsScreen = () => { + return ( + + Lists Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Notifications.tsx b/apps/mobile/src/modules/settings/routes/Notifications.tsx new file mode 100644 index 0000000000..e29470555f --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Notifications.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const NotificationsScreen = () => { + return ( + + Notifications Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Privacy.tsx b/apps/mobile/src/modules/settings/routes/Privacy.tsx new file mode 100644 index 0000000000..047e8b2b0d --- /dev/null +++ b/apps/mobile/src/modules/settings/routes/Privacy.tsx @@ -0,0 +1,9 @@ +import { Text, View } from "react-native" + +export const PrivacyScreen = () => { + return ( + + Privacy Settings + + ) +} diff --git a/apps/mobile/src/modules/settings/routes/Account.tsx b/apps/mobile/src/modules/settings/routes/Profile.tsx similarity index 74% rename from apps/mobile/src/modules/settings/routes/Account.tsx rename to apps/mobile/src/modules/settings/routes/Profile.tsx index c6e706ab06..f64faa7161 100644 --- a/apps/mobile/src/modules/settings/routes/Account.tsx +++ b/apps/mobile/src/modules/settings/routes/Profile.tsx @@ -1,6 +1,6 @@ import { Text, View } from "react-native" -export const AccountScreen = () => { +export const ProfileScreen = () => { return ( Account diff --git a/apps/mobile/src/modules/settings/routes/index.tsx b/apps/mobile/src/modules/settings/routes/index.tsx index 4d4aad7315..2634466eab 100644 --- a/apps/mobile/src/modules/settings/routes/index.tsx +++ b/apps/mobile/src/modules/settings/routes/index.tsx @@ -1,7 +1,29 @@ import type { createNativeStackNavigator } from "@react-navigation/native-stack" -import { AccountScreen } from "./Account" +import { AboutScreen } from "./About" +import { AchievementScreen } from "./Achievement" +import { ActionsScreen } from "./Actions" +import { AppearanceScreen } from "./Appearance" +import { DataScreen } from "./Data" +import { FeedsScreen } from "./Feeds" +import { GeneralScreen } from "./General" +import { ListsScreen } from "./Lists" +import { NotificationsScreen } from "./Notifications" +import { PrivacyScreen } from "./Privacy" +import { ProfileScreen } from "./Profile" export const SettingRoutes = (Stack: ReturnType) => { - return [] + return [ + , + , + , + , + , + , + , + , + , + , + , + ] } diff --git a/apps/mobile/src/modules/settings/types.ts b/apps/mobile/src/modules/settings/types.ts new file mode 100644 index 0000000000..6a371ca3c7 --- /dev/null +++ b/apps/mobile/src/modules/settings/types.ts @@ -0,0 +1,13 @@ +export type SettingsStackParamList = { + Profile: undefined + Achievement: undefined + General: undefined + Notifications: undefined + Appearance: undefined + Data: undefined + Actions: undefined + Lists: undefined + Feeds: undefined + Privacy: undefined + About: undefined +} diff --git a/apps/mobile/src/screens/(stack)/(tabs)/settings.tsx b/apps/mobile/src/screens/(stack)/(tabs)/settings.tsx index 97557e5923..b41760f8c8 100644 --- a/apps/mobile/src/screens/(stack)/(tabs)/settings.tsx +++ b/apps/mobile/src/screens/(stack)/(tabs)/settings.tsx @@ -2,12 +2,13 @@ import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs" import { useIsFocused } from "@react-navigation/native" import { createNativeStackNavigator } from "@react-navigation/native-stack" import { createContext, useCallback, useContext, useEffect, useRef } from "react" -import type { NativeScrollEvent, NativeSyntheticEvent } from "react-native" -import { findNodeHandle, ScrollView, UIManager, useAnimatedValue } from "react-native" -import { withTiming } from "react-native-reanimated" +import type { NativeScrollEvent, NativeSyntheticEvent, ScrollView } from "react-native" +import { findNodeHandle, UIManager } from "react-native" +import { useSharedValue, withTiming } from "react-native-reanimated" import { useSafeAreaInsets } from "react-native-safe-area-context" import { useEventCallback } from "usehooks-ts" +import { ReAnimatedScrollView } from "@/src/components/common/AnimatedComponents" import { BottomTabBarBackgroundContext } from "@/src/contexts/BottomTabBarBackgroundContext" import { SettingRoutes } from "@/src/modules/settings/routes" import { SettingsList } from "@/src/modules/settings/SettingsList" @@ -52,39 +53,45 @@ function Settings() { useEffect(() => { if (!isFocused) return const scrollView = scrollRef.current + if (scrollView) { const node = findNodeHandle(scrollView) if (node) { UIManager.measure(node, (x, y, width, height) => { - calculateOpacity(height, height, 0) + calculateOpacity(contentSizeRef.current.height, height, 0) }) } } }, [opacity, isFocused, calculateOpacity]) - const animatedScrollY = useAnimatedValue(0) + const animatedScrollY = useSharedValue(0) const handleScroll = useEventCallback( ({ nativeEvent }: NativeSyntheticEvent) => { const { contentOffset, contentSize, layoutMeasurement } = nativeEvent calculateOpacity(contentSize.height, layoutMeasurement.height, contentOffset.y) - animatedScrollY.setValue(contentOffset.y) + animatedScrollY.value = contentOffset.y }, ) const scrollRef = useRef(null) + + const contentSizeRef = useRef({ height: 0, width: 0 }) + return ( - { + contentSizeRef.current = { height: h, width: w } + }} style={{ paddingTop: insets.top }} - className="bg-system-background flex-1" - contentContainerStyle={{ paddingBottom: insets.bottom + tabBarHeight }} + className="bg-system-grouped-background flex-1" scrollIndicatorInsets={{ bottom: tabBarHeight - insets.bottom }} > - + ) } diff --git a/icons/mgc/bell_ringing_cute_fi.svg b/icons/mgc/bell_ringing_cute_fi.svg new file mode 100644 index 0000000000..c5343d3e4b --- /dev/null +++ b/icons/mgc/bell_ringing_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/magic_2_cute_fi.svg b/icons/mgc/magic_2_cute_fi.svg new file mode 100644 index 0000000000..d49113081f --- /dev/null +++ b/icons/mgc/magic_2_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/palette_cute_fi.svg b/icons/mgc/palette_cute_fi.svg new file mode 100644 index 0000000000..fa85478c2b --- /dev/null +++ b/icons/mgc/palette_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/safe_lock_filled.svg b/icons/mgc/safe_lock_filled.svg new file mode 100644 index 0000000000..2c954d8825 --- /dev/null +++ b/icons/mgc/safe_lock_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/settings_7_cute_fi.svg b/icons/mgc/settings_7_cute_fi.svg new file mode 100644 index 0000000000..4e2ad77853 --- /dev/null +++ b/icons/mgc/settings_7_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/trophy_cute_fi.svg b/icons/mgc/trophy_cute_fi.svg new file mode 100644 index 0000000000..82c36d8c94 --- /dev/null +++ b/icons/mgc/trophy_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mgc/user_3_cute_fi.svg b/icons/mgc/user_3_cute_fi.svg new file mode 100644 index 0000000000..bee1ab63c3 --- /dev/null +++ b/icons/mgc/user_3_cute_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/svg-to-rn.ts b/scripts/svg-to-rn.ts index 3c208ffb2b..12306ffffa 100644 --- a/scripts/svg-to-rn.ts +++ b/scripts/svg-to-rn.ts @@ -3,6 +3,9 @@ import path from "node:path" import { parse } from "svg-parser" +const DIST_DIR = "apps/mobile/src/icons" + +const DEFAULT_COLOR = "#10161F" interface SvgNode { type: string tagName: string @@ -15,14 +18,11 @@ const generatePathElement = (node: SvgNode): string => { .map(([key, value]) => { const camelKey = key.replaceAll(/-([a-z])/g, (_match, p1: string) => p1.toUpperCase()) - if (["stroke", "fill"].includes(key)) { - if (value === "currentColor") { - return `${camelKey}={color}` - } - - if (value === "#000000" || value === "#000" || value === "black") { - return `${camelKey}={color}` - } + if ( + ["stroke", "fill"].includes(key) && + (!value || value === "currentColor" || value === DEFAULT_COLOR) + ) { + return `${camelKey}={color}` } if (typeof value === "number") { @@ -59,7 +59,7 @@ interface ${componentName}Props { export const ${componentName} = ({ width = ${width}, height = ${height}, - color = "#10161F", + color = "${DEFAULT_COLOR}", }: ${componentName}Props) => { return ( @@ -80,7 +80,7 @@ const processFile = (filePath: string) => { const rnComponent = convertSvgToRN(svgContent, componentName) - const outputDir = path.join(process.cwd(), "apps/mobile/src/components/icons") + const outputDir = path.join(process.cwd(), DIST_DIR) if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }) }