nitro-react/src/components/achievements/AchievementsView.tsx
2022-03-04 01:29:14 -05:00

241 lines
9.5 KiB
TypeScript

import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { GetConfiguration, LocalizeText, SendMessageComposer } from '../../api';
import { Base, Column, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../common';
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events';
import { BatchUpdates, DispatchUiEvent, UseMessageEventHook, UseUiEvent } from '../../hooks';
import { AchievementCategory } from './common/AchievementCategory';
import { AchievementUtilities } from './common/AchievementUtilities';
import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView';
import { AchievementCategoryView } from './views/category/AchievementCategoryView';
export const AchievementsView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ isInitalized, setIsInitalized ] = useState(false);
const [ achievementCategories, setAchievementCategories ] = useState<AchievementCategory[]>([]);
const [ selectedCategoryCode, setSelectedCategoryCode ] = useState<string>(null);
const [ achievementScore, setAchievementScore ] = useState(0);
const onAchievementsUIEvent = useCallback((event: AchievementsUIEvent) =>
{
switch(event.type)
{
case AchievementsUIEvent.SHOW_ACHIEVEMENTS:
setIsVisible(true);
return;
case AchievementsUIEvent.HIDE_ACHIEVEMENTS:
setIsVisible(false);
return;
case AchievementsUIEvent.TOGGLE_ACHIEVEMENTS:
setIsVisible(value => !value);
return;
}
}, []);
UseUiEvent(AchievementsUIEvent.SHOW_ACHIEVEMENTS, onAchievementsUIEvent);
UseUiEvent(AchievementsUIEvent.HIDE_ACHIEVEMENTS, onAchievementsUIEvent);
UseUiEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS, onAchievementsUIEvent);
const onAchievementEvent = useCallback((event: AchievementEvent) =>
{
const parser = event.getParser();
const achievement = parser.achievement;
const newCategories = [ ...achievementCategories ];
const categoryName = achievement.category;
const categoryIndex = newCategories.findIndex(existing => (existing.code === categoryName));
if(categoryIndex === -1)
{
const category = new AchievementCategory(categoryName);
category.achievements.push(achievement);
newCategories.push(category);
}
else
{
const category = newCategories[categoryIndex];
const newAchievements = [ ...category.achievements ];
const achievementIndex = newAchievements.findIndex(existing => (existing.achievementId === achievement.achievementId));
let previousAchievement: AchievementData = null;
if(achievementIndex === -1)
{
newAchievements.push(achievement);
}
else
{
previousAchievement = newAchievements[achievementIndex];
newAchievements[achievementIndex] = achievement;
}
if(!AchievementUtilities.isIgnoredAchievement(achievement))
{
achievement.unseen++;
if(previousAchievement) achievement.unseen += previousAchievement.unseen;
}
category.achievements = newAchievements;
}
setAchievementCategories(newCategories);
}, [ achievementCategories ]);
UseMessageEventHook(AchievementEvent, onAchievementEvent);
const onAchievementsEvent = useCallback((event: AchievementsEvent) =>
{
const parser = event.getParser();
const categories: AchievementCategory[] = [];
for(const achievement of parser.achievements)
{
const categoryName = achievement.category;
let existing = categories.find(category => (category.code === categoryName));
if(!existing)
{
existing = new AchievementCategory(categoryName);
categories.push(existing);
}
existing.achievements.push(achievement);
}
BatchUpdates(() =>
{
setAchievementCategories(categories);
setIsInitalized(true);
});
}, []);
UseMessageEventHook(AchievementsEvent, onAchievementsEvent);
const onAchievementsScoreEvent = useCallback((event: AchievementsScoreEvent) =>
{
const parser = event.getParser();
setAchievementScore(parser.score);
}, []);
UseMessageEventHook(AchievementsScoreEvent, onAchievementsScoreEvent);
const getTotalUnseen = useMemo(() =>
{
let unseen = 0;
for(const category of achievementCategories)
{
for(const achievement of category.achievements) unseen += achievement.unseen;
}
return unseen;
}, [ achievementCategories ]);
const getProgress = useMemo(() =>
{
let progress = 0;
for(const category of achievementCategories) progress += category.getProgress();
return progress;
}, [ achievementCategories ]);
const getMaxProgress = useMemo(() =>
{
let progress = 0;
for(const category of achievementCategories) progress += category.getMaxProgress();
return progress;
}, [ achievementCategories ]);
const scaledProgressPercent = useMemo(() =>
{
return ~~((((getProgress - 0) * (100 - 0)) / (getMaxProgress - 0)) + 0);
}, [ getProgress, getMaxProgress ]);
const getSelectedCategory = useMemo(() =>
{
if(!achievementCategories || !achievementCategories.length) return null;
return achievementCategories.find(existing => (existing.code === selectedCategoryCode));
}, [ achievementCategories, selectedCategoryCode ]);
const getCategoryIcon = useMemo(() =>
{
if(!getSelectedCategory) return null;
const imageUrl = GetConfiguration<string>('achievements.images.url');
return imageUrl.replace('%image%', `achicon_${ getSelectedCategory.code }`);
}, [ getSelectedCategory ]);
const setAchievementSeen = useCallback((code: string, achievementId: number) =>
{
const newCategories = [ ...achievementCategories ];
for(const category of newCategories)
{
if(category.code !== code) continue;
for(const achievement of category.achievements)
{
if(achievement.achievementId !== achievementId) continue;
achievement.unseen = 0;
}
}
setAchievementCategories(newCategories);
}, [ achievementCategories ]);
useEffect(() =>
{
if(!isVisible || !isInitalized) return;
SendMessageComposer(new RequestAchievementsMessageComposer());
}, [ isVisible, isInitalized ]);
useEffect(() =>
{
DispatchUiEvent(new AchievementsUIUnseenCountEvent(getTotalUnseen));
}, [ getTotalUnseen ]);
if(!isVisible || !isInitalized) return null;
return (
<NitroCardView uniqueKey="achievements" className="nitro-achievements" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
{ getSelectedCategory &&
<NitroCardSubHeaderView position="relative" className="justify-content-center align-items-center cursor-pointer" gap={ 3 }>
<Base onClick={ event => setSelectedCategoryCode(null) } className="nitro-achievements-back-arrow" />
<Column grow gap={ 0 }>
<Text fontSize={ 4 } fontWeight="bold" className="text-small">{ LocalizeText(`quests.${ getSelectedCategory.code }.name`) }</Text>
<Text>{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ getSelectedCategory.getProgress().toString(), getSelectedCategory.getMaxProgress().toString() ]) }</Text>
</Column>
</NitroCardSubHeaderView> }
<NitroCardContentView>
{ !getSelectedCategory &&
<>
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
<Column grow justifyContent="end">
<Base className="progress" position="relative">
<Flex fit center position="absolute" className="text-black">{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) }</Flex>
<Base className="progress-bar bg-success" style={ { width: (scaledProgressPercent + '%') }} />
</Base>
<Text className="bg-muted rounded p-1" center>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
</Column>
</> }
{ getSelectedCategory &&
<AchievementCategoryView category={ getSelectedCategory } setAchievementSeen={ setAchievementSeen } /> }
</NitroCardContentView>
</NitroCardView>
);
};