Fix types

This commit is contained in:
2024-01-07 01:29:58 +01:00
committed by Clément Le Bihan
parent 17a4328af5
commit 3c04e8bb39
20 changed files with 35 additions and 197 deletions

View File

@@ -41,8 +41,8 @@ import { createCustomNavigator } from './utils/navigator';
import { Cup, Discover, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const Tab = createCustomNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const Stack = createNativeStackNavigator<AppRouteParams>();
const Tab = createCustomNavigator<AppRouteParams>();
const Tabs = () => {
return (
@@ -50,7 +50,7 @@ const Tabs = () => {
{Object.entries(tabRoutes).map(([name, route], routeIndex) => (
<Tab.Screen
key={'route-' + routeIndex}
name={name}
name={name as keyof AppRouteParams}
options={{ ...route.options, headerTransparent: true }}
component={route.component}
/>
@@ -167,18 +167,19 @@ type Route<Props = any> = {
link?: string;
};
type OmitOrUndefined<T, K extends string> = T extends undefined ? T : Omit<T, K>;
// if the component has no props, ComponentProps return unknown so we remove those
type RemoveNonObjects<T> = [T] extends [{}] ? T : undefined;
type RouteParams<Routes extends Record<string, Route>> = {
[RouteName in keyof Routes]: OmitOrUndefined<
ComponentProps<Routes[RouteName]['component']>,
keyof NativeStackScreenProps<{}>
>;
[RouteName in keyof Routes]: RemoveNonObjects<ComponentProps<Routes[RouteName]['component']>>;
};
type PrivateRoutesParams = RouteParams<typeof protectedRoutes>;
type PublicRoutesParams = RouteParams<typeof publicRoutes>;
type AppRouteParams = PrivateRoutesParams & PublicRoutesParams;
type TabsRoutesParams = RouteParams<typeof tabRoutes>;
type AppRouteParams = PrivateRoutesParams &
PublicRoutesParams &
TabsRoutesParams & { Oops: undefined };
const RouteToScreen = <T extends {}>(Component: Route<T>['component']) =>
function Route(props: NativeStackScreenProps<T & ParamListBase>) {
@@ -326,6 +327,4 @@ export const Router = () => {
);
};
export type RouteProps<T> = T & Pick<NativeStackScreenProps<T & ParamListBase>, 'route'>;
export const useNavigation = () => navigationHook<NavigationProp<AppRouteParams>>();

View File

@@ -15,7 +15,7 @@ type CompetenciesTableProps = {
const CompetenciesTable = (props: CompetenciesTableProps) => {
const navigation = useNavigation();
return (
<Card padding={5} onPress={() => navigation.navigate('User', {})} shadow={3}>
<Card padding={5} onPress={() => navigation.navigate('User')} shadow={3}>
<HStack space={5} flex={1}>
<VStack space={5}>
{Object.keys(props).map((competencyName, i) => (

View File

@@ -14,7 +14,7 @@ const ProgressBar = ({ xp }: { xp: number }) => {
const nav = useNavigation();
return (
<Card w="100%" onPress={() => nav.navigate('User', {})}>
<Card w="100%" onPress={() => nav.navigate('User')}>
<Stack padding={4} space={2} direction="row" alignItems="center">
<UserAvatar />
<VStack alignItems={'center'} flexGrow={1} space={2}>

View File

@@ -108,7 +108,7 @@ const ScoreModal = (props: ScoreModalProps) => {
onPress={() =>
navigation.canGoBack()
? navigation.goBack()
: navigation.navigate('Home', {})
: navigation.navigate('Home')
}
/>
</Row>

View File

@@ -5,7 +5,7 @@ import API from '../API';
import Song from '../models/Song';
import SongRow from '../components/SongRow';
import { Key } from 'react';
import { RouteProps, useNavigation } from '../Navigation';
import { useNavigation } from '../Navigation';
import { ImageBackground } from 'react-native';
import { useLikeSongMutation } from '../utils/likeSongMutation';
@@ -13,7 +13,7 @@ type ArtistDetailsViewProps = {
artistId: number;
};
const ArtistDetailsView = ({ artistId }: RouteProps<ArtistDetailsViewProps>) => {
const ArtistDetailsView = ({ artistId }: ArtistDetailsViewProps) => {
const artistQuery = useQuery(API.getArtist(artistId));
const songsQuery = useQuery(API.getSongsByArtist(artistId));
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });

View File

@@ -8,7 +8,7 @@ const ErrorView = () => {
<Center style={{ flexGrow: 1 }}>
<VStack space={3} alignItems="center">
<Translate translationKey="anErrorOccured" />
<Button onPress={() => navigation.navigate('Home', {})}>
<Button onPress={() => navigation.navigate('Home')}>
<Translate translationKey="goBackHome" />
</Button>
</VStack>

View File

@@ -12,7 +12,7 @@ const ForgotPasswordView = () => {
route: `/auth/forgot-password?email=${email}`,
method: 'PUT',
});
navigation.navigate('Home', {});
navigation.navigate('Home');
return 'email sent';
} catch {
return 'Error with email, please contact support';

View File

@@ -1,7 +1,7 @@
import { Flex, Heading, useBreakpointValue, ScrollView } from 'native-base';
import { useQuery } from '../Queries';
import { LoadingView } from '../components/Loading';
import { RouteProps, useNavigation } from '../Navigation';
import { useNavigation } from '../Navigation';
import API from '../API';
import CardGridCustom from '../components/CardGridCustom';
import SongCard from '../components/SongCard';
@@ -11,7 +11,7 @@ type GenreDetailsViewProps = {
genreId: number;
};
const GenreDetailsView = ({ genreId }: RouteProps<GenreDetailsViewProps>) => {
const GenreDetailsView = ({ genreId }: GenreDetailsViewProps) => {
const genreQuery = useQuery(API.getGenre(genreId));
const songsQuery = useQuery(API.getSongsByGenre(genreId, ['artist']));
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });

View File

@@ -1,17 +0,0 @@
import React from 'react';
import { Provider } from 'react-redux';
import TestRenderer from 'react-test-renderer';
import store from '../state/Store';
import HomeView from '../views/HomeView';
describe('<HomeView />', () => {
it('has 2 children', () => {
const tree = TestRenderer.create(
<Provider store={store}>
<HomeView />
</Provider>
).toJSON();
expect(tree.children.length).toBe(2);
});
});

View File

@@ -1,137 +0,0 @@
import React from 'react';
import { useQuery } from '../Queries';
import API from '../API';
import { LoadingView } from '../components/Loading';
import { Box, Flex, Stack, Heading, VStack, HStack } from 'native-base';
import { RouteProps, useNavigation } from '../Navigation';
import SongCardGrid from '../components/SongCardGrid';
import CompetenciesTable from '../components/CompetenciesTable';
import Translate from '../components/Translate';
import TextButton from '../components/TextButton';
import { FontAwesome5 } from '@expo/vector-icons';
// eslint-disable-next-line @typescript-eslint/ban-types
const HomeView = (props: RouteProps<{}>) => {
const navigation = useNavigation();
const userQuery = useQuery(API.getUserInfo);
const playHistoryQuery = useQuery(API.getUserPlayHistory(['artist']));
const searchHistoryQuery = useQuery(API.getSearchHistory(0, 10));
const skillsQuery = useQuery(API.getUserSkills);
const nextStepQuery = useQuery(API.getSongSuggestions(['artist']));
if (
!userQuery.data ||
!skillsQuery.data ||
!searchHistoryQuery.data ||
!playHistoryQuery.data
) {
return <LoadingView />;
}
return (
<Stack direction={{ base: 'column', lg: 'row' }} space={5} paddingTop={5}>
<VStack flex={{ lg: 2 }} space={5}>
<SongCardGrid
heading={<Translate translationKey="goNextStep" />}
songs={
nextStepQuery.data?.map((song) => ({
cover: song.cover,
name: song.name,
songId: song.id,
artistName: song.artist!.name,
})) ?? []
}
/>
<Stack direction={{ base: 'column', lg: 'row' }}>
<Box flex={{ lg: 1 }}>
<Heading>
<Translate translationKey="mySkillsToImprove" />
</Heading>
<Box padding={5}>
<CompetenciesTable {...skillsQuery.data} />
</Box>
</Box>
<Box flex={{ lg: 1 }}>
<SongCardGrid
heading={<Translate translationKey="recentlyPlayed" />}
songs={
playHistoryQuery.data
?.map((x) => x.song)
.map((song) => ({
cover: song!.cover,
name: song!.name,
songId: song!.id,
artistName: song!.artist!.name,
})) ?? []
}
/>
</Box>
</Stack>
</VStack>
<VStack flex={{ lg: 1 }} height={{ lg: '100%' }} alignItems="center">
<HStack width="100%" justifyContent="space-evenly" p={5} space={5}>
<TextButton
translate={{ translationKey: 'searchBtn' }}
colorScheme="secondary"
size="sm"
onPress={() => navigation.navigate('Search', {})}
/>
<TextButton
translate={{ translationKey: 'settingsBtn' }}
colorScheme="gray"
size="sm"
onPress={() => navigation.navigate('Settings', {})}
/>
<TextButton
translate={{ translationKey: 'leaderboardTitle' }}
colorScheme="primary"
size="sm"
onPress={() => navigation.navigate('Leaderboard', {})}
/>
<TextButton
label={'V2'}
colorScheme="gray"
size="sm"
onPress={() => navigation.navigate('Home', {})}
/>
</HStack>
<Box style={{ width: '100%' }}>
<Heading>
<Translate translationKey="recentSearches" />
</Heading>
<Flex
padding={3}
style={{
width: '100%',
alignItems: 'flex-start',
alignContent: 'flex-start',
flexDirection: 'row',
flexWrap: 'wrap',
}}
>
{searchHistoryQuery.data?.length === 0 && (
<Translate translationKey="noRecentSearches" />
)}
{[...new Set(searchHistoryQuery.data.map((x) => x.query))]
.slice(0, 5)
.map((query) => (
<TextButton
leftIcon={<FontAwesome5 name="search" size={16} />}
style={{
margin: 2,
}}
key={query}
variant="solid"
size="xs"
colorScheme="primary"
label={query}
onPress={() => navigation.navigate('Search', {})}
/>
))}
</Flex>
</Box>
</VStack>
</Stack>
);
};
export default HomeView;

View File

@@ -3,12 +3,12 @@ import { SafeAreaView, View } from 'react-native';
import { useQuery } from '../Queries';
import API from '../API';
import { LoadingView } from '../components/Loading';
import { useNavigation, RouteProps } from '../Navigation';
import { useNavigation } from '../Navigation';
import { translate } from '../i18n/i18n';
import { PodiumCard } from '../components/leaderboard/PodiumCard';
import { BoardRow } from '../components/leaderboard/BoardRow';
const Leaderboardiew = (props: RouteProps<Record<string, never>>) => {
const Leaderboardiew = () => {
const navigation = useNavigation();
const scoresQuery = useQuery(API.getTopTwentyPlayers());
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });

View File

@@ -11,7 +11,7 @@ import {
} from 'react-native-tab-view';
import { Heart, Clock, StatusUp, FolderCross } from 'iconsax-react-native';
import { Scene } from 'react-native-tab-view/lib/typescript/src/types';
import { RouteProps, useNavigation } from '../Navigation';
import { useNavigation } from '../Navigation';
import { Translate, TranslationKey } from '../i18n/i18n';
import MusicList from '../components/UI/MusicList';
import { useQuery } from '../Queries';
@@ -109,7 +109,7 @@ const getTabData = (key: string) => {
}
};
const MusicTab = (props: RouteProps<object>) => {
const MusicTab = () => {
const layout = useWindowDimensions();
const [index, setIndex] = React.useState(0);
const { colors } = useTheme();

View File

@@ -17,7 +17,7 @@ const PasswordResetView = () => {
password,
},
});
navigation.navigate('Home', {});
navigation.navigate('Home');
return 'password succesfully reset';
} catch {
return 'password reset failed';

View File

@@ -12,7 +12,7 @@ import Animated, {
} from 'react-native-reanimated';
import * as ScreenOrientation from 'expo-screen-orientation';
import { Text, Row, View, useToast } from 'native-base';
import { RouteProps, useNavigation } from '../Navigation';
import { useNavigation } from '../Navigation';
import { useQuery } from '../Queries';
import API from '../API';
import { LoadingView } from '../components/Loading';
@@ -68,7 +68,7 @@ function parseMidiMessage(message: MIDIMessageEvent) {
};
}
const PlayView = ({ songId, route }: RouteProps<PlayViewProps>) => {
const PlayView = ({ songId }: PlayViewProps) => {
const [playType, setPlayType] = useState<'practice' | 'normal' | null>(null);
const accessToken = useSelector((state: RootState) => state.user.accessToken);
const navigation = useNavigation();
@@ -283,7 +283,7 @@ const PlayView = ({ songId, route }: RouteProps<PlayViewProps>) => {
useEffect(() => {
// Song.data is updated on navigation.navigate (do not know why)
// Hotfix to prevent midi setup process from reruning on game end
if (navigation.getState().routes.at(-1)?.name != route.name) {
if (navigation.getState().routes.at(-1)?.name != "Play") {
return;
}
if (playType && song.data && !webSocket.current) {

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useWindowDimensions } from 'react-native';
import { Column, Flex, Progress, Row, Text, View, useTheme } from 'native-base';
import { RouteProps, useNavigation } from '../Navigation';
import { useNavigation } from '../Navigation';
import UserAvatar from '../components/UserAvatar';
import { LoadingView } from '../components/Loading';
import { useQuery } from '../Queries';
@@ -18,8 +18,7 @@ function xpToProgressBarValue(xp: number): number {
return Math.floor(xp / 10);
}
// eslint-disable-next-line @typescript-eslint/ban-types
const ProfileView = (props: RouteProps<{}>) => {
const ProfileView = () => {
const layout = useWindowDimensions();
const navigation = useNavigation();
const userQuery = useQuery(API.getUserInfo);
@@ -77,7 +76,7 @@ const ProfileView = (props: RouteProps<{}>) => {
<ButtonBase
title={translate('updateProfile')}
type={'filled'}
onPress={async () => navigation.navigate('Settings', {})}
onPress={async () => navigation.navigate('Settings')}
/>
</View>
<Translate

View File

@@ -9,7 +9,6 @@ import { SearchResultComponent } from '../components/SearchResult';
import { SafeAreaView } from 'react-native';
import { Filter } from '../components/SearchBar';
import { ScrollView } from 'native-base';
import { RouteProps } from '../Navigation';
import LikedSong from '../models/LikedSong';
interface SearchContextType {
@@ -46,7 +45,7 @@ type SearchViewProps = {
query?: string;
};
const SearchView = (props: RouteProps<SearchViewProps>) => {
const SearchView = (props: SearchViewProps) => {
const [filter, setFilter] = useState<Filter>('all');
const [stringQuery, setStringQuery] = useState<string>(props?.query ?? '');

View File

@@ -4,7 +4,7 @@ import React from 'react';
import { useQuery } from '../../Queries';
import SongCardInfo from '../../components/V2/SongCardInfo';
import API from '../../API';
import { RouteProps, useNavigation } from '../../Navigation';
import { useNavigation } from '../../Navigation';
import GoldenRatio from '../../components/V2/GoldenRatio';
const HomeView = () => {

View File

@@ -1,11 +1,8 @@
import React from 'react';
import SearchBarComponent from '../../components/V2/SearchBar';
import { RouteProps } from '../../Navigation';
import SearchHistory from '../../components/V2/SearchHistory';
import { View } from 'react-native';
// eslint-disable-next-line @typescript-eslint/ban-types
const SearchView = (props: RouteProps<{}>) => {
const SearchView = () => {
return (
<View style={{ display: 'flex', gap: 50 }}>
<SearchBarComponent />

View File

@@ -17,7 +17,7 @@ const VerifiedView = () => {
route: `/auth/verify?token=${(route.params as any).token}`,
method: 'PUT',
});
navigation.navigate('Home', {});
navigation.navigate('Home');
} catch {
setFailed(true);
}

View File

@@ -14,7 +14,6 @@ import {
} from 'react-native-tab-view';
import { HeartEdit, UserEdit, SecurityUser, FolderCross } from 'iconsax-react-native';
import { Scene } from 'react-native-tab-view/lib/typescript/src/types';
import { RouteProps } from '../../Navigation';
import { translate } from '../../i18n/i18n';
const renderScene = SceneMap({
@@ -36,8 +35,7 @@ const getTabData = (key: string) => {
}
};
// eslint-disable-next-line @typescript-eslint/ban-types
const SettingsTab = (props: RouteProps<{}>) => {
const SettingsTab = () => {
const layout = useWindowDimensions();
const [index, setIndex] = React.useState(0);
const { colors } = useTheme();