Fix types
This commit is contained in:
@@ -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>>();
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -108,7 +108,7 @@ const ScoreModal = (props: ScoreModalProps) => {
|
||||
onPress={() =>
|
||||
navigation.canGoBack()
|
||||
? navigation.goBack()
|
||||
: navigation.navigate('Home', {})
|
||||
: navigation.navigate('Home')
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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' });
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -17,7 +17,7 @@ const PasswordResetView = () => {
|
||||
password,
|
||||
},
|
||||
});
|
||||
navigation.navigate('Home', {});
|
||||
navigation.navigate('Home');
|
||||
return 'password succesfully reset';
|
||||
} catch {
|
||||
return 'password reset failed';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ?? '');
|
||||
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user