Skip to content

Commit

Permalink
fix: leaderboard crash and other fixes (#61)
Browse files Browse the repository at this point in the history
* fix: leaderboard crash and other fixes

* chore: update for aoe 4

* feat: added player tounrment info to player page
  • Loading branch information
noahbrandyberry committed May 4, 2024
1 parent d4d78f1 commit 7aabf55
Show file tree
Hide file tree
Showing 30 changed files with 1,121 additions and 962 deletions.
4 changes: 2 additions & 2 deletions app/assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
"main.profile.recentmatches.heading": "Recent matches",
"main.profile.follow": "Follow",
"main.profile.unfollow": "Unfollow",
"main.matches.search.placeholder": "player",
"main.matches.search.placeholder": "Search by player",
"main.matches.withme": "me",
"main.matches.matches": "{matches} matches",

Expand Down Expand Up @@ -233,7 +233,7 @@
"search.title": "Search",
"search.nouserfound": "No user found.",
"search.condition.1": "A user must have played at least 10 games in a timespan of about 3 months to be found.",
"search.minlength": "Enter at least 3 chars.",
"search.minlength": "Enter at least 2 chars.",
"search.placeholder": "Search by username, steam id, profile id",
"search.show": "Show",
"search.heading.name": "Name",
Expand Down
8 changes: 8 additions & 0 deletions app/src/api/tournaments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ export const useTournamentDetail = (id: string, enabled?: boolean) =>
enabled: Platform.OS === 'web' ? false : enabled,
});

export const useTournamentPlayer = (id?: string) =>
useQuery({
queryKey: ['player', id],
staleTime: 120000,
queryFn: async () => await liquipedia.aoe.getPlayer(id ?? ''),
enabled: Platform.OS === 'web' ? false : !!id,
});

export const useTournamentMatches = (enabled?: boolean) => {
const { data: upcomingTournaments, isLoading: isLoadingTournaments } = useUpcomingTournaments();
const { data, isLoading, ...query } = useQuery<Match[]>({
Expand Down
269 changes: 140 additions & 129 deletions app/src/app/competitive/tournaments/[id].tsx

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions app/src/app/competitive/tournaments/all.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { useAppTheme } from '@app/theming';
import { DismissKeyboard } from '@app/view/components/dismiss-keyboard';
import RefreshControlThemed from '@app/view/components/refresh-control-themed';
import { TournamentCard } from '@app/view/tournaments/tournament-card';
import { appConfig } from '@nex/dataset';
import { Stack, useLocalSearchParams } from 'expo-router';
import { TournamentCategory } from 'liquipedia';
import { GameVersion, TournamentCategory } from 'liquipedia';
import { useMemo, useState } from 'react';
import { Platform, StyleSheet, View } from 'react-native';

Expand All @@ -35,8 +36,9 @@ export default function AllTournaments() {
...tournamentsSection,
data: tournamentsSection.data.filter(
(tournament) =>
transformSearch(tournament.name).includes(transformSearch(search)) ||
tournamentAbbreviation(tournament.name).includes(transformSearch(search))
tournament.game === (appConfig.game === 'aoe2de' ? GameVersion.Age2 : GameVersion.Age4) &&
(transformSearch(tournament.name).includes(transformSearch(search)) ||
tournamentAbbreviation(tournament.name).includes(transformSearch(search)))
),
};
})
Expand All @@ -57,7 +59,7 @@ export default function AllTournaments() {
<View style={styles.container}>
<View style={styles.searchContainer}>
<Dropdown
style={{ minWidth: 100 }}
style={{ minWidth: 100, height: 45 }}
onChange={setCategory}
options={sortedTiers.map((tier) => ({ value: tier, label: formatTier(tier), abbreviated: formatTierShort(category) }))}
value={category}
Expand Down
212 changes: 79 additions & 133 deletions app/src/app/matches/users/[profileId].tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import React, { useEffect } from 'react';
import { Alert, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
import { RootStackParamList, RootStackProp } from '../../../../App2';
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { MainPageInner } from '@app/view/main.page';
import { createStylesheet } from '../../../theming-new';
import { setAuth, setPrefValue, useMutate, useSelector } from '../../../redux/reducer';
import { useCavy } from '@app/view/testing/tester';
import { clearSettingsInStorage, saveCurrentPrefsToStorage, saveSettingsToStorage } from '../../../service/storage';
import { IProfilesResult, IProfilesResultProfile } from '@app/api/helper/api.types';
import { Icon } from '@app/components/icon';
import { toggleFollowing } from '@app/service/following';
import Search from '@app/view/components/search';
import { getTranslation } from '../../../helper/translate';
import { FontAwesome5 } from '@expo/vector-icons';
import { getRootNavigation } from '../../../service/navigation';
import { MainPageInner } from '@app/view/main.page';
import { Stack, router, useLocalSearchParams } from 'expo-router';
import React, { useEffect, useState } from 'react';
import { ActivityIndicator, Alert, Platform, TouchableOpacity } from 'react-native';

import { setAccountProfile } from '../../../api/following';
import { openLink } from '../../../helper/url';
import { fetchProfiles } from '../../../api/helper/api';
import { router } from 'expo-router';
import { getTranslation } from '../../../helper/translate';
import { setAuth, setFollowing, setPrefValue, useMutate, useSelector } from '../../../redux/reducer';
import { clearSettingsInStorage, saveCurrentPrefsToStorage, saveSettingsToStorage } from '../../../service/storage';
import { HeaderTitle } from '@app/components/header-title';
import { CountryImage } from '../../../view/components/country-image';
import { getVerifiedPlayer, isVerifiedPlayer } from '@nex/data';

export function UserMenu() {
const styles = useStyles();
const route = useRoute<RouteProp<RootStackParamList, 'User'>>();
const profileId = route.params.profileId;
const { profileId: profileIdParam } = useLocalSearchParams<{ profileId: string }>();
const profileId = Number(profileIdParam ?? 0);
const auth = useSelector((state) => state.auth!);
const account = useSelector((state) => state.account);
const profile = useSelector((state) => state.user[profileId]?.profile);
const steamProfileUrl = 'https://steamcommunity.com/profiles/' + profile?.steamId;
const xboxProfileUrl = 'https://www.ageofempires.com/stats/?game=age2&profileId=' + profile?.profileId;
const following = useSelector((state) => state.following);
const [isFollowingLoading, setIsFollowingLoading] = useState<boolean>(false);
const followingThisUser = !!following.find((f) => profile && f.profileId === profile.profileId);

const mutate = useMutate();

Expand Down Expand Up @@ -53,45 +53,47 @@ export function UserMenu() {
setAccountProfile(account.id, { profile_id: null, steam_id: null });
};

return (
<View style={styles.menu}>
{!!profile?.profileId && (
<TouchableOpacity style={styles.menuButton} onPress={() => openLink(xboxProfileUrl)}>
<FontAwesome5 style={styles.menuIcon} name="xbox" size={20} />
</TouchableOpacity>
)}
{!!profile?.steamId && (
<TouchableOpacity style={styles.menuButton} onPress={() => openLink(steamProfileUrl)}>
<FontAwesome5 style={styles.menuIcon} name="steam" size={20} />
</TouchableOpacity>
)}
{!!profile?.profileId && Number(profileId) === Number(auth?.profileId) && (
<TouchableOpacity style={styles.menuButton} onPress={deleteUser}>
<FontAwesome5 style={styles.menuIcon} name="user-times" size={16} />
</TouchableOpacity>
)}
</View>
);
const ToggleFollowing = async () => {
setIsFollowingLoading(true);
const following = await toggleFollowing(profile!);
if (following) {
setIsFollowingLoading(false);
mutate(setFollowing(following));
}
};

if (!profile) {
return null;
}

if (Number(profileId) === Number(auth?.profileId)) {
return (
<TouchableOpacity onPress={deleteUser}>
<Icon icon="user-times" size={20} color="subtle" />
</TouchableOpacity>
);
} else {
return isFollowingLoading ? (
<ActivityIndicator size={20} />
) : (
<TouchableOpacity hitSlop={10} onPress={ToggleFollowing}>
<Icon prefix={followingThisUser ? 'fass' : 'fasr'} icon="heart" size={20} color="text-[#ef4444]" />
</TouchableOpacity>
);
}
}

export default function UserPage() {
const route = useRoute<RouteProp<RootStackParamList, 'User'>>();
const profileId = route.params?.profileId;
const styles = useStyles();

console.log('==> UserPage', profileId);
const { profileId: profileIdParam, name, country } = useLocalSearchParams<{ profileId: string; name?: string; country?: string }>();
const profileId = Number(profileIdParam ?? 0);

const mutate = useMutate();
const [loadedProfile, setLoadedProfile] = useState<IProfilesResultProfile>();
const auth = useSelector((state) => state.auth);
const account = useSelector((state) => state.account);
const profile = useSelector((state) => state.user[profileId]?.profile);

// console.log('==> UserPage');
// console.log(route.params);
// console.log(auth);
// console.log(profileId);

const navigation = useNavigation<RootStackProp>();
const isVerified = isVerifiedPlayer(profileId);
const verified = getVerifiedPlayer(profileId);
const isMainAccount = verified?.platforms.rl?.[0] === profileIdParam;

const onSelect = async (user: any) => {
await saveSettingsToStorage({
Expand All @@ -110,13 +112,6 @@ export default function UserPage() {
}
}, [auth]);

// useEffect(() => {
// navigation.setOptions({ title: profile?.name || ' ' });
// console.log('PROFILE UPDATED', profile?.name);
// }, [profile]);

// When user is not set but we have auth

const navigateToAuthUser = async () => {
console.log('==> NAVIGATE');
// @ts-ignore
Expand All @@ -130,94 +125,45 @@ export default function UserPage() {
}
}, [auth]);

// When name is not set yet

// useEffect(() => {
// if (profile != null) {
// // @ts-ignore
// navigation.setParams({
// profileId: profile.profileId,
// });
// }
// }, [profile]);

// When visiting user page with only profileId / steamId

const completeUserIdInfo = async () => {
// console.log('completeUserIdInfo');

const loadedProfile = await fetchProfiles({ profileId: profileId });
if (loadedProfile) {
const name = loadedProfile?.profiles?.[0]?.name;
navigation.setOptions({ title: name || ' ', headerRight: () => <UserMenu /> });
// console.log('PROFILE UPDATED', name);
// mutate(state => {
// set(state.cache, ['profile', profileId, 'name'], loadedProfile.profiles[0].name);
// });
const loaded = await fetchProfiles({ profileId });
if (loaded) {
setLoadedProfile(loaded.profiles[0]);
}
// console.log(loadedProfile);

// if (!loadedProfile?.steamId) {
// setHasSteamId(false);
// }

// if (loadedProfile) {
// // @ts-ignore
// navigation.setParams({
// profileId: loadedProfile.profileId,
// name: loadedProfile?.name,
// });
// }
};

useEffect(() => {
// const hasInCache = false;
// if (!hasInCache) {
completeUserIdInfo();
// }
}, [profileId]);

// if (profileId) {
// if (profileId == null) {
// return <View style={styles.container}><MyText>Loading profile by Steam ID...</MyText></View>;
// }
// if (user.steamId == null && hasSteamId) {
// return <View style={styles.container}><MyText>Loading profile by profile ID...</MyText></View>;
// }
// }

if (profileId) {
return <MainPageInner profileId={profileId} />;
return (
<>
<Stack.Screen
options={{
headerTitle: () => (
<HeaderTitle
iconComponent={
<CountryImage
style={{ transform: [{ scale: 1.5 }] }}
country={getVerifiedPlayer(profileId)?.country || loadedProfile?.country || country}
/>
}
title={loadedProfile?.name || name || ''}
subtitle={isVerified && !isMainAccount && `${getVerifiedPlayer(profileId)?.name} - Alternate account`}
/>
),
headerRight: () => <UserMenu />,
}}
/>
<MainPageInner profileId={profileId} />
</>
);
}

if (auth == null) {
return <Search title="Enter your AoE username to track your games:" selectedUser={onSelect} actionText="Choose" />;
}

return <MainPageInner profileId={profileId} />;
return null;
}

const useStyles = createStylesheet((theme) =>
StyleSheet.create({
container: {
padding: 20,
},
menu: {
// backgroundColor: 'red',
flexDirection: 'row',
// flex: 1,
// marginRight: 10,
},
menuButton: {
// backgroundColor: 'blue',
width: 35,
justifyContent: 'center',
alignItems: 'center',
margin: 0,
marginHorizontal: 2,
},
menuIcon: {
color: theme.textNoteColor,
},
})
);
2 changes: 1 addition & 1 deletion app/src/app/matches/users/follow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function Follow() {

return (
<Search
selectedUser={(user) => router.navigate(`/matches/users/${user.profileId}`)}
selectedUser={(user) => router.navigate(`/matches/users/${user.profileId}?name=${user.name}&country=${user.country}`)}
action={(user: IProfilesResultProfile) => <FeedAction user={user} />}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion app/src/app/matches/users/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function SearchPage() {
return (
<>
<Stack.Screen options={{ title: 'Find Player' }} />
<Search selectedUser={(user) => router.navigate(`/matches/users/${user.profileId}`)} />
<Search selectedUser={(user) => router.navigate(`/matches/users/${user.profileId}?name=${user.name}&country=${user.country}`)} />
</>
);
}
11 changes: 4 additions & 7 deletions app/src/app/matches/users/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const SelectProfilePage = () => {
});
mutate(setAuth(user));
setAccountProfile(account.id, { profile_id: user.profileId!, steam_id: user.steamId });
router.navigate(`/matches/users/${user.profileId!}`);
router.navigate(`/matches/users/${user.profileId!}?name=${user.name}&country=${user.country}`);
};

const navigation = useNavigation();
Expand All @@ -28,12 +28,9 @@ const SelectProfilePage = () => {
navigation.setOptions({ title: 'Find My Account' });
}, [navigation]);

return <Search
title="Enter your AoE username to track your games"
selectedUser={onSelect}
actionText="Choose"
initialText={route.params?.search}
/>;
return (
<Search title="Enter your AoE username to track your games" selectedUser={onSelect} actionText="Choose" initialText={route.params?.search} />
);
};

export default SelectProfilePage;
Loading

0 comments on commit 7aabf55

Please sign in to comment.