mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Merge branch '@feature/achievements' into dev
This commit is contained in:
commit
770ea994f8
8
src/events/achievements/AchievementsUIEvent.ts
Normal file
8
src/events/achievements/AchievementsUIEvent.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { NitroEvent } from 'nitro-renderer';
|
||||||
|
|
||||||
|
export class AchievementsUIEvent extends NitroEvent
|
||||||
|
{
|
||||||
|
public static SHOW_ACHIEVEMENTS: string = 'AE_SHOW_ACHIEVEMENTS';
|
||||||
|
public static HIDE_ACHIEVEMENTS: string = 'AE_HIDE_ACHIEVEMENTS';
|
||||||
|
public static TOGGLE_ACHIEVEMENTS: string = 'AE_TOGGLE_ACHIEVEMENTS';
|
||||||
|
}
|
1
src/events/achievements/index.ts
Normal file
1
src/events/achievements/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './AchievementsUIEvent';
|
@ -16,3 +16,4 @@
|
|||||||
@import './toolbar/ToolbarView';
|
@import './toolbar/ToolbarView';
|
||||||
@import './wired/WiredView';
|
@import './wired/WiredView';
|
||||||
@import './mod-tools/ModToolsView';
|
@import './mod-tools/ModToolsView';
|
||||||
|
@import './achievements/AchievementsView';
|
||||||
|
70
src/views/achievements/AchievementsMessageHandler.tsx
Normal file
70
src/views/achievements/AchievementsMessageHandler.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { AchievementEvent, AchievementsEvent, AchievementsScoreEvent } from 'nitro-renderer';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { CreateMessageHook } from '../../hooks/messages';
|
||||||
|
import { IAchievementsMessageHandlerProps } from './AchievementsMessageHandler.types';
|
||||||
|
import { useAchievementsContext } from './context/AchievementsContext';
|
||||||
|
import { AchievementsActions } from './reducers/AchievementsReducer';
|
||||||
|
import { AchievementCategory } from './utils/AchievementCategory';
|
||||||
|
|
||||||
|
export const AchievementsMessageHandler: FC<IAchievementsMessageHandlerProps> = props =>
|
||||||
|
{
|
||||||
|
const { achievementsState = null, dispatchAchievementsState = null } = useAchievementsContext();
|
||||||
|
|
||||||
|
const onAchievementEvent = useCallback((event: AchievementEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
console.log(parser);
|
||||||
|
|
||||||
|
}, [ dispatchAchievementsState ]);
|
||||||
|
|
||||||
|
const onAchievementsEvent = useCallback((event: AchievementsEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
const categories: AchievementCategory[] = [];
|
||||||
|
|
||||||
|
for(const achievement of parser.achievements)
|
||||||
|
{
|
||||||
|
const categoryName = achievement.category;
|
||||||
|
|
||||||
|
const existing = categories.find(category => category.name === categoryName);
|
||||||
|
|
||||||
|
if(existing)
|
||||||
|
{
|
||||||
|
existing.achievements.push(achievement);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const category = new AchievementCategory(categoryName);
|
||||||
|
category.achievements.push(achievement);
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchAchievementsState({
|
||||||
|
type: AchievementsActions.SET_CATEGORIES,
|
||||||
|
payload: {
|
||||||
|
categories: categories
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ dispatchAchievementsState ]);
|
||||||
|
|
||||||
|
const onAchievementsScoreEvent = useCallback((event: AchievementsScoreEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
dispatchAchievementsState({
|
||||||
|
type: AchievementsActions.SET_SCORE,
|
||||||
|
payload: {
|
||||||
|
score: parser.score
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}, [ dispatchAchievementsState ]);
|
||||||
|
|
||||||
|
CreateMessageHook(AchievementEvent, onAchievementEvent);
|
||||||
|
CreateMessageHook(AchievementsEvent, onAchievementsEvent);
|
||||||
|
CreateMessageHook(AchievementsScoreEvent, onAchievementsScoreEvent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
@ -0,0 +1,2 @@
|
|||||||
|
export interface IAchievementsMessageHandlerProps
|
||||||
|
{}
|
52
src/views/achievements/AchievementsView.scss
Normal file
52
src/views/achievements/AchievementsView.scss
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
.nitro-achievements {
|
||||||
|
width: 650px;
|
||||||
|
height: 376px;
|
||||||
|
|
||||||
|
.score {
|
||||||
|
border-color: $grid-border-color !important;
|
||||||
|
background-color: $grid-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
border-color: $grid-border-color !important;
|
||||||
|
background-color: $grid-bg-color;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $grid-active-border-color !important;
|
||||||
|
background-color: $grid-active-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-score {
|
||||||
|
margin-top: 43.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievements {
|
||||||
|
height: 230px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
.achievement {
|
||||||
|
border-color: $grid-border-color !important;
|
||||||
|
background-color: $grid-bg-color;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $grid-active-border-color !important;
|
||||||
|
background-color: $grid-active-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.gray {
|
||||||
|
div {
|
||||||
|
filter: grayscale(1);
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/views/achievements/AchievementsView.tsx
Normal file
67
src/views/achievements/AchievementsView.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
||||||
|
import { AchievementsUIEvent } from '../../events/achievements';
|
||||||
|
import { useUiEvent } from '../../hooks/events';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
|
||||||
|
import { LocalizeText } from '../../utils/LocalizeText';
|
||||||
|
import { AchievementsMessageHandler } from './AchievementsMessageHandler';
|
||||||
|
import { AchievementsViewProps } from './AchievementsView.types';
|
||||||
|
import { AchievementsContextProvider } from './context/AchievementsContext';
|
||||||
|
import { AchievementsReducer, initialAchievements } from './reducers/AchievementsReducer';
|
||||||
|
import { AchievementCategoryView } from './views/category/AchievementCategoryView';
|
||||||
|
import { AchievementsListView } from './views/list/AchievementsListView';
|
||||||
|
|
||||||
|
export const AchievementsView: FC<AchievementsViewProps> = props =>
|
||||||
|
{
|
||||||
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
|
const [ achievementsState, dispatchAchievementsState ] = useReducer(AchievementsReducer, initialAchievements);
|
||||||
|
const { score = null } = achievementsState;
|
||||||
|
|
||||||
|
const onAchievementsEvent = 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, onAchievementsEvent);
|
||||||
|
useUiEvent(AchievementsUIEvent.HIDE_ACHIEVEMENTS, onAchievementsEvent);
|
||||||
|
useUiEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS, onAchievementsEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!isVisible) return;
|
||||||
|
|
||||||
|
}, [ isVisible ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AchievementsContextProvider value={ { achievementsState, dispatchAchievementsState } }>
|
||||||
|
<AchievementsMessageHandler />
|
||||||
|
{ isVisible &&
|
||||||
|
<NitroCardView className="nitro-achievements">
|
||||||
|
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||||
|
<NitroCardContentView>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-6">
|
||||||
|
<AchievementsListView />
|
||||||
|
<div className="score border border-2 text-black text-center rounded">
|
||||||
|
{ LocalizeText('achievements.categories.score', ['score'], [score.toString()]) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-6">
|
||||||
|
<AchievementCategoryView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView> }
|
||||||
|
</AchievementsContextProvider>
|
||||||
|
);
|
||||||
|
};
|
2
src/views/achievements/AchievementsView.types.ts
Normal file
2
src/views/achievements/AchievementsView.types.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export class AchievementsViewProps
|
||||||
|
{}
|
14
src/views/achievements/context/AchievementsContext.tsx
Normal file
14
src/views/achievements/context/AchievementsContext.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createContext, FC, useContext } from 'react';
|
||||||
|
import { AchievementsContextProps, IAchievementsContext } from './AchievementsContext.types';
|
||||||
|
|
||||||
|
const AchievementsContext = createContext<IAchievementsContext>({
|
||||||
|
achievementsState: null,
|
||||||
|
dispatchAchievementsState: null
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AchievementsContextProvider: FC<AchievementsContextProps> = props =>
|
||||||
|
{
|
||||||
|
return <AchievementsContext.Provider value={ props.value }>{ props.children }</AchievementsContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAchievementsContext = () => useContext(AchievementsContext);
|
13
src/views/achievements/context/AchievementsContext.types.ts
Normal file
13
src/views/achievements/context/AchievementsContext.types.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Dispatch, ProviderProps } from 'react';
|
||||||
|
import { IAchievementsAction, IAchievementsState } from '../reducers/AchievementsReducer';
|
||||||
|
|
||||||
|
export interface IAchievementsContext
|
||||||
|
{
|
||||||
|
achievementsState: IAchievementsState;
|
||||||
|
dispatchAchievementsState: Dispatch<IAchievementsAction>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AchievementsContextProps extends ProviderProps<IAchievementsContext>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
84
src/views/achievements/reducers/AchievementsReducer.tsx
Normal file
84
src/views/achievements/reducers/AchievementsReducer.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Reducer } from 'react';
|
||||||
|
import { AchievementCategory } from '../utils/AchievementCategory';
|
||||||
|
|
||||||
|
export interface IAchievementsState
|
||||||
|
{
|
||||||
|
categories: AchievementCategory[],
|
||||||
|
score: number,
|
||||||
|
selectedCategoryName: string,
|
||||||
|
selectedAchievementId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAchievementsAction
|
||||||
|
{
|
||||||
|
type: string;
|
||||||
|
payload: {
|
||||||
|
categories?: AchievementCategory[],
|
||||||
|
score?: number,
|
||||||
|
selectedCategoryName?: string,
|
||||||
|
selectedAchievementId?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AchievementsActions
|
||||||
|
{
|
||||||
|
public static SET_CATEGORIES: string = 'AA_SET_CATEGORIES';
|
||||||
|
public static SET_SCORE: string = 'AA_SET_SCORE';
|
||||||
|
public static SELECT_CATEGORY: string = 'AA_SELECT_CATEGORY';
|
||||||
|
public static SELECT_ACHIEVEMENT: string = 'AA_SELECT_ACHIEVEMENT';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialAchievements: IAchievementsState = {
|
||||||
|
categories: null,
|
||||||
|
score: null,
|
||||||
|
selectedCategoryName: null,
|
||||||
|
selectedAchievementId: null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementsReducer: Reducer<IAchievementsState, IAchievementsAction> = (state, action) =>
|
||||||
|
{
|
||||||
|
switch(action.type)
|
||||||
|
{
|
||||||
|
case AchievementsActions.SET_CATEGORIES: {
|
||||||
|
const categories = (action.payload.categories || state.categories || null);
|
||||||
|
|
||||||
|
let selectedCategoryName = null;
|
||||||
|
|
||||||
|
if(categories.length > 0)
|
||||||
|
{
|
||||||
|
selectedCategoryName = categories[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...state, categories, selectedCategoryName };
|
||||||
|
}
|
||||||
|
case AchievementsActions.SET_SCORE: {
|
||||||
|
const score = (action.payload.score || state.score || null);
|
||||||
|
|
||||||
|
return { ...state, score };
|
||||||
|
}
|
||||||
|
case AchievementsActions.SELECT_CATEGORY: {
|
||||||
|
const selectedCategoryName = (action.payload.selectedCategoryName || state.selectedCategoryName || null);
|
||||||
|
|
||||||
|
let selectedAchievementId = null;
|
||||||
|
|
||||||
|
if(selectedCategoryName)
|
||||||
|
{
|
||||||
|
const category = state.categories.find(category => category.name === selectedCategoryName);
|
||||||
|
|
||||||
|
if(category && category.achievements.length > 0)
|
||||||
|
{
|
||||||
|
selectedAchievementId = category.achievements[0].achievementId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...state, selectedCategoryName, selectedAchievementId };
|
||||||
|
}
|
||||||
|
case AchievementsActions.SELECT_ACHIEVEMENT: {
|
||||||
|
const selectedAchievementId = (action.payload.selectedAchievementId || state.selectedAchievementId || null);
|
||||||
|
|
||||||
|
return { ...state, selectedAchievementId };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
33
src/views/achievements/utils/AchievementCategory.ts
Normal file
33
src/views/achievements/utils/AchievementCategory.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { AchievementData } from 'nitro-renderer';
|
||||||
|
|
||||||
|
export class AchievementCategory
|
||||||
|
{
|
||||||
|
private _name: string;
|
||||||
|
private _achievements: AchievementData[];
|
||||||
|
|
||||||
|
constructor(name: string)
|
||||||
|
{
|
||||||
|
this._name = name;
|
||||||
|
this._achievements = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string
|
||||||
|
{
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set name(name: string)
|
||||||
|
{
|
||||||
|
this._name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get achievements(): AchievementData[]
|
||||||
|
{
|
||||||
|
return this._achievements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set achievements(achievements: AchievementData[])
|
||||||
|
{
|
||||||
|
this._achievements = achievements;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { AchievementData } from 'nitro-renderer';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../utils/LocalizeText';
|
||||||
|
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
|
||||||
|
import { useAchievementsContext } from '../../context/AchievementsContext';
|
||||||
|
import { AchievementsActions } from '../../reducers/AchievementsReducer';
|
||||||
|
import { AchievementCategoryViewProps } from './AchievementCategoryView.types';
|
||||||
|
|
||||||
|
export const AchievementCategoryView: FC<AchievementCategoryViewProps> = props =>
|
||||||
|
{
|
||||||
|
const achievementsContext = useAchievementsContext();
|
||||||
|
|
||||||
|
const { achievementsState = null, dispatchAchievementsState = null } = achievementsContext;
|
||||||
|
const { categories = null, selectedCategoryName = null, selectedAchievementId = null } = achievementsState;
|
||||||
|
|
||||||
|
const getSelectedCategory = useCallback(() =>
|
||||||
|
{
|
||||||
|
return categories.find(category => category.name === selectedCategoryName);
|
||||||
|
}, [ categories, selectedCategoryName ]);
|
||||||
|
|
||||||
|
const getAchievementImage = useCallback((achievement: AchievementData) =>
|
||||||
|
{
|
||||||
|
if(!achievement) return null;
|
||||||
|
|
||||||
|
let badgeId = achievement.badgeId;
|
||||||
|
|
||||||
|
if(achievement.levelCount > 1)
|
||||||
|
{
|
||||||
|
badgeId = badgeId.replace(/[0-9]/g, '');
|
||||||
|
badgeId = (badgeId + (((achievement.level - 1) > 0) ? (achievement.level - 1) : achievement.level));
|
||||||
|
}
|
||||||
|
|
||||||
|
return badgeId;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getSelectedAchievement = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(!getSelectedCategory()) return null;
|
||||||
|
|
||||||
|
return getSelectedCategory().achievements.find(achievement => achievement.achievementId === selectedAchievementId);
|
||||||
|
}, [ getSelectedCategory, selectedAchievementId ]);
|
||||||
|
|
||||||
|
const selectAchievement = useCallback((id: number) =>
|
||||||
|
{
|
||||||
|
dispatchAchievementsState({
|
||||||
|
type: AchievementsActions.SELECT_ACHIEVEMENT,
|
||||||
|
payload: {
|
||||||
|
selectedAchievementId: id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ dispatchAchievementsState ]);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-column h-100">
|
||||||
|
<div className="bg-primary rounded p-2 d-flex align-items-center mb-3">
|
||||||
|
<h5 className="m-0 me-2 w-100">{ LocalizeText('quests.' + selectedCategoryName + '.name') }</h5>
|
||||||
|
<div>IMAGE</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-muted rounded p-2 mb-3 d-flex">
|
||||||
|
<div>
|
||||||
|
<BadgeImageView badgeCode={ getAchievementImage(getSelectedAchievement()) } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="achievements">
|
||||||
|
<div className="row row-cols-4">
|
||||||
|
{ getSelectedCategory().achievements.map((achievement, index) =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<div key={ index } className="col mb-3">
|
||||||
|
<div className={'achievement border border-2 rounded d-flex flex-column justify-content-center align-items-center p-2' + classNames({' active': selectedAchievementId === achievement.achievementId, ' gray': achievement.progress === 0})} onClick={() => selectAchievement(achievement.achievementId)}>
|
||||||
|
<BadgeImageView badgeCode={ getAchievementImage(achievement) } />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
export class AchievementCategoryViewProps
|
||||||
|
{}
|
@ -0,0 +1,2 @@
|
|||||||
|
export class AchievementListViewProps
|
||||||
|
{}
|
70
src/views/achievements/views/list/AchievementsListView.tsx
Normal file
70
src/views/achievements/views/list/AchievementsListView.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { GetConfiguration } from '../../../../api';
|
||||||
|
import { useAchievementsContext } from '../../context/AchievementsContext';
|
||||||
|
import { AchievementsActions } from '../../reducers/AchievementsReducer';
|
||||||
|
import { AchievementCategory } from '../../utils/AchievementCategory';
|
||||||
|
import { AchievementListViewProps } from './AchievementListView.types';
|
||||||
|
|
||||||
|
export const AchievementsListView: FC<AchievementListViewProps> = props =>
|
||||||
|
{
|
||||||
|
const achievementsContext = useAchievementsContext();
|
||||||
|
|
||||||
|
const { achievementsState = null, dispatchAchievementsState = null } = achievementsContext;
|
||||||
|
const { categories = null, selectedCategoryName = null } = achievementsState;
|
||||||
|
|
||||||
|
const getCategoryImage = useCallback((category: AchievementCategory) =>
|
||||||
|
{
|
||||||
|
let level = 0;
|
||||||
|
|
||||||
|
for(const achievement of category.achievements)
|
||||||
|
{
|
||||||
|
level = (level + ((achievement.finalLevel) ? achievement.level : (achievement.level - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActive = ((level > 0) ? 'active' : 'inactive');
|
||||||
|
|
||||||
|
return GetConfiguration('achievements.images.url', GetConfiguration('achievements.images.url') + `quests/achcategory_${category.name}_${isActive}.png`).replace('%image%',`achcategory_${category.name}_${isActive}`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getCategoryProgress = useCallback((category: AchievementCategory) =>
|
||||||
|
{
|
||||||
|
let completed = 0;
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
for(const achievement of category.achievements)
|
||||||
|
{
|
||||||
|
if(!achievement) continue;
|
||||||
|
|
||||||
|
if(achievement.finalLevel) completed = completed + 1 + achievement.level;
|
||||||
|
|
||||||
|
total = (total + achievement.scoreLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (completed + ' / ' + total);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const selectCategory = useCallback((name: string) =>
|
||||||
|
{
|
||||||
|
dispatchAchievementsState({
|
||||||
|
type: AchievementsActions.SELECT_CATEGORY,
|
||||||
|
payload: {
|
||||||
|
selectedCategoryName: name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ dispatchAchievementsState ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="row row-cols-3">
|
||||||
|
{ categories && categories.map((category, index) =>
|
||||||
|
{
|
||||||
|
return <div key={ index } className="col mb-3">
|
||||||
|
<div className={'category border border-2 rounded d-flex flex-column justify-content-center align-items-center p-2' + classNames({' active': selectedCategoryName === category.name})} onClick={() => selectCategory(category.name)}>
|
||||||
|
<img alt="" src={getCategoryImage(category)} />
|
||||||
|
<div className="position-absolute category-score small">{ getCategoryProgress(category) }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
import { Nitro, RoomSessionEvent } from 'nitro-renderer';
|
import { Nitro, RoomSessionEvent } from 'nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
|
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
|
||||||
|
import { AchievementsView } from '../achievements/AchievementsView';
|
||||||
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
||||||
import { CatalogView } from '../catalog/CatalogView';
|
import { CatalogView } from '../catalog/CatalogView';
|
||||||
import { FriendListView } from '../friend-list/FriendListView';
|
import { FriendListView } from '../friend-list/FriendListView';
|
||||||
@ -51,6 +52,7 @@ export const MainView: FC<MainViewProps> = props =>
|
|||||||
<RoomHostView />
|
<RoomHostView />
|
||||||
<WiredView />
|
<WiredView />
|
||||||
<AvatarEditorView />
|
<AvatarEditorView />
|
||||||
|
<AchievementsView />
|
||||||
<NavigatorView />
|
<NavigatorView />
|
||||||
<InventoryView />
|
<InventoryView />
|
||||||
<CatalogView />
|
<CatalogView />
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Dispose, DropBounce, EaseOut, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserFigureEvent, UserInfoDataParser, UserInfoEvent, Wait } from 'nitro-renderer';
|
import { Dispose, DropBounce, EaseOut, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserFigureEvent, UserInfoDataParser, UserInfoEvent, Wait } from 'nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||||
|
import { AchievementsUIEvent } from '../../events/achievements';
|
||||||
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
|
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
|
||||||
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
||||||
import { useRoomEngineEvent } from '../../hooks';
|
import { dispatchUiEvent, useRoomEngineEvent, useUiEvent } from '../../hooks';
|
||||||
import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
|
|
||||||
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation';
|
import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation';
|
||||||
import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types';
|
import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types';
|
||||||
@ -117,6 +117,10 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
|||||||
case ToolbarViewItems.MOD_TOOLS_ITEM:
|
case ToolbarViewItems.MOD_TOOLS_ITEM:
|
||||||
dispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS));
|
dispatchUiEvent(new ModToolsEvent(ModToolsEvent.TOGGLE_MOD_TOOLS));
|
||||||
return;
|
return;
|
||||||
|
case ToolbarViewItems.ACHIEVEMENTS_ITEM:
|
||||||
|
dispatchUiEvent(new AchievementsUIEvent(AchievementsUIEvent.TOGGLE_ACHIEVEMENTS));
|
||||||
|
setMeExpanded(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -12,4 +12,5 @@ export class ToolbarViewItems
|
|||||||
public static CLOTHING_ITEM: string = 'TVI_CLOTHING_ITEM';
|
public static CLOTHING_ITEM: string = 'TVI_CLOTHING_ITEM';
|
||||||
public static CAMERA_ITEM: string = 'TVI_CAMERA_ITEM';
|
public static CAMERA_ITEM: string = 'TVI_CAMERA_ITEM';
|
||||||
public static MOD_TOOLS_ITEM: string = 'TVI_MOD_TOOLS_ITEM';
|
public static MOD_TOOLS_ITEM: string = 'TVI_MOD_TOOLS_ITEM';
|
||||||
|
public static ACHIEVEMENTS_ITEM: string = 'TVI_ACHIEVEMENTS_ITEM';
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ export const ToolbarMeView: FC<ToolbarMeViewProps> = props =>
|
|||||||
<div className="navigation-item">
|
<div className="navigation-item">
|
||||||
<i className="icon icon-me-helper-tool"></i>
|
<i className="icon icon-me-helper-tool"></i>
|
||||||
</div>
|
</div>
|
||||||
<div className="navigation-item">
|
<div className="navigation-item" onClick={ event => handleToolbarItemClick(ToolbarViewItems.ACHIEVEMENTS_ITEM) }>
|
||||||
<i className="icon icon-me-achievements"></i>
|
<i className="icon icon-me-achievements"></i>
|
||||||
</div>
|
</div>
|
||||||
<div className="navigation-item">
|
<div className="navigation-item">
|
||||||
|
Loading…
Reference in New Issue
Block a user