[add] Scaffold for mobile & desktop

This commit is contained in:
mathysPaul
2023-09-27 17:31:09 +02:00
parent 3ca17338e8
commit 1228eb603e
6 changed files with 450 additions and 6 deletions

View File

@@ -34,7 +34,7 @@ import SigninView from './views/SigninView';
import SignupView from './views/SignupView';
import PasswordResetView from './views/PasswordResetView';
import ForgotPasswordView from './views/ForgotPasswordView';
import ScaffoldCC from './components/UI/Scaffold';
import ScaffoldCC from './components/UI/ScaffoldCC';
import DiscoveryView from './views/V2/DiscoveryView';
// Util function to hide route props in URL

View File

@@ -0,0 +1,87 @@
import { View, Image } from 'react-native';
import { Divider, Text, ScrollView, Flex, Row, Popover, Heading, Button, useBreakpointValue } from 'native-base';
import useColorScheme from '../../hooks/colorScheme';
import { useQuery, useQueries } from '../../Queries';
import API from '../../API';
import Song from '../../models/Song';
import { LinearGradient } from 'expo-linear-gradient';
import ButtonBase from './ButtonBase';
import {
Cup,
Discover,
Icon,
LogoutCurve,
Music,
SearchNormal1,
Setting2,
User,
} from 'iconsax-react-native';
import { useDispatch } from 'react-redux';
import { LoadingView } from '../Loading';
import { translate } from '../../i18n/i18n';
import { unsetAccessToken } from '../../state/UserSlice';
import { useNavigation } from '../../Navigation';
import Spacer from './Spacer';
import ScaffoldDesktopCC from './ScaffoldDesktopCC';
import ScaffoldMobileCC from './ScaffoldMobileCC';
const menu: {
type: "main" | "sub";
title: string;
icon: Icon;
link: string;
}[] = [
{ type: "main", title: 'Discovery', icon: Discover, link: 'HomeNew' },
{ type: "main", title: 'Profile', icon: User, link: 'User' },
{ type: "main", title: 'Music', icon: Music, link: 'Home' },
{ type: "main", title: 'Search', icon: SearchNormal1, link: 'Search' },
{ type: "main", title: 'LeaderBoard', icon: Cup, link: 'Score' },
{ type: "sub", title: 'Settings', icon: Setting2, link: 'Settings' },
];
type ScaffoldCCProps = {
children?: React.ReactNode;
routeName: string;
};
const ScaffoldCC = (props: ScaffoldCCProps) => {
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
const userQuery = useQuery(API.getUserInfo);
if (!userQuery.data || userQuery.isLoading) {
return <LoadingView />;
}
const colorScheme = useColorScheme();
if (screenSize === 'small') {
return (
<ScaffoldMobileCC
user={userQuery.data}
logo={colorScheme == 'light'
? require('../../assets/icon_light.png')
: require('../../assets/icon_dark.png')}
routeName={props.routeName}
menu={menu}
>
{props.children}
</ScaffoldMobileCC>
);
}
return (
<ScaffoldDesktopCC
user={userQuery.data}
logo={colorScheme == 'light'
? require('../../assets/icon_light.png')
: require('../../assets/icon_dark.png')}
routeName={props.routeName}
menu={menu}
>
{props.children}
</ScaffoldDesktopCC>
);
};
export default ScaffoldCC;

View File

@@ -0,0 +1,257 @@
import { View, Image } from 'react-native';
import { Divider, Text, ScrollView, Flex, Row, Popover, Heading, Button } from 'native-base';
import { useQuery, useQueries } from '../../Queries';
import API from '../../API';
import Song from '../../models/Song';
import { LinearGradient } from 'expo-linear-gradient';
import ButtonBase from './ButtonBase';
import { Icon, LogoutCurve } from 'iconsax-react-native';
import { useDispatch } from 'react-redux';
import { LoadingView } from '../Loading';
import { translate } from '../../i18n/i18n';
import { unsetAccessToken } from '../../state/UserSlice';
import { useNavigation } from '../../Navigation';
import Spacer from './Spacer';
import User from '../../models/User';
type ScaffoldDesktopCCProps = {
children?: React.ReactNode;
user: User;
logo: string;
routeName: string;
menu: {
type: "main" | "sub";
title: string;
icon: Icon;
link: string;
}[]
};
const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
const navigation = useNavigation();
const userQuery = useQuery(API.getUserInfo);
const dispatch = useDispatch();
if (!userQuery.data || userQuery.isLoading) {
return <LoadingView />;
}
const playHistoryQuery = useQuery(API.getUserPlayHistory);
const songHistory = useQueries(
playHistoryQuery.data?.map(({ songID }) => API.getSong(songID)) ?? []
);
return (
<Flex style={{ flex: 1 }}>
<View style={{ height: '100%', flexDirection: 'row', overflow: 'hidden' }}>
<View
style={{
display: 'flex',
width: '300px',
height: '100vh',
maxHeight: '100vh',
padding: '32px',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'flex-start',
flexShrink: 0,
}}
>
<View style={{ width: '100%' }}>
<Row>
<Image
source={{ uri: props.logo }}
style={{
aspectRatio: 1,
width: '40px',
height: 'auto',
marginRight: '10px',
}}
/>
<Spacer width="xs" />
<Text fontSize={'2xl'} selectable={false}>
Chromacase
</Text>
</Row>
<Spacer height="xl" />
<View style={{ width: '100%' }}>
{props.menu.map((value) => (
value.type === "main" &&
<View key={'key-menu-link-' + value.title}>
<ButtonBase
style={{ width: '100%' }}
type="menu"
icon={value.icon}
title={value.title}
isDisabled={props.routeName === value.link}
iconVariant={
props.routeName === value.link ? 'Bold' : 'Outline'
}
onPress={async () =>
navigation.navigate(value.link as never)
}
/>
<Spacer />
</View>
))}
</View>
</View>
<View style={{ width: '100%' }}>
<Divider />
<Spacer />
<Text
bold
style={{
paddingHorizontal: '16px',
paddingVertical: '10px',
fontSize: 20,
}}
>
Recently played
</Text>
{songHistory.length === 0 && (
<Text
style={{
paddingHorizontal: '16px',
paddingVertical: '10px',
}}
>
No songs played yet
</Text>
)}
{songHistory
.map((h) => h.data)
.filter((data): data is Song => data !== undefined)
.filter(
(song, i, array) =>
array.map((s) => s.id).findIndex((id) => id == song.id) == i
)
.slice(0, 4)
.map((histoItem, index) => (
<View
key={'tab-navigation-other-' + index}
style={{
paddingHorizontal: '16px',
paddingVertical: '10px',
}}
>
<Text numberOfLines={1}>{histoItem.name}</Text>
</View>
))}
</View>
<Spacer />
<View style={{ width: '100%' }}>
<Divider />
<Spacer />
{props.menu.map((value) => (
value.type === "sub" &&
<ButtonBase
key={'key-menu-link-' + value.title}
style={{ width: '100%' }}
type="menu"
icon={value.icon}
title={value.title}
isDisabled={props.routeName === value.link}
iconVariant={
props.routeName === value.link ? 'Bold' : 'Outline'
}
onPress={async () =>
navigation.navigate(value.link as never)
}
/>
))}
<Spacer />
{!props.user.isGuest && (
<ButtonBase
style={{ width: '100%' }}
icon={LogoutCurve}
title={translate('signOutBtn')}
type="menu"
onPress={async () => {
dispatch(unsetAccessToken());
}}
/>
)}
{props.user.isGuest && (
<Popover
trigger={(triggerProps) => (
<ButtonBase {...triggerProps}>
{translate('signOutBtn')}
</ButtonBase>
)}
>
<Popover.Content>
<Popover.Arrow />
<Popover.Body>
<Heading size="md" mb={2}>
{translate('Attention')}
</Heading>
<Text>
{translate(
'YouAreCurrentlyConnectedWithAGuestAccountWarning'
)}
</Text>
<Button.Group variant="ghost" space={2}>
<Button
onPress={() => dispatch(unsetAccessToken())}
colorScheme="red"
>
{translate('signOutBtn')}
</Button>
<Button
onPress={() => {
navigation.navigate('Login');
}}
colorScheme="green"
>
{translate('signUpBtn')}
</Button>
</Button.Group>
</Popover.Body>
</Popover.Content>
</Popover>
)}
</View>
</View>
<ScrollView
style={{ flex: 1, maxHeight: '100vh' }}
contentContainerStyle={{ flex: 1 }}
>
<View
style={{
backgroundColor: 'rgba(16,16,20,0.5)',
flex: 1,
margin: 8,
padding: 20,
borderRadius: 12,
minHeight: 'fit-content',
}}
>
{props.children}
</View>
<Spacer/>
</ScrollView>
</View>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
colors={['#101014', '#6075F9']}
style={{
top: 0,
bottom: 0,
right: 0,
left: 0,
width: '100%',
height: '100%',
minHeight: 'fit-content',
minWidth: 'fit-content',
flex: 1,
position: 'absolute',
zIndex: -2,
}}
/>
</Flex>
);
};
export default ScaffoldDesktopCC;

View File

@@ -0,0 +1,100 @@
import { View, useWindowDimensions } from 'react-native';
import { ScrollView, Flex, useBreakpointValue, useMediaQuery } from 'native-base';
import { LinearGradient } from 'expo-linear-gradient';
import ButtonBase from './ButtonBase';
import { Icon } from 'iconsax-react-native';
import { useNavigation } from '../../Navigation';
import Spacer from './Spacer';
import User from '../../models/User';
type ScaffoldMobileCCProps = {
children?: React.ReactNode;
user: User;
logo: string;
routeName: string;
menu: {
type: "main" | "sub";
title: string;
icon: Icon;
link: string;
}[]
};
const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
const navigation = useNavigation();
const [isSmallScreen] = useMediaQuery({ maxWidth: 400 });
console.log(isSmallScreen);
return (
<Flex style={{ flex: 1 }}>
<View style={{ height: '100%', flexDirection: 'column', overflow: 'hidden' }}>
<ScrollView
style={{
flex: 1,
maxHeight: '100vh',
flexDirection: 'column',
flexShrink: 0,
padding: 16
}}
contentContainerStyle={{ flex: 1 }}
>
<View style={{ flex: 1, minHeight: 'fit-content' }}>
{props.children}
</View>
<Spacer/>
</ScrollView>
<View style={{padding: 8, paddingTop: 0}}>
<Flex
style={{
width: '100%',
height: 'fit-content',
flexDirection: 'row',
backgroundColor: 'rgba(16,16,20,0.5)',
padding: 8,
justifyContent: 'space-between',
borderRadius: 8,
}}
>
{props.menu.map((value) => (
<ButtonBase
key={'key-menu-link-' + value.title}
type="menu"
icon={value.icon}
title={props.routeName === value.link && !isSmallScreen ? value.title : undefined}
isDisabled={props.routeName === value.link}
iconVariant={
props.routeName === value.link ? 'Bold' : 'Outline'
}
onPress={async () =>
navigation.navigate(value.link as never)
}
/>
))}
</Flex>
</View>
</View>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
colors={['#101014', '#6075F9']}
style={{
top: 0,
bottom: 0,
right: 0,
left: 0,
width: '100%',
height: '100%',
minHeight: 'fit-content',
minWidth: 'fit-content',
flex: 1,
position: 'absolute',
zIndex: -2,
}}
/>
</Flex>
);
};
export default ScaffoldMobileCC;

View File

@@ -40,7 +40,7 @@ const ProfileSettings = () => {
<ElementList
style={{
marginTop: 20,
width: '90%',
width: '100%',
maxWidth: 850,
}}
elements={[

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { Center, Text } from 'native-base';
import { Center, Flex, Text } from 'native-base';
import ProfileSettings from './SettingsProfileView';
import NotificationsView from './NotificationView';
import PrivacyView from './PrivacyView';
import PreferencesView from './PreferencesView';
import { View, useWindowDimensions } from 'react-native';
import { useWindowDimensions } from 'react-native';
import {
TabView,
SceneMap,
@@ -110,7 +110,7 @@ const SetttingsNavigator = () => {
);
return (
<View style={{ width: '100%' }}>
<Flex style={{ flex: 1 }}>
<TabView
style={{ height: 'fit-content' }}
renderTabBar={renderTabBar}
@@ -119,7 +119,7 @@ const SetttingsNavigator = () => {
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
</View>
</Flex>
);
};