@mediacubeco/expo-template-fsd 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,30 +25,31 @@ This project is built on top of the `@mediacubeco/expo-template-fsd` template.
25
25
 
26
26
  ## Quick Start
27
27
 
28
- Run the project from the `templates/expo-template-fsd` directory:
29
-
30
28
  ```bash
31
- yarn install
32
- yarn start
29
+ npm install
30
+
31
+ npm run postinstall
32
+
33
+ npm run start
33
34
  ```
34
35
 
35
36
  Then choose a target platform:
36
37
 
37
38
  ```bash
38
- yarn android
39
- yarn ios
40
- yarn web
39
+ npm run android
40
+ npm run ios
41
+ npm run web
41
42
  ```
42
43
 
43
44
  ## Scripts
44
45
 
45
- - `yarn prebuild` - Generates native projects with Expo prebuild.
46
- - `yarn start` - Starts the Expo development server.
47
- - `yarn android` - Starts the app in Android mode.
48
- - `yarn android:device` - Runs the Android app on a connected physical device.
49
- - `yarn ios` - Starts the app in iOS mode.
50
- - `yarn ios:device` - Runs the iOS app on a connected physical device.
51
- - `yarn web` - Starts the app in web mode.
52
- - `yarn format` - Formats the codebase with Prettier.
53
- - `yarn lint` - Runs ESLint checks.
54
- - `yarn type` - Runs TypeScript type checking.
46
+ - `npm run prebuild` - Generates native projects with Expo prebuild.
47
+ - `npm run start` - Starts the Expo development server.
48
+ - `npm run android` - Starts the app in Android mode.
49
+ - `npm run android:device` - Runs the Android app on a connected physical device.
50
+ - `npm run ios` - Starts the app in iOS mode.
51
+ - `npm run ios:device` - Runs the iOS app on a connected physical device.
52
+ - `npm run web` - Starts the app in web mode.
53
+ - `npm run format` - Formats the codebase with Prettier.
54
+ - `npm run lint` - Runs ESLint checks.
55
+ - `npm run type` - Runs TypeScript type checking.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "private": false,
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "name": "@mediacubeco/expo-template-fsd",
5
5
  "description": "Application by Mediacube developers",
6
6
  "license": "MIT",
@@ -29,10 +29,10 @@
29
29
  "expo-system-ui": "~56.0.5",
30
30
  "expo-web-browser": "~56.0.5",
31
31
  "i18next": "^26.0.5",
32
- "react": "^19.2.0",
33
- "react-dom": "^19.2.0",
32
+ "react": "19.2.3",
33
+ "react-dom": "19.2.3",
34
34
  "react-i18next": "^17.0.3",
35
- "react-native": "^0.85.3",
35
+ "react-native": "0.85.3",
36
36
  "react-native-gesture-handler": "~2.31.2",
37
37
  "react-native-keyboard-controller": "1.21.6",
38
38
  "react-native-reanimated": "4.3.1",
@@ -41,9 +41,9 @@
41
41
  "react-native-svg": "15.15.4",
42
42
  "react-native-web": "^0.21.0",
43
43
  "react-native-worklets": "0.8.3",
44
- "@mediacubeco/base-react": "2.0.0",
45
- "@mediacubeco/base": "2.0.0",
46
- "@mediacubeco/base-react-native": "2.0.0"
44
+ "@mediacubeco/base-react-native": "2.0.2",
45
+ "@mediacubeco/base-react": "2.0.2",
46
+ "@mediacubeco/base": "2.0.2"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/react": "^19.2.0",
@@ -52,18 +52,19 @@
52
52
  "husky": "^9.1.7",
53
53
  "prettier": "^3.8.1",
54
54
  "typescript": "^6.0.3",
55
- "@mediacubeco/eslint-config-fsd": "2.0.0",
56
- "@mediacubeco/eslint-config": "2.0.0",
57
- "@mediacubeco/eslint-config-react": "2.0.0",
58
- "@mediacubeco/prettier-config": "2.0.0",
59
- "@mediacubeco/typescript-config-fsd": "2.0.0",
60
- "@mediacubeco/typescript-config-react": "2.0.0",
61
- "@mediacubeco/typescript-config-react-native": "2.0.0",
62
- "@mediacubeco/eslint-config-react-native": "2.0.0",
63
- "@mediacubeco/typescript-config": "2.0.0",
64
- "@mediacubeco/prettier-config-fsd": "2.0.0"
55
+ "@mediacubeco/eslint-config-react": "2.0.2",
56
+ "@mediacubeco/eslint-config": "2.0.2",
57
+ "@mediacubeco/prettier-config": "2.0.2",
58
+ "@mediacubeco/eslint-config-react-native": "2.0.2",
59
+ "@mediacubeco/prettier-config-fsd": "2.0.2",
60
+ "@mediacubeco/eslint-config-fsd": "2.0.2",
61
+ "@mediacubeco/typescript-config": "2.0.2",
62
+ "@mediacubeco/typescript-config-react": "2.0.2",
63
+ "@mediacubeco/typescript-config-react-native": "2.0.2",
64
+ "@mediacubeco/typescript-config-fsd": "2.0.2"
65
65
  },
66
66
  "scripts": {
67
+ "postinstall": "npx husky && npx skills update",
67
68
  "prebuild": "expo prebuild",
68
69
  "start": "expo start",
69
70
  "android": "expo start --android",
@@ -0,0 +1,15 @@
1
+ import { Stack } from 'expo-router'
2
+ import { useLocalizationText, useNavigationStackOptions } from '~shared/lib'
3
+
4
+ const Layout = () => {
5
+ const { createStackOptions, createScreenOptions } = useNavigationStackOptions()
6
+ const t = useLocalizationText()
7
+
8
+ return (
9
+ <Stack screenOptions={createStackOptions()}>
10
+ <Stack.Screen name={'index'} options={createScreenOptions({ title: t('home.title') })} />
11
+ </Stack>
12
+ )
13
+ }
14
+
15
+ export default Layout
@@ -0,0 +1,7 @@
1
+ import { HomeMain } from '~pages/home'
2
+
3
+ const Screen = () => {
4
+ return <HomeMain />
5
+ }
6
+
7
+ export default Screen
@@ -0,0 +1,15 @@
1
+ import { Stack } from 'expo-router'
2
+ import { useLocalizationText, useNavigationStackOptions } from '~shared/lib'
3
+
4
+ const Layout = () => {
5
+ const { createStackOptions, createScreenOptions } = useNavigationStackOptions()
6
+ const t = useLocalizationText()
7
+
8
+ return (
9
+ <Stack screenOptions={createStackOptions()}>
10
+ <Stack.Screen name={'index'} options={createScreenOptions({ title: t('settings.title') })} />
11
+ </Stack>
12
+ )
13
+ }
14
+
15
+ export default Layout
@@ -0,0 +1,7 @@
1
+ import { SettingsMain } from '~pages/settings'
2
+
3
+ const Screen = () => {
4
+ return <SettingsMain />
5
+ }
6
+
7
+ export default Screen
@@ -0,0 +1,38 @@
1
+ import { NativeTabs } from 'expo-router/build/native-tabs'
2
+ import { useLocalizationText, useNavigationTabBarOptions } from '~shared/lib'
3
+
4
+ const Layout = () => {
5
+ const { createTabBarOptions, createTabOptions } = useNavigationTabBarOptions()
6
+ const t = useLocalizationText()
7
+
8
+ const tabBarOptions = createTabBarOptions()
9
+
10
+ const homeTabOptions = createTabOptions({
11
+ name: '(home)',
12
+ children: t('home.title'),
13
+ sf: 'house',
14
+ md: 'home',
15
+ })
16
+
17
+ const settingsTabOptions = createTabOptions({
18
+ name: '(settings)',
19
+ children: t('settings.title'),
20
+ sf: 'gear',
21
+ md: 'settings',
22
+ })
23
+
24
+ return (
25
+ <NativeTabs {...tabBarOptions}>
26
+ <NativeTabs.Trigger {...homeTabOptions.triggerProps}>
27
+ <NativeTabs.Trigger.Label {...homeTabOptions.triggerLabelProps} />
28
+ <NativeTabs.Trigger.Icon {...homeTabOptions.triggerIconProps} />
29
+ </NativeTabs.Trigger>
30
+ <NativeTabs.Trigger {...settingsTabOptions.triggerProps}>
31
+ <NativeTabs.Trigger.Label {...settingsTabOptions.triggerLabelProps} />
32
+ <NativeTabs.Trigger.Icon {...settingsTabOptions.triggerIconProps} />
33
+ </NativeTabs.Trigger>
34
+ </NativeTabs>
35
+ )
36
+ }
37
+
38
+ export default Layout
@@ -0,0 +1,18 @@
1
+ import { Text } from 'react-native'
2
+ import { useLocalizationText, useThemeMode } from '~shared/lib'
3
+ import { ScreenContainerScroll } from '~shared/ui'
4
+
5
+ const HomeMain = () => {
6
+ const t = useLocalizationText()
7
+ const { COLORS } = useThemeMode()
8
+
9
+ return (
10
+ <ScreenContainerScroll>
11
+ <Text style={{ fontSize: 20, fontWeight: 'bold', color: COLORS.TYPOGRAPHY.BASE }}>
12
+ {t('home.description')}
13
+ </Text>
14
+ </ScreenContainerScroll>
15
+ )
16
+ }
17
+
18
+ export default HomeMain
@@ -0,0 +1 @@
1
+ export { default as HomeMain } from './home-main'
@@ -0,0 +1 @@
1
+ export { default as SettingsMain } from './settings-main'
@@ -0,0 +1,18 @@
1
+ import { Text } from 'react-native'
2
+ import { useLocalizationText, useThemeMode } from '~shared/lib'
3
+ import { ScreenContainerScroll } from '~shared/ui/templates'
4
+
5
+ const SettingsMain = () => {
6
+ const t = useLocalizationText()
7
+ const { COLORS } = useThemeMode()
8
+
9
+ return (
10
+ <ScreenContainerScroll>
11
+ <Text style={{ fontSize: 20, fontWeight: 'bold', color: COLORS.TYPOGRAPHY.BASE }}>
12
+ {t('settings.description')}
13
+ </Text>
14
+ </ScreenContainerScroll>
15
+ )
16
+ }
17
+
18
+ export default SettingsMain
@@ -1,3 +1,12 @@
1
- const en = {}
1
+ const en = {
2
+ home: {
3
+ title: 'Home',
4
+ description: 'Home screen',
5
+ },
6
+ settings: {
7
+ title: 'Settings',
8
+ description: 'Settings screen',
9
+ },
10
+ }
2
11
 
3
12
  export default en
@@ -1,3 +1,12 @@
1
- const ru = {}
1
+ const ru = {
2
+ home: {
3
+ title: 'Главная',
4
+ description: 'Главная страница',
5
+ },
6
+ settings: {
7
+ title: 'Настройки',
8
+ description: 'Страница настроек',
9
+ },
10
+ }
2
11
 
3
12
  export default ru
@@ -1,6 +1,3 @@
1
- const QUERY_KEYS = {
2
- POSTS: 'posts',
3
- POST: 'post',
4
- }
1
+ const QUERY_KEYS = {}
5
2
 
6
3
  export { QUERY_KEYS }
@@ -11,7 +11,7 @@ import { type LocalizationProviderProps } from './localization.types'
11
11
 
12
12
  const LocalizationProvider = ({ children }: LocalizationProviderProps) => {
13
13
  useMemo(() => {
14
- return i18n
14
+ i18n
15
15
  .use(languageDetector)
16
16
  .use(initReactI18next)
17
17
  .init({
@@ -3,10 +3,12 @@ import { Platform } from 'react-native'
3
3
  import { isGlassEffectAPIAvailable } from 'expo-glass-effect'
4
4
  import { type StackScreenProps } from 'expo-router'
5
5
  import {
6
+ type MaterialIcon,
6
7
  type NativeTabsProps,
7
8
  type NativeTabsTriggerIconProps,
8
9
  type NativeTabsTriggerLabelProps,
9
10
  type NativeTabTriggerProps,
11
+ type SFSymbolIcon,
10
12
  } from 'expo-router/unstable-native-tabs'
11
13
  import { useThemeMode, useThemeSetup } from '../../theme'
12
14
 
@@ -14,16 +16,20 @@ const useNavigationStackOptions = () => {
14
16
  const { COLORS } = useThemeMode()
15
17
  const { themeModeSystem } = useThemeSetup()
16
18
 
17
- const createStackOptions = useCallback(() => {
18
- return {
19
- headerShown: false,
20
- headerTransparent: false,
21
- headerShadowVisible: false,
22
- contentStyle: {
23
- backgroundColor: COLORS.FILL.BASE,
24
- },
25
- } satisfies StackScreenProps['options']
26
- }, [COLORS])
19
+ const createStackOptions = useCallback(
20
+ (options?: StackScreenProps['options']) => {
21
+ return {
22
+ headerShown: false,
23
+ headerTransparent: false,
24
+ headerShadowVisible: false,
25
+ contentStyle: {
26
+ backgroundColor: COLORS.FILL.BASE,
27
+ },
28
+ ...options,
29
+ } satisfies StackScreenProps['options']
30
+ },
31
+ [COLORS]
32
+ )
27
33
 
28
34
  const createScreenOptions = useCallback(
29
35
  (options?: StackScreenProps['options']) => {
@@ -58,33 +64,55 @@ const useNavigationTabBarOptions = () => {
58
64
  const { COLORS } = useThemeMode()
59
65
  const { isThemeModeDark } = useThemeSetup()
60
66
 
61
- const createTabBarOptions = useCallback(() => {
62
- return {
63
- sidebarAdaptable: Platform.OS === 'ios',
64
- disableTransparentOnScrollEdge: Platform.OS === 'ios',
65
- labelVisibilityMode: 'labeled',
66
- blurEffect:
67
- (isGlassEffectAPIAvailable() && 'systemMaterial') || (isThemeModeDark && 'dark') || 'light',
68
- } satisfies NativeTabsProps
69
- }, [isThemeModeDark])
67
+ const createTabBarOptions = useCallback(
68
+ (options?: NativeTabsProps) => {
69
+ return {
70
+ sidebarAdaptable: Platform.OS === 'ios',
71
+ disableTransparentOnScrollEdge: Platform.OS === 'ios',
72
+ labelVisibilityMode: 'labeled',
73
+ blurEffect:
74
+ (isGlassEffectAPIAvailable() && 'systemMaterial') ||
75
+ (isThemeModeDark && 'dark') ||
76
+ 'light',
77
+ ...options,
78
+ } satisfies NativeTabsProps
79
+ },
80
+ [isThemeModeDark]
81
+ )
70
82
 
71
- const createTabOptions = useCallback(() => {
72
- const triggerProps = {} satisfies NativeTabTriggerProps
83
+ const createTabOptions = useCallback(
84
+ ({
85
+ name,
86
+ children,
87
+ sf,
88
+ md,
89
+ }: NativeTabTriggerProps &
90
+ NativeTabsTriggerLabelProps &
91
+ Partial<SFSymbolIcon> &
92
+ Partial<MaterialIcon> = {}) => {
93
+ const triggerProps = {
94
+ name,
95
+ } satisfies NativeTabTriggerProps
73
96
 
74
- const triggerIconProps = {
75
- selectedColor: COLORS.TYPOGRAPHY.PRIMARY,
76
- } satisfies NativeTabsTriggerIconProps
97
+ const triggerLabelProps = {
98
+ hidden: Platform.OS !== 'web',
99
+ children,
100
+ } satisfies NativeTabsTriggerLabelProps
77
101
 
78
- const triggerLabelProps = {
79
- hidden: Platform.OS !== 'web',
80
- } satisfies NativeTabsTriggerLabelProps
102
+ const triggerIconProps = {
103
+ selectedColor: COLORS.TYPOGRAPHY.PRIMARY,
104
+ sf,
105
+ md,
106
+ } satisfies NativeTabsTriggerIconProps
81
107
 
82
- return {
83
- triggerProps,
84
- triggerIconProps,
85
- triggerLabelProps,
86
- }
87
- }, [COLORS])
108
+ return {
109
+ triggerProps,
110
+ triggerLabelProps,
111
+ triggerIconProps,
112
+ }
113
+ },
114
+ [COLORS]
115
+ )
88
116
 
89
117
  return {
90
118
  createTabBarOptions,
@@ -8,8 +8,8 @@ const PALETTE = {
8
8
  'LIGHT/SURFACE': '#F5F8FD',
9
9
  'LIGHT/FAINT': '#EFF2F7',
10
10
 
11
- 'LIGHT/PRIMARY': '#6115CD',
12
- 'LIGHT/PRIMARY_ACTIVE': '#4E11A4',
11
+ 'LIGHT/PRIMARY': '#8756FF',
12
+ 'LIGHT/PRIMARY_ACTIVE': '#7238FF',
13
13
  'LIGHT/PRIMARY_DISABLED': '#D5C9FF',
14
14
 
15
15
  'LIGHT/ACCENT': '#FF2B75',
@@ -51,4 +51,6 @@ const themes = {
51
51
  [ThemeModeSystem.Dark]: darkTheme,
52
52
  }
53
53
 
54
- export { ThemeMode, ThemeModeSystem, ThemeModeStatusBar, themes }
54
+ const hasDarkMode = LIGHT_COLORS !== DARK_COLORS
55
+
56
+ export { ThemeMode, ThemeModeSystem, ThemeModeStatusBar, themes, hasDarkMode }
@@ -1,14 +1,14 @@
1
- import { Platform } from '@mediacubeco/react-native-base'
1
+ import { Platform } from '@mediacubeco/base-react-native'
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import { Appearance, StatusBar, useColorScheme } from 'react-native'
4
4
  import { themeStorage } from './utils'
5
- import { ThemeMode, ThemeModeStatusBar, ThemeModeSystem, themes } from './constants'
5
+ import { hasDarkMode, ThemeMode, ThemeModeStatusBar, ThemeModeSystem, themes } from './constants'
6
6
  import { ThemeContext } from './theme.context'
7
7
  import { type ThemeProviderProps } from './theme.types'
8
8
 
9
9
  const ThemeProvider = ({ children }: ThemeProviderProps) => {
10
10
  const didDefine = useRef(false)
11
- const [themeMode, setThemeMode] = useState(ThemeMode.System)
11
+ const [themeMode, setThemeMode] = useState(hasDarkMode ? ThemeMode.System : ThemeMode.Light)
12
12
  const themeModeSystem = (useColorScheme() as ThemeModeSystem) ?? ThemeModeSystem.Light
13
13
 
14
14
  const currentThemeMode = {
@@ -23,7 +23,9 @@ const ThemeProvider = ({ children }: ThemeProviderProps) => {
23
23
 
24
24
  const updateThemeModeSystem = useCallback((themeMode: ThemeMode) => {
25
25
  if (Platform.isIos) {
26
- if (themeMode === ThemeMode.System) {
26
+ if (!hasDarkMode) {
27
+ Appearance.setColorScheme('light')
28
+ } else if (themeMode === ThemeMode.System) {
27
29
  Appearance.setColorScheme('unspecified')
28
30
  } else {
29
31
  Appearance.setColorScheme(themeMode)
File without changes