Skip to content

Commit

Permalink
Merge pull request #2 from martinezguillaume/nativewind
Browse files Browse the repository at this point in the history
Add NativeWind 🔥
  • Loading branch information
martinezguillaume authored Feb 12, 2024
2 parents 560c83e + e48afc1 commit 569fad9
Show file tree
Hide file tree
Showing 30 changed files with 653 additions and 782 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: test

on:
push:
branches:
- 'main'

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: bun install
- run: bun run deploy
3 changes: 2 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"expo-router"
],
"experiments": {
"typedRoutes": true
"typedRoutes": true,
"baseUrl": "/portfolio"
}
}
}
108 changes: 40 additions & 68 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,70 @@
import AsyncStorage from '@react-native-async-storage/async-storage'
import {DarkTheme, DefaultTheme, ThemeProvider} from '@react-navigation/native'
import {
DarkTheme,
DefaultTheme,
ThemeProvider,
useTheme,
} from '@react-navigation/native'
import {useAssets} from 'expo-asset'
import {loadAsync, useFonts} from 'expo-font'
import {useFonts} from 'expo-font'
import {Slot, SplashScreen} from 'expo-router'
import * as SystemUI from 'expo-system-ui'
import {
INativebaseConfig,
NativeBaseProvider,
StorageManager,
useColorMode,
useColorModeValue,
} from 'native-base'
import {ReactNode, useEffect} from 'react'
import Ionicons from '@expo/vector-icons/Ionicons'
import {useColorScheme} from 'nativewind'
import * as SystemUI from 'expo-system-ui'
import Feather from '@expo/vector-icons/Feather'
import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'
import {View} from 'react-native'

import {useAppStore} from '@/store'
import {useColorSchemeStore, useLocalStore} from '@/stores'
import {ICONS, IMAGES} from '@/assets'
import {theme} from '@/theme'
import {ThemeName, themes} from '@/themes'
import '../global.css'

// Native Base config
const colorModeManager: StorageManager = {
get: async () => {
try {
const val = await AsyncStorage.getItem('@color-mode')
return val === 'dark' ? 'dark' : 'light'
} catch (e) {
return 'dark'
}
},
set: async value => {
try {
if (value) {
await AsyncStorage.setItem('@color-mode', value)
}
} catch (e) {
console.log(e)
}
},
}
const config: INativebaseConfig = {
strictMode: 'error',
theme,
}

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync()

export default function RootLayout() {
const [fontsLoaded, error] = useFonts({
...Ionicons.font,
const [fontsLoaded, fontsError] = useFonts({
...Feather.font,
...MaterialCommunityIcons.font,
})

const [assets] = useAssets([
const [assets, assetsError] = useAssets([
...Object.values(IMAGES),
...Object.values(ICONS),
])

const isLoading = !fontsLoaded || !assets

// Expo Router uses Error Boundaries to catch errors in the navigation tree.
useEffect(() => {
if (error) {
throw error
if (assetsError) {
throw assetsError
}
if (fontsError) {
throw fontsError
}
}, [error])
}, [assetsError, fontsError])

useEffect(() => {
loadAsync(Ionicons.font)
if (fontsLoaded && assets) {
if (!isLoading) {
SplashScreen.hideAsync()
}
}, [assets, fontsLoaded])
}, [isLoading])

if (!fontsLoaded || !assets) {
if (isLoading) {
return null
}

return <RootLayoutNav />
}

function Theme({name, children}: {name: ThemeName; children: ReactNode}) {
const {colorScheme} = useColorScheme()
const {colorScheme} = useColorSchemeStore()
const {colors} = useTheme()

useEffect(() => {
SystemUI.setBackgroundColorAsync(colors.background)
}, [colorScheme, colors.background])

return (
<View className="flex-1" style={themes[name][colorScheme]}>
{children}
Expand All @@ -91,24 +73,14 @@ function Theme({name, children}: {name: ThemeName; children: ReactNode}) {
}

function RootLayoutNav() {
const locale = useAppStore(state => state.locale)
const {colorMode} = useColorMode()
const backgroundColor = useColorModeValue(
DefaultTheme.colors.background,
DarkTheme.colors.background,
)

useEffect(() => {
SystemUI.setBackgroundColorAsync(backgroundColor)
}, [backgroundColor])
const locale = useLocalStore(state => state.locale)
const {colorScheme} = useColorSchemeStore()

return (
<Theme name="twitter">
<ThemeProvider value={colorMode === 'dark' ? DarkTheme : DefaultTheme}>
<NativeBaseProvider colorModeManager={colorModeManager} config={config}>
<Slot key={locale} />
</NativeBaseProvider>
</ThemeProvider>
</Theme>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Theme name="twitter">
<Slot key={locale} />
</Theme>
</ThemeProvider>
)
}
96 changes: 25 additions & 71 deletions app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,104 +1,58 @@
import {Box, Divider, Text} from 'native-base'
import {useCallback, useState, ReactElement} from 'react'
import {LayoutChangeEvent, ListRenderItem, StyleSheet} from 'react-native'
import Animated, {useSharedValue} from 'react-native-reanimated'
import {Route} from 'react-native-tab-view'
import {useTheme} from '@react-navigation/native'
import {useState, ReactElement} from 'react'
import {View} from 'react-native'
import {useSharedValue} from 'react-native-reanimated'

import {
Header,
LocaleFab,
ListItem,
TabView,
TabViewProps,
ColorModeFab,
Background,
ListHeader,
} from '@/components'
import {data, DataItem} from '@/data'
import {useValues} from '@/hooks'
import {data} from '@/data'
import {i18n} from '@/i18n'
import {Text} from '@/components/base'

export default function Home(): ReactElement {
const theme = useTheme()
const scrollY = useSharedValue(0)
const [headerHeight, setHeaderHeight] = useState(0)
const {appWidth} = useValues()

const onHeaderLayout = useCallback(({nativeEvent}: LayoutChangeEvent) => {
setHeaderHeight(nativeEvent.layout.height)
}, [])

const [routes] = useState([
{key: 'projects', title: i18n.t('home.projects')},
{key: 'experiences', title: i18n.t('home.experiences')},
])

const getDataKey = useCallback((item: DataItem) => item.id.toString(), [])
const renderData = useCallback<ListRenderItem<DataItem>>(
({item}) => <ListItem data={item} />,
[],
)
const renderScene = useCallback<TabViewProps<Route>['renderScene']>(
({route, listProps}) => {
return (
<Animated.FlatList
{...listProps}
keyExtractor={getDataKey}
data={route.key === 'experiences' ? data.experiences : data.projects}
renderItem={renderData}
ItemSeparatorComponent={Divider}
/>
)
const [routes] = useState<TabViewProps['routes']>(() => [
{
key: 'projects',
title: i18n.t('home.projects'),
data: data.projects,
},
[getDataKey, renderData],
)
{
key: 'experiences',
title: i18n.t('home.experiences'),
data: data.experiences,
},
])

return (
<>
<Background />

<Box
flex={1}
_web={{
width: appWidth,
alignSelf: 'center',
borderRightWidth: 1,
borderLeftWidth: 1,
}}>
<Header scrollY={scrollY} onLayout={onHeaderLayout} />
<View className="flex-1 max-w-screen-sm web:sm:self-center sm:border-x border-divider">
<Header scrollY={scrollY} />

<TabView
routes={routes}
renderScene={renderScene}
style={styles.tabView}
ListHeaderComponent={<ListHeader scrollY={scrollY} />}
scrollY={scrollY}
paddingTop={headerHeight}
/>

<LocaleFab />
<ColorModeFab />
</Box>
<Box
position={{md: 'absolute'}}
bottom={0}
right={0}
borderWidth={{sm: 1, md: 0}}>
<Text
textAlign="center"
fontWeight={700}
fontSize={8}
bg={theme.colors.background}
px={1}>
</View>

<View className="sm:absolute bottom-0 right-0 border-t sm:border-x sm:border border-divider sm:bg-background">
<Text className="!text-[8px] font-bold text-center p-1 leading-none">
Cette app est cross-platform (iOS + Android + Web) ❤️
</Text>
</Box>
</View>
</>
)
}

const styles = StyleSheet.create({
tabView: {
zIndex: 2,
...StyleSheet.absoluteFillObject,
},
})
Binary file added assets/images/avatar.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/images/avatar.jpg
Binary file not shown.
2 changes: 1 addition & 1 deletion assets/images/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const IMAGES = {
cover: require('./cover.webp'),
avatar: require('./avatar.webp'),
avatar: require('./avatar.jpeg'),
totem: require('./totem.webp'),
t2m: require('./t2m.jpg'),
faks: require('./faks.webp'),
Expand Down
Binary file modified bun.lockb
Binary file not shown.
34 changes: 15 additions & 19 deletions components/Background.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {memo, useEffect} from 'react'
import {StyleSheet} from 'react-native'
import {Box, useColorMode} from 'native-base'
import {ViewProps} from 'react-native'
import Animated, {
interpolateColor,
useAnimatedStyle,
Expand All @@ -9,17 +8,17 @@ import Animated, {
} from 'react-native-reanimated'
import {DarkTheme, DefaultTheme} from '@react-navigation/native'

const AnimatedBox = Animated.createAnimatedComponent(Box)
import {useColorSchemeStore} from '@/stores'

type Props = {}
type Props = ViewProps

export const Background = memo<Props>(() => {
const {colorMode} = useColorMode()
const colorProgress = useSharedValue(0)
export const Background = memo<Props>(({className, style, ...props}) => {
const {colorScheme} = useColorSchemeStore()
const colorProgress = useSharedValue(colorScheme === 'light' ? 0 : 1)

useEffect(() => {
colorProgress.value = withTiming(colorMode === 'light' ? 0 : 1)
}, [colorMode, colorProgress])
colorProgress.value = withTiming(colorScheme === 'light' ? 0 : 1)
}, [colorProgress, colorScheme])

const contentStyle = useAnimatedStyle(() => {
return {
Expand All @@ -31,14 +30,11 @@ export const Background = memo<Props>(() => {
}
})

return <AnimatedBox style={[styles.content, contentStyle]} flex={1} />
})

const styles = StyleSheet.create({
gradient: {
...StyleSheet.absoluteFillObject,
},
content: {
...StyleSheet.absoluteFillObject,
},
return (
<Animated.View
className={`flex-1 absolute top-0 bottom-0 left-0 right-0 ${className}`}
style={[contentStyle, style]}
{...props}
/>
)
})
Loading

0 comments on commit 569fad9

Please sign in to comment.