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([]); const [ selectedCategoryCode, setSelectedCategoryCode ] = useState(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('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 ( setIsVisible(false) } /> { getSelectedCategory && setSelectedCategoryCode(null) } className="nitro-achievements-back-arrow" /> { LocalizeText(`quests.${ getSelectedCategory.code }.name`) } { LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ getSelectedCategory.getProgress().toString(), getSelectedCategory.getMaxProgress().toString() ]) } } { !getSelectedCategory && <> { LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } { LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) } } { getSelectedCategory && } ); };