@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.
- package/.claude/settings.local.json +1 -7
- package/.claude/skills/generate-api-model/references/entities/common.ts +1 -1
- package/README.md +1 -1
- package/app.json +9 -7
- package/package.json +16 -16
- package/skills-lock.json +2 -2
- package/src/app/(root)/(home)/_layout.tsx +15 -0
- package/src/app/(root)/(home)/index.tsx +7 -0
- package/src/app/(root)/(settings)/_layout.tsx +15 -0
- package/src/app/(root)/(settings)/index.tsx +7 -0
- package/src/app/(root)/_layout.tsx +44 -0
- package/src/app/_app.tsx +1 -1
- package/src/app/_layout.tsx +19 -11
- package/src/pages/home/home-main.tsx +18 -0
- package/src/pages/home/index.ts +1 -0
- package/src/pages/settings/index.ts +1 -0
- package/src/pages/settings/settings-main.tsx +18 -0
- package/src/shared/assets/images/index.ts +2 -1
- package/src/shared/assets/images/splash-dark.png +0 -0
- package/src/shared/assets/images/splash-light.png +0 -0
- package/src/shared/assets/translations/en.ts +10 -1
- package/src/shared/assets/translations/ru.ts +10 -1
- package/src/shared/hooks/use-safe-area.ts +31 -12
- package/src/shared/lib/api/api.provider.tsx +2 -2
- package/src/shared/lib/api/constants/index.ts +1 -4
- package/src/shared/lib/api/entities/common.ts +1 -1
- package/src/shared/lib/index.ts +1 -0
- package/src/shared/lib/localization/localization.provider.tsx +1 -1
- package/src/shared/lib/localization/utils/storage.ts +2 -6
- package/src/shared/lib/navigation/hooks/index.ts +1 -0
- package/src/shared/lib/navigation/hooks/use-navigation-native-tabs.ts +10 -0
- package/src/shared/lib/navigation/hooks/use-navigation-options.ts +63 -35
- package/src/shared/lib/navigation/index.ts +2 -0
- package/src/shared/lib/navigation/navigation-native-tabs.context.tsx +5 -0
- package/src/shared/lib/navigation/navigation-native-tabs.provider.tsx +12 -0
- package/src/shared/lib/storage/index.ts +2 -0
- package/src/shared/lib/storage/storage.provider.tsx +7 -0
- package/src/shared/lib/storage/storage.ts +27 -0
- package/src/shared/lib/storage/storage.types.ts +5 -0
- package/src/shared/lib/theme/constants/colors.ts +2 -2
- package/src/shared/lib/theme/constants/index.ts +3 -1
- package/src/shared/lib/theme/theme.provider.tsx +12 -11
- package/src/shared/lib/theme/utils/index.ts +1 -1
- package/src/shared/lib/theme/utils/storage.ts +2 -6
- package/src/shared/ui/atoms/blur/blur.tsx +27 -0
- package/src/shared/ui/atoms/blur/blur.types.ts +9 -0
- package/src/shared/ui/atoms/blur/index.ts +2 -0
- package/src/shared/ui/atoms/glass/glass.tsx +26 -0
- package/src/shared/ui/atoms/glass/glass.types.ts +9 -0
- package/src/shared/ui/atoms/glass/index.ts +2 -0
- package/src/shared/ui/atoms/index.ts +2 -1
- package/src/shared/ui/molecules/keyboard-area/keyboard-area.styles.ts +5 -5
- package/src/shared/ui/molecules/keyboard-area/keyboard-area.tsx +1 -21
- package/src/pages/.gitkeep +0 -0
- package/src/shared/assets/images/splash.png +0 -0
- package/src/shared/lib/api/utils/index.ts +0 -1
- package/src/shared/lib/api/utils/storage.ts +0 -9
package/README.md
CHANGED
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": "
|
|
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.
|
|
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.
|
|
20
|
+
"package": "com.mediacube.expo-template-fsd",
|
|
21
21
|
"adaptiveIcon": {
|
|
22
|
-
"backgroundColor": "#
|
|
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": "#
|
|
39
|
+
"backgroundColor": "#ffffff00",
|
|
40
40
|
"android": {
|
|
41
|
-
"image": "./assets/images/splash-
|
|
42
|
-
"
|
|
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
|
|
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
|
|
45
|
-
"@mediacubeco/base-react
|
|
46
|
-
"@mediacubeco/base-react": "2.0
|
|
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
|
|
56
|
-
"@mediacubeco/
|
|
57
|
-
"@mediacubeco/eslint-config-react
|
|
58
|
-
"@mediacubeco/prettier-config
|
|
59
|
-
"@mediacubeco/
|
|
60
|
-
"@mediacubeco/
|
|
61
|
-
"@mediacubeco/typescript-config-
|
|
62
|
-
"@mediacubeco/typescript-config-react": "2.0
|
|
63
|
-
"@mediacubeco/typescript-config": "2.0
|
|
64
|
-
"@mediacubeco/
|
|
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
|
|
70
|
+
"android": "expo run:android",
|
|
71
71
|
"android:device": "expo run:android -d",
|
|
72
|
-
"ios": "expo
|
|
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": "
|
|
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": "
|
|
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,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,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
|
</>
|
package/src/app/_layout.tsx
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
import {
|
|
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
|
-
<
|
|
8
|
-
<
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
7
|
+
SPLASH_LIGHT: require('./splash-light.png'),
|
|
8
|
+
SPLASH_DARK: require('./splash-dark.png'),
|
|
8
9
|
DEFAULT: require('./default.png'),
|
|
9
10
|
}
|
|
10
11
|
|
|
Binary file
|
|
Binary file
|
|
@@ -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.
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
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 {
|
|
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:
|
|
28
|
+
storage: storageAsync,
|
|
29
29
|
throttleTime: 1000,
|
|
30
30
|
}),
|
|
31
31
|
} satisfies PersistQueryClientProviderProps['persistOptions']
|
package/src/shared/lib/index.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { Storage } from '@mediacubeco/base/services'
|
|
2
|
-
import
|
|
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 }
|
|
@@ -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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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.
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
const triggerLabelProps = {
|
|
98
|
+
hidden: Platform.OS !== 'web',
|
|
99
|
+
children,
|
|
100
|
+
} satisfies NativeTabsTriggerLabelProps
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
102
|
+
const triggerIconProps = {
|
|
103
|
+
selectedColor: COLORS.TYPOGRAPHY.PRIMARY,
|
|
104
|
+
sf,
|
|
105
|
+
md,
|
|
106
|
+
} satisfies NativeTabsTriggerIconProps
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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,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,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 }
|
|
@@ -8,8 +8,8 @@ const PALETTE = {
|
|
|
8
8
|
'LIGHT/SURFACE': '#F5F8FD',
|
|
9
9
|
'LIGHT/FAINT': '#EFF2F7',
|
|
10
10
|
|
|
11
|
-
'LIGHT/PRIMARY': '#
|
|
12
|
-
'LIGHT/PRIMARY_ACTIVE': '#
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
1
|
+
export { themeStorage } from './storage'
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { Storage } from '@mediacubeco/base/services'
|
|
2
|
-
import
|
|
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,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
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
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 {
|
|
5
|
-
const
|
|
6
|
-
const [
|
|
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:
|
|
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 {
|
|
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
|
}
|
package/src/pages/.gitkeep
DELETED
|
File without changes
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './storage'
|