import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, ILinkEventTracker, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { AchievementCategory, AddEventLinkTracker, GetAchievementCategoryImageUrl, GetAchievementIsIgnored, LocalizeText, RemoveLinkEventTracker, SendMessageComposer } from '../../api'; import { Base, Column, LayoutImage, LayoutProgressBar, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../common'; import { AchievementsUIUnseenCountEvent } from '../../events'; import { BatchUpdates, DispatchUiEvent, UseMessageEventHook } from '../../hooks'; import { AchievementCategoryView } from './views/AchievementCategoryView'; import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView'; 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 onAchievementEvent = useCallback((event: AchievementEvent) => { const parser = event.getParser(); const achievement = parser.achievement; const categoryName = achievement.category; setAchievementCategories(prevValue => { const newValue = [ ...prevValue ]; const categoryIndex = newValue.findIndex(existing => (existing.code === categoryName)); if(categoryIndex === -1) { const category = new AchievementCategory(categoryName); category.achievements.push(achievement); newValue.push(category); } else { const category = newValue[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(!GetAchievementIsIgnored(achievement)) { achievement.unseen++; if(previousAchievement) achievement.unseen += previousAchievement.unseen; } category.achievements = newAchievements; } return newValue; }); }, []); 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 setAchievementSeen = useCallback((code: string, achievementId: number) => { setAchievementCategories(prevValue => { const newValue = [ ...prevValue ]; for(const category of newValue) { if(category.code !== code) continue; for(const achievement of category.achievements) { if(achievement.achievementId !== achievementId) continue; achievement.unseen = 0; } } return newValue; }); }, []); const linkReceived = useCallback((url: string) => { const parts = url.split('/'); if(parts.length < 2) return; switch(parts[1]) { case 'show': setIsVisible(true); return; case 'hide': setIsVisible(false); return; case 'toggle': setIsVisible(prevValue => !prevValue); return; } }, []); useEffect(() => { const linkTracker: ILinkEventTracker = { linkReceived, eventUrlPrefix: 'achievements/' }; AddEventLinkTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); }, [ linkReceived ]); 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.score', [ 'score' ], [ achievementScore.toString() ]) } } { getSelectedCategory && } ); };