@mediacubeco/expo-template-fsd 2.0.1 → 2.1.0

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.
Files changed (57) hide show
  1. package/.claude/settings.local.json +1 -7
  2. package/.claude/skills/generate-api-model/references/entities/common.ts +1 -1
  3. package/README.md +1 -1
  4. package/app.json +9 -7
  5. package/package.json +16 -16
  6. package/skills-lock.json +2 -2
  7. package/src/app/(root)/(home)/_layout.tsx +15 -0
  8. package/src/app/(root)/(home)/index.tsx +7 -0
  9. package/src/app/(root)/(settings)/_layout.tsx +15 -0
  10. package/src/app/(root)/(settings)/index.tsx +7 -0
  11. package/src/app/(root)/_layout.tsx +44 -0
  12. package/src/app/_app.tsx +1 -1
  13. package/src/app/_layout.tsx +19 -11
  14. package/src/pages/home/home-main.tsx +18 -0
  15. package/src/pages/home/index.ts +1 -0
  16. package/src/pages/settings/index.ts +1 -0
  17. package/src/pages/settings/settings-main.tsx +18 -0
  18. package/src/shared/assets/images/index.ts +2 -1
  19. package/src/shared/assets/images/splash-dark.png +0 -0
  20. package/src/shared/assets/images/splash-light.png +0 -0
  21. package/src/shared/assets/translations/en.ts +10 -1
  22. package/src/shared/assets/translations/ru.ts +10 -1
  23. package/src/shared/hooks/use-safe-area.ts +31 -12
  24. package/src/shared/lib/api/api.provider.tsx +2 -2
  25. package/src/shared/lib/api/constants/index.ts +1 -4
  26. package/src/shared/lib/api/entities/common.ts +1 -1
  27. package/src/shared/lib/index.ts +1 -0
  28. package/src/shared/lib/localization/localization.provider.tsx +1 -1
  29. package/src/shared/lib/localization/utils/storage.ts +2 -6
  30. package/src/shared/lib/navigation/hooks/index.ts +1 -0
  31. package/src/shared/lib/navigation/hooks/use-navigation-native-tabs.ts +10 -0
  32. package/src/shared/lib/navigation/hooks/use-navigation-options.ts +63 -35
  33. package/src/shared/lib/navigation/index.ts +2 -0
  34. package/src/shared/lib/navigation/navigation-native-tabs.context.tsx +5 -0
  35. package/src/shared/lib/navigation/navigation-native-tabs.provider.tsx +12 -0
  36. package/src/shared/lib/storage/index.ts +2 -0
  37. package/src/shared/lib/storage/storage.provider.tsx +7 -0
  38. package/src/shared/lib/storage/storage.ts +27 -0
  39. package/src/shared/lib/storage/storage.types.ts +5 -0
  40. package/src/shared/lib/theme/constants/colors.ts +2 -2
  41. package/src/shared/lib/theme/constants/index.ts +3 -1
  42. package/src/shared/lib/theme/theme.provider.tsx +12 -11
  43. package/src/shared/lib/theme/utils/index.ts +1 -1
  44. package/src/shared/lib/theme/utils/storage.ts +2 -6
  45. package/src/shared/ui/atoms/blur/blur.tsx +27 -0
  46. package/src/shared/ui/atoms/blur/blur.types.ts +9 -0
  47. package/src/shared/ui/atoms/blur/index.ts +2 -0
  48. package/src/shared/ui/atoms/glass/glass.tsx +26 -0
  49. package/src/shared/ui/atoms/glass/glass.types.ts +9 -0
  50. package/src/shared/ui/atoms/glass/index.ts +2 -0
  51. package/src/shared/ui/atoms/index.ts +2 -1
  52. package/src/shared/ui/molecules/keyboard-area/keyboard-area.styles.ts +5 -5
  53. package/src/shared/ui/molecules/keyboard-area/keyboard-area.tsx +1 -21
  54. package/src/pages/.gitkeep +0 -0
  55. package/src/shared/assets/images/splash.png +0 -0
  56. package/src/shared/lib/api/utils/index.ts +0 -1
  57. package/src/shared/lib/api/utils/storage.ts +0 -9
@@ -1,11 +1,5 @@
1
1
  {
2
2
  "permissions": {
3
- "allow": [
4
- "Read(//Users/alkhimenok/Public/Programing/Work/kit/**)",
5
- "Bash(npm view *)",
6
- "Bash(npx expo *)",
7
- "Bash(npx expo-doctor *)",
8
- "Bash(pnpm install *)"
9
- ]
3
+ "allow": ["Bash(npm *)", "Bash(npx *)"]
10
4
  }
11
5
  }
@@ -1,4 +1,4 @@
1
- import { generateIdFactory } from '@mediacontent/base/utils'
1
+ import { generateIdFactory } from '@mediacubeco/base/utils'
2
2
  import { type BaseResource } from '../resources'
3
3
 
4
4
  const generateId = generateIdFactory()
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Expo Template FSD
1
+ # Expo-Template-FSD
2
2
 
3
3
  This project is built on top of the `@mediacubeco/expo-template-fsd` template.
4
4
 
package/app.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "expo": {
3
3
  "name": "expo-template-fsd",
4
4
  "slug": "expo-template-fsd",
5
- "scheme": "expotemplatefsd",
5
+ "scheme": "expo-template-fsd",
6
6
  "version": "1.0.0",
7
7
  "orientation": "portrait",
8
8
  "userInterfaceStyle": "automatic",
@@ -12,14 +12,14 @@
12
12
  },
13
13
  "ios": {
14
14
  "supportsTablet": true,
15
- "bundleIdentifier": "com.mediacube.expotemplatefsd",
15
+ "bundleIdentifier": "com.mediacube.expo-template-fsd",
16
16
  "icon": "./src/shared/assets/images/logo-ios.png"
17
17
  },
18
18
  "android": {
19
19
  "predictiveBackGestureEnabled": false,
20
- "package": "com.mediacube.expotemplatefsd",
20
+ "package": "com.mediacube.expo-template-fsd",
21
21
  "adaptiveIcon": {
22
- "backgroundColor": "#E6F4FE",
22
+ "backgroundColor": "#ffffff00",
23
23
  "foregroundImage": "./src/shared/assets/images/logo-android-foreground.png",
24
24
  "backgroundImage": "./src/shared/assets/images/logo-android-background.png",
25
25
  "monochromeImage": "./src/shared/assets/images/logo-android-monochrome.png"
@@ -36,10 +36,12 @@
36
36
  [
37
37
  "expo-splash-screen",
38
38
  {
39
- "backgroundColor": "#208AEF",
39
+ "backgroundColor": "#ffffff00",
40
40
  "android": {
41
- "image": "./assets/images/splash-icon.png",
42
- "imageWidth": 76
41
+ "image": "./src/shared/assets/images/splash-light.png",
42
+ "dark": {
43
+ "image": "./src/shared/assets/images/splash-dark.png"
44
+ }
43
45
  }
44
46
  }
45
47
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "private": false,
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "name": "@mediacubeco/expo-template-fsd",
5
5
  "description": "Application by Mediacube developers",
6
6
  "license": "MIT",
@@ -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": "2.0.1",
45
- "@mediacubeco/base-react-native": "2.0.1",
46
- "@mediacubeco/base-react": "2.0.1"
44
+ "@mediacubeco/base": "2.1.0",
45
+ "@mediacubeco/base-react": "2.1.0",
46
+ "@mediacubeco/base-react-native": "2.1.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/react": "^19.2.0",
@@ -52,24 +52,24 @@
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.1",
56
- "@mediacubeco/prettier-config": "2.0.1",
57
- "@mediacubeco/eslint-config-react-native": "2.0.1",
58
- "@mediacubeco/prettier-config-fsd": "2.0.1",
59
- "@mediacubeco/eslint-config-react": "2.0.1",
60
- "@mediacubeco/eslint-config": "2.0.1",
61
- "@mediacubeco/typescript-config-fsd": "2.0.1",
62
- "@mediacubeco/typescript-config-react": "2.0.1",
63
- "@mediacubeco/typescript-config": "2.0.1",
64
- "@mediacubeco/typescript-config-react-native": "2.0.1"
55
+ "@mediacubeco/eslint-config": "2.1.0",
56
+ "@mediacubeco/eslint-config-fsd": "2.1.0",
57
+ "@mediacubeco/eslint-config-react": "2.1.0",
58
+ "@mediacubeco/prettier-config": "2.1.0",
59
+ "@mediacubeco/prettier-config-fsd": "2.1.0",
60
+ "@mediacubeco/typescript-config": "2.1.0",
61
+ "@mediacubeco/typescript-config-react": "2.1.0",
62
+ "@mediacubeco/typescript-config-react-native": "2.1.0",
63
+ "@mediacubeco/typescript-config-fsd": "2.1.0",
64
+ "@mediacubeco/eslint-config-react-native": "2.1.0"
65
65
  },
66
66
  "scripts": {
67
67
  "postinstall": "npx husky && npx skills update",
68
68
  "prebuild": "expo prebuild",
69
69
  "start": "expo start",
70
- "android": "expo start --android",
70
+ "android": "expo run:android",
71
71
  "android:device": "expo run:android -d",
72
- "ios": "expo start --ios",
72
+ "ios": "expo run:ios",
73
73
  "ios:device": "expo run:ios -d",
74
74
  "web": "expo start --web",
75
75
  "format": "prettier --write ./src",
package/skills-lock.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "source": "pbakaus/impeccable",
6
6
  "sourceType": "github",
7
7
  "skillPath": ".agents/skills/impeccable/SKILL.md",
8
- "computedHash": "2f248d7db158d32acb8d52e688444de1edbaccf7344ac5288634cbe427bc8a8a"
8
+ "computedHash": "5e8c66eeffba8dc0c5e0941afbf4aecd6359184f8b0ea08cfc60a33f2e328e49"
9
9
  },
10
10
  "react-native-best-practices": {
11
11
  "source": "callstackincubator/agent-skills",
@@ -17,7 +17,7 @@
17
17
  "source": "vercel-labs/agent-skills",
18
18
  "sourceType": "github",
19
19
  "skillPath": "skills/react-best-practices/SKILL.md",
20
- "computedHash": "ca7b0c0c6e5f2750043f7f0cd72d16ac4e2abc48f9b5500d047a4b77a2506212"
20
+ "computedHash": "3219a1944e404ffc14d1d9d6aef6dd2e3855b81387ee0a044ccbfe14d34c2357"
21
21
  },
22
22
  "vercel-react-native-skills": {
23
23
  "source": "vercel-labs/agent-skills",
@@ -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,44 @@
1
+ import { NativeTabs } from 'expo-router/build/native-tabs'
2
+ import {
3
+ NavigationNativeTabsProvider,
4
+ useLocalizationText,
5
+ useNavigationTabBarOptions,
6
+ } from '~shared/lib'
7
+
8
+ const Layout = () => {
9
+ const { createTabBarOptions, createTabOptions } = useNavigationTabBarOptions()
10
+ const t = useLocalizationText()
11
+
12
+ const tabBarOptions = createTabBarOptions()
13
+
14
+ const homeTabOptions = createTabOptions({
15
+ name: '(home)',
16
+ children: t('home.title'),
17
+ sf: 'house',
18
+ md: 'home',
19
+ })
20
+
21
+ const settingsTabOptions = createTabOptions({
22
+ name: '(settings)',
23
+ children: t('settings.title'),
24
+ sf: 'gear',
25
+ md: 'settings',
26
+ })
27
+
28
+ return (
29
+ <NavigationNativeTabsProvider>
30
+ <NativeTabs {...tabBarOptions}>
31
+ <NativeTabs.Trigger {...homeTabOptions.triggerProps}>
32
+ <NativeTabs.Trigger.Label {...homeTabOptions.triggerLabelProps} />
33
+ <NativeTabs.Trigger.Icon {...homeTabOptions.triggerIconProps} />
34
+ </NativeTabs.Trigger>
35
+ <NativeTabs.Trigger {...settingsTabOptions.triggerProps}>
36
+ <NativeTabs.Trigger.Label {...settingsTabOptions.triggerLabelProps} />
37
+ <NativeTabs.Trigger.Icon {...settingsTabOptions.triggerIconProps} />
38
+ </NativeTabs.Trigger>
39
+ </NativeTabs>
40
+ </NavigationNativeTabsProvider>
41
+ )
42
+ }
43
+
44
+ export default Layout
package/src/app/_app.tsx CHANGED
@@ -10,7 +10,7 @@ const App = () => {
10
10
  <>
11
11
  <StatusBar style={'auto'} />
12
12
  <NavigationBar style={'auto'} />
13
- <Stack screenOptions={createStackOptions()}>
13
+ <Stack initialRouteName={'(root)'} screenOptions={createStackOptions()}>
14
14
  <Stack.Screen name={'(root)'} />
15
15
  </Stack>
16
16
  </>
@@ -1,19 +1,27 @@
1
- import { ApiProvider, LocalizationProvider, NavigationProvider, ThemeProvider } from '~shared/lib'
1
+ import {
2
+ ApiProvider,
3
+ LocalizationProvider,
4
+ NavigationProvider,
5
+ StorageProvider,
6
+ ThemeProvider,
7
+ } from '~shared/lib'
2
8
  import App from './_app'
3
9
  import Splash from './_splash'
4
10
 
5
11
  const Layout = () => {
6
12
  return (
7
- <ApiProvider>
8
- <ThemeProvider>
9
- <LocalizationProvider>
10
- <NavigationProvider>
11
- <App />
12
- <Splash />
13
- </NavigationProvider>
14
- </LocalizationProvider>
15
- </ThemeProvider>
16
- </ApiProvider>
13
+ <StorageProvider>
14
+ <ApiProvider>
15
+ <ThemeProvider>
16
+ <LocalizationProvider>
17
+ <NavigationProvider>
18
+ <App />
19
+ <Splash />
20
+ </NavigationProvider>
21
+ </LocalizationProvider>
22
+ </ThemeProvider>
23
+ </ApiProvider>
24
+ </StorageProvider>
17
25
  )
18
26
  }
19
27
 
@@ -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
@@ -4,7 +4,8 @@ const IMAGES = {
4
4
  LOGO_ANDROID_BACKGROUND: require('./logo-android-background.png'),
5
5
  LOGO_ANDROID_FOREGROUND: require('./logo-android-foreground.png'),
6
6
  LOGO_ANDROID_MONOCHROME: require('./logo-android-monochrome.png'),
7
- SPLASH: require('./splash.png'),
7
+ SPLASH_LIGHT: require('./splash-light.png'),
8
+ SPLASH_DARK: require('./splash-dark.png'),
8
9
  DEFAULT: require('./default.png'),
9
10
  }
10
11
 
@@ -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,7 +1,8 @@
1
- import { Platform } from 'react-native'
1
+ import { Platform } from '@mediacubeco/base-react-native'
2
2
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
3
3
  import { useBottomTabBarHeight } from 'expo-router/js-tabs'
4
4
  import { useHeaderHeight } from 'expo-router/react-navigation'
5
+ import { useNavigationNativeTabs } from '../lib'
5
6
 
6
7
  const ANDROID_TAB_BAR_DEFAULT_HEIGHT = 56
7
8
  const IOS_TAB_BAR_DEFAULT_HEIGHT = 50
@@ -10,7 +11,7 @@ const useNavigationHeaderHeight = (safeArea: number = 0) => {
10
11
  try {
11
12
  const height = useHeaderHeight()
12
13
 
13
- return Platform.select({
14
+ return Platform.by({
14
15
  android: height ? 0 : safeArea,
15
16
  ios: height || safeArea,
16
17
  default: safeArea,
@@ -21,19 +22,36 @@ const useNavigationHeaderHeight = (safeArea: number = 0) => {
21
22
  }
22
23
 
23
24
  const useNavigationFooterHeight = (safeArea: number = 0) => {
25
+ const isNativeTabs = useNavigationNativeTabs()
26
+
24
27
  try {
25
28
  const height = useBottomTabBarHeight()
26
29
 
27
- return Platform.select({
28
- android: (height || ANDROID_TAB_BAR_DEFAULT_HEIGHT) + safeArea,
29
- ios: (height || IOS_TAB_BAR_DEFAULT_HEIGHT) + safeArea,
30
- default: safeArea,
31
- })
30
+ return {
31
+ bottom: Platform.by({
32
+ android: height ? 0 : safeArea,
33
+ ios: height ? 0 : safeArea,
34
+ default: safeArea,
35
+ }),
36
+ value: Platform.by({
37
+ android: height,
38
+ ios: height,
39
+ default: safeArea,
40
+ }),
41
+ }
32
42
  } catch (_) {
33
- return Platform.select({
34
- android: 0,
35
- default: safeArea,
36
- })
43
+ return {
44
+ bottom: Platform.by({
45
+ android: isNativeTabs ? 0 : safeArea,
46
+ ios: safeArea,
47
+ default: safeArea,
48
+ }),
49
+ value: Platform.by({
50
+ android: isNativeTabs ? safeArea * 2 + ANDROID_TAB_BAR_DEFAULT_HEIGHT : safeArea,
51
+ ios: isNativeTabs ? safeArea + IOS_TAB_BAR_DEFAULT_HEIGHT : safeArea,
52
+ default: safeArea,
53
+ }),
54
+ }
37
55
  }
38
56
  }
39
57
 
@@ -44,7 +62,8 @@ const useSafeArea = () => {
44
62
 
45
63
  return {
46
64
  TOP: header,
47
- BOTTOM: footer,
65
+ BOTTOM: footer.bottom,
66
+ FOOTER: footer.value,
48
67
  LEFT: left,
49
68
  RIGHT: right,
50
69
  }
@@ -5,7 +5,7 @@ import {
5
5
  PersistQueryClientProvider,
6
6
  type PersistQueryClientProviderProps,
7
7
  } from '@tanstack/react-query-persist-client'
8
- import { apiAsyncStorage } from './utils'
8
+ import { storageAsync } from '../storage'
9
9
  import { type ApiProviderProps } from './api.types'
10
10
 
11
11
  const client = new QueryClient({
@@ -25,7 +25,7 @@ const persistOptions = {
25
25
  },
26
26
  },
27
27
  persister: createAsyncStoragePersister({
28
- storage: apiAsyncStorage,
28
+ storage: storageAsync,
29
29
  throttleTime: 1000,
30
30
  }),
31
31
  } satisfies PersistQueryClientProviderProps['persistOptions']
@@ -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 }
@@ -1,4 +1,4 @@
1
- import { generateIdFactory } from '@mediacontent/base/utils'
1
+ import { generateIdFactory } from '@mediacubeco/base/utils'
2
2
  import { type BaseResource } from '../resources'
3
3
 
4
4
  const generateId = generateIdFactory()
@@ -1,4 +1,5 @@
1
1
  export * from './api'
2
2
  export * from './localization'
3
3
  export * from './navigation'
4
+ export * from './storage'
4
5
  export * from './theme'
@@ -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({
@@ -1,11 +1,7 @@
1
1
  import { Storage } from '@mediacubeco/base/services'
2
- import * as SecureStore from 'expo-secure-store'
2
+ import { storageModel } from '../../storage'
3
3
  import { type LocalizationCode } from '../constants'
4
4
 
5
- const localizationStorage = new Storage({
6
- get: SecureStore.getItemAsync,
7
- set: SecureStore.setItemAsync,
8
- delete: SecureStore.deleteItemAsync,
9
- }).create<'code', LocalizationCode>('code')
5
+ const localizationStorage = new Storage(storageModel).create<'code', LocalizationCode>('code')
10
6
 
11
7
  export { localizationStorage }
@@ -1,3 +1,4 @@
1
1
  export * from './use-navigation'
2
2
  export * from './use-navigation-params'
3
3
  export * from './use-navigation-options'
4
+ export * from './use-navigation-native-tabs'
@@ -0,0 +1,10 @@
1
+ import { useContext } from 'react'
2
+ import { NavigationNativeTabsContext } from '../navigation-native-tabs.context'
3
+
4
+ const useNavigationNativeTabs = () => {
5
+ const isNativeTabs = useContext(NavigationNativeTabsContext)
6
+
7
+ return isNativeTabs
8
+ }
9
+
10
+ export { useNavigationNativeTabs }
@@ -1,12 +1,14 @@
1
+ import { Platform } from '@mediacubeco/base-react-native'
1
2
  import { useCallback } from 'react'
2
- 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']) => {
@@ -37,7 +43,7 @@ const useNavigationStackOptions = () => {
37
43
  headerBlurEffect: isGlassEffectAPIAvailable() ? 'none' : themeModeSystem,
38
44
  headerTintColor: COLORS.TYPOGRAPHY.BASE,
39
45
  headerStyle: {
40
- backgroundColor: Platform.select({
46
+ backgroundColor: Platform.by({
41
47
  ios: COLORS.FILL.TRANSPARENT,
42
48
  default: COLORS.FILL.CONTRAST,
43
49
  }),
@@ -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,
@@ -3,5 +3,7 @@ export {
3
3
  useNavigationParams,
4
4
  useNavigationStackOptions,
5
5
  useNavigationTabBarOptions,
6
+ useNavigationNativeTabs,
6
7
  } from './hooks'
7
8
  export { default as NavigationProvider } from './navigation.provider'
9
+ export { default as NavigationNativeTabsProvider } from './navigation-native-tabs.provider'
@@ -0,0 +1,5 @@
1
+ import { createContext } from 'react'
2
+
3
+ const NavigationNativeTabsContext = createContext(false)
4
+
5
+ export { NavigationNativeTabsContext }
@@ -0,0 +1,12 @@
1
+ import { NavigationNativeTabsContext } from './navigation-native-tabs.context'
2
+ import { type NavigationProviderProps } from './navigation.types'
3
+
4
+ const NavigationNativeTabsProvider = ({ children }: NavigationProviderProps) => {
5
+ return (
6
+ <NavigationNativeTabsContext.Provider value={true}>
7
+ {children}
8
+ </NavigationNativeTabsContext.Provider>
9
+ )
10
+ }
11
+
12
+ export default NavigationNativeTabsProvider
@@ -0,0 +1,2 @@
1
+ export { default as StorageProvider } from './storage.provider'
2
+ export { storageAsync, storageModel } from './storage'
@@ -0,0 +1,7 @@
1
+ import { type StorageProviderProps } from './storage.types'
2
+
3
+ const StorageProvider = ({ children }: StorageProviderProps) => {
4
+ return <>{children}</>
5
+ }
6
+
7
+ export default StorageProvider
@@ -0,0 +1,27 @@
1
+ import { Platform } from '@mediacubeco/base-react-native'
2
+ import * as SecureStore from 'expo-secure-store'
3
+
4
+ const storageModelNative = {
5
+ getItem: SecureStore.getItemAsync,
6
+ setItem: SecureStore.setItemAsync,
7
+ removeItem: SecureStore.deleteItemAsync,
8
+ }
9
+
10
+ const storageModelWeb = {
11
+ getItem: (key: string) => Promise.resolve(localStorage.getItem(key)),
12
+ setItem: (key: string, value: string) => Promise.resolve(localStorage.setItem(key, value)),
13
+ removeItem: (key: string) => Promise.resolve(localStorage.removeItem(key)),
14
+ }
15
+
16
+ const storageAsync = Platform.by({
17
+ web: storageModelWeb,
18
+ default: storageModelNative,
19
+ })
20
+
21
+ const storageModel = {
22
+ get: storageAsync.getItem,
23
+ set: storageAsync.setItem,
24
+ delete: storageAsync.removeItem,
25
+ }
26
+
27
+ export { storageAsync, storageModel }
@@ -0,0 +1,5 @@
1
+ import { type PropsWithChildren } from 'react'
2
+
3
+ type StorageProviderProps = PropsWithChildren
4
+
5
+ export type { StorageProviderProps }
@@ -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,13 @@
1
- import { Platform } from '@mediacubeco/base-react-native'
2
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
2
  import { Appearance, StatusBar, useColorScheme } from 'react-native'
4
3
  import { themeStorage } from './utils'
5
- import { ThemeMode, ThemeModeStatusBar, ThemeModeSystem, themes } from './constants'
4
+ import { hasDarkMode, ThemeMode, ThemeModeStatusBar, ThemeModeSystem, themes } from './constants'
6
5
  import { ThemeContext } from './theme.context'
7
6
  import { type ThemeProviderProps } from './theme.types'
8
7
 
9
8
  const ThemeProvider = ({ children }: ThemeProviderProps) => {
10
9
  const didDefine = useRef(false)
11
- const [themeMode, setThemeMode] = useState(ThemeMode.System)
10
+ const [themeMode, setThemeMode] = useState(hasDarkMode ? ThemeMode.System : ThemeMode.Light)
12
11
  const themeModeSystem = (useColorScheme() as ThemeModeSystem) ?? ThemeModeSystem.Light
13
12
 
14
13
  const currentThemeMode = {
@@ -22,13 +21,15 @@ const ThemeProvider = ({ children }: ThemeProviderProps) => {
22
21
  }, [currentThemeMode])
23
22
 
24
23
  const updateThemeModeSystem = useCallback((themeMode: ThemeMode) => {
25
- if (Platform.isIos) {
26
- if (themeMode === ThemeMode.System) {
27
- Appearance.setColorScheme('unspecified')
28
- } else {
29
- Appearance.setColorScheme(themeMode)
30
- }
24
+ // if (Platform.isIos) {
25
+ if (!hasDarkMode) {
26
+ Appearance.setColorScheme('light')
27
+ } else if (themeMode === ThemeMode.System) {
28
+ Appearance.setColorScheme('unspecified')
29
+ } else {
30
+ Appearance.setColorScheme(themeMode)
31
31
  }
32
+ // }
32
33
  }, [])
33
34
 
34
35
  useEffect(() => {
@@ -51,9 +52,9 @@ const ThemeProvider = ({ children }: ThemeProviderProps) => {
51
52
  const updateThemeMode = async () => {
52
53
  if (didDefine.current) {
53
54
  await themeStorage.mode.set(themeMode)
54
-
55
- updateThemeModeSystem(themeMode)
56
55
  }
56
+
57
+ updateThemeModeSystem(themeMode)
57
58
  }
58
59
 
59
60
  updateThemeMode()
@@ -1 +1 @@
1
- export * from './storage'
1
+ export { themeStorage } from './storage'
@@ -1,11 +1,7 @@
1
1
  import { Storage } from '@mediacubeco/base/services'
2
- import * as SecureStore from 'expo-secure-store'
2
+ import { storageModel } from '../../storage'
3
3
  import { type ThemeMode } from '../constants'
4
4
 
5
- const themeStorage = new Storage({
6
- get: SecureStore.getItemAsync,
7
- set: SecureStore.setItemAsync,
8
- delete: SecureStore.deleteItemAsync,
9
- }).create<'mode', ThemeMode>('mode')
5
+ const themeStorage = new Storage(storageModel).create<'mode', ThemeMode>('mode')
10
6
 
11
7
  export { themeStorage }
@@ -0,0 +1,27 @@
1
+ import { type Ref } from 'react'
2
+ import { BlurView } from 'expo-blur'
3
+ import { type BlurProps } from './blur.types'
4
+
5
+ const Blur = ({
6
+ reference,
7
+ blurTarget,
8
+ tint,
9
+ intensity,
10
+ blurReductionFactor,
11
+ blurMethod,
12
+ ...props
13
+ }: BlurProps) => {
14
+ return (
15
+ <BlurView
16
+ ref={reference as Ref<BlurView>}
17
+ blurTarget={blurTarget}
18
+ tint={tint}
19
+ intensity={intensity}
20
+ blurReductionFactor={blurReductionFactor}
21
+ blurMethod={blurMethod}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ export default Blur
@@ -0,0 +1,9 @@
1
+ import { type Ref } from 'react'
2
+ import { type View } from 'react-native'
3
+ import { type BlurViewProps } from 'expo-blur'
4
+
5
+ type BlurProps = BlurViewProps & {
6
+ reference?: Ref<View>
7
+ }
8
+
9
+ export type { BlurProps }
@@ -0,0 +1,2 @@
1
+ export { default as Blur } from './blur'
2
+ export type { BlurProps } from './blur.types'
@@ -0,0 +1,26 @@
1
+ import { GlassView, isGlassEffectAPIAvailable } from 'expo-glass-effect'
2
+ import { Blur } from '../blur'
3
+ import { type GlassProps } from './glass.types'
4
+
5
+ const Glass = ({
6
+ reference,
7
+ glassEffectStyle = 'clear',
8
+ tintColor,
9
+ colorScheme,
10
+ isInteractive = true,
11
+ ...props
12
+ }: GlassProps) => {
13
+ return isGlassEffectAPIAvailable() ? (
14
+ <GlassView
15
+ ref={reference}
16
+ glassEffectStyle={glassEffectStyle}
17
+ tintColor={tintColor}
18
+ colorScheme={colorScheme}
19
+ isInteractive={isInteractive}
20
+ {...props}
21
+ />
22
+ ) : (
23
+ <Blur reference={reference} {...props} />
24
+ )
25
+ }
26
+ export default Glass
@@ -0,0 +1,9 @@
1
+ import { type Ref } from 'react'
2
+ import { type View } from 'react-native'
3
+ import { type GlassViewProps } from 'expo-glass-effect'
4
+
5
+ type GlassProps = GlassViewProps & {
6
+ reference?: Ref<View>
7
+ }
8
+
9
+ export type { GlassProps }
@@ -0,0 +1,2 @@
1
+ export { default as Glass } from './glass'
2
+ export type { GlassProps } from './glass.types'
@@ -1 +1,2 @@
1
- export const atoms = {}
1
+ export * from './blur'
2
+ export * from './glass'
@@ -1,18 +1,18 @@
1
+ import { useAnimatedKeyboard } from 'react-native-keyboard-controller'
1
2
  import { useAnimated, useAnimatedStyle, useSafeArea } from '../../../hooks'
2
3
 
3
4
  const useKeyboardAreaStyles = () => {
4
- const { BOTTOM } = useSafeArea()
5
- const [safeAreaHeight] = useAnimated(BOTTOM)
6
- const [keyboardHeight] = useAnimated<number>(0)
5
+ const { FOOTER } = useSafeArea()
6
+ const { height } = useAnimatedKeyboard()
7
+ const [safeAreaHeight] = useAnimated(FOOTER)
7
8
 
8
9
  const keyboardAnimatedStyle = useAnimatedStyle(() => {
9
10
  return {
10
- height: keyboardHeight.value - safeAreaHeight.value,
11
+ height: height.value - safeAreaHeight.value,
11
12
  }
12
13
  })
13
14
 
14
15
  return {
15
- keyboardHeight,
16
16
  keyboardAnimatedStyle,
17
17
  }
18
18
  }
@@ -1,5 +1,4 @@
1
1
  import { View } from 'react-native'
2
- import { useKeyboardHandler } from 'react-native-keyboard-controller'
3
2
  import { animate } from '../../../hooks'
4
3
  import { useKeyboardAreaStyles } from './keyboard-area.styles'
5
4
  import { type KeyboardAreaProps } from './keyboard-area.types'
@@ -7,26 +6,7 @@ import { type KeyboardAreaProps } from './keyboard-area.types'
7
6
  const KeyboardAnimatedArea = animate(View)
8
7
 
9
8
  const KeyboardArea = ({}: KeyboardAreaProps) => {
10
- const { keyboardHeight, keyboardAnimatedStyle } = useKeyboardAreaStyles()
11
-
12
- useKeyboardHandler(
13
- {
14
- onStart: () => {
15
- 'worklet'
16
- },
17
- onMove: (event) => {
18
- 'worklet'
19
-
20
- keyboardHeight.set(event.height)
21
- },
22
- onEnd: (event) => {
23
- 'worklet'
24
-
25
- keyboardHeight.set(event.height)
26
- },
27
- },
28
- []
29
- )
9
+ const { keyboardAnimatedStyle } = useKeyboardAreaStyles()
30
10
 
31
11
  return <KeyboardAnimatedArea style={keyboardAnimatedStyle} />
32
12
  }
File without changes
Binary file
@@ -1 +0,0 @@
1
- export * from './storage'
@@ -1,9 +0,0 @@
1
- import * as SecureStore from 'expo-secure-store'
2
-
3
- const apiAsyncStorage = {
4
- getItem: SecureStore.getItemAsync,
5
- setItem: SecureStore.setItemAsync,
6
- removeItem: SecureStore.deleteItemAsync,
7
- }
8
-
9
- export { apiAsyncStorage }