Update purse

This commit is contained in:
Bill 2021-05-07 03:50:20 -04:00
parent 1cf02c53d7
commit 4ef0a1d6d0
10 changed files with 189 additions and 92 deletions

View File

@ -0,0 +1,56 @@
import { UserCreditsEvent, UserCurrencyEvent, UserCurrencyUpdateEvent } from 'nitro-renderer';
import { FC, useCallback } from 'react';
import { CreateMessageHook } from '../../hooks/messages/message-event';
import { usePurseContext } from './context/PurseContext';
import { PurseMessageHandlerProps } from './PurseMessageHandler.types';
import { PurseActions } from './reducers/PurseReducer';
import { Currency } from './utils/Currency';
export const PurseMessageHandler: FC<PurseMessageHandlerProps> = props =>
{
const { dispatchPurseState = null } = usePurseContext();
const onUserCreditsEvent = useCallback((event: UserCreditsEvent) =>
{
const parser = event.getParser();
dispatchPurseState({
type: PurseActions.SET_CURRENCY,
payload: {
currency: { type: -1, amount: parseFloat(parser.credits) }
}
});
}, [ dispatchPurseState ]);
const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) =>
{
const parser = event.getParser();
const currencies: Currency[] = [];
for(const [ key, value ] of parser.currencies.entries()) currencies.push({ type: key, amount: value });
dispatchPurseState({
type: PurseActions.SET_CURRENCIES,
payload: { currencies }
});
}, [ dispatchPurseState ]);
const onUserCurrencyUpdateEvent = useCallback((event: UserCurrencyUpdateEvent) =>
{
const parser = event.getParser();
dispatchPurseState({
type: PurseActions.SET_CURRENCY,
payload: {
currency: { type: parser.type, amount: parser.amount }
}
});
}, [ dispatchPurseState ]);
CreateMessageHook(UserCreditsEvent, onUserCreditsEvent);
CreateMessageHook(UserCurrencyEvent, onUserCurrencyEvent);
CreateMessageHook(UserCurrencyUpdateEvent, onUserCurrencyUpdateEvent);
return null;
}

View File

@ -0,0 +1,4 @@
export interface PurseMessageHandlerProps
{
}

View File

@ -1,90 +1,39 @@
import { UserCreditsEvent, UserCurrencyComposer, UserCurrencyEvent, UserCurrencyUpdateEvent } from 'nitro-renderer';
import { useCallback, useEffect, useState } from 'react';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
import { TransitionAnimation } from '../../transitions/TransitionAnimation';
import { TransitionAnimationTypes } from '../../transitions/TransitionAnimation.types';
import { UserCurrencyComposer } from 'nitro-renderer';
import { FC, useEffect, useMemo, useReducer } from 'react';
import { SendMessageHook } from '../../hooks/messages/message-event';
import { GetConfiguration } from '../../utils/GetConfiguration';
import { CurrencySet } from './currency/CurrencySet';
import { PurseContextProvider } from './context/PurseContext';
import { CurrencyView } from './currency/CurrencyView';
import { PurseMessageHandler } from './PurseMessageHandler';
import { PurseViewProps } from './PurseView.types';
import { initialPurse, PurseReducer } from './reducers/PurseReducer';
export function PurseView(props: PurseViewProps): JSX.Element
export const PurseView: FC<PurseViewProps> = props =>
{
const [ currencies, setCurrencies ] = useState<CurrencySet[]>([ new CurrencySet(-1, 0) ]);
const [ isReady, setIsReady ] = useState(false);
const displayedCurrencies = GetConfiguration<number[]>('system.currency.types', []);
const onUserCreditsEvent = useCallback((event: UserCreditsEvent) =>
const [ purseState, dispatchPurseState ] = useReducer(PurseReducer, initialPurse);
const { currencies = [] } = purseState;
const displayedCurrencies = useMemo(() =>
{
const parser = event.getParser();
updateCurrency(-1, parseFloat(parser.credits));
return GetConfiguration<number[]>('system.currency.types', []);
}, []);
const onUserCurrencyEvent = useCallback((event: UserCurrencyEvent) =>
{
const parser = event.getParser();
for(const [ key, value ] of parser.currencies.entries()) updateCurrency(key, value);
setIsReady(true);
}, []);
const onUserCurrencyUpdateEvent = useCallback((event: UserCurrencyUpdateEvent) =>
{
const parser = event.getParser();
updateCurrency(parser.type, parser.amount)
}, []);
function updateCurrency(type: number, amount: number): void
{
setCurrencies(oldState =>
{
const newState: CurrencySet[] = [];
let found = false;
for(const set of oldState)
{
if(set.type !== type)
{
newState.push(set);
continue;
}
newState.push(new CurrencySet(set.type, amount));
found = true;
}
if(!found) newState.push(new CurrencySet(type, amount));
return newState;
});
}
CreateMessageHook(UserCreditsEvent, onUserCreditsEvent);
CreateMessageHook(UserCurrencyEvent, onUserCurrencyEvent);
CreateMessageHook(UserCurrencyUpdateEvent, onUserCurrencyUpdateEvent);
useEffect(() =>
{
SendMessageHook(new UserCurrencyComposer());
}, []);
return (
<TransitionAnimation className="nitro-purse position-relative mb-1" type={ TransitionAnimationTypes.FADE_DOWN } inProp={ isReady } timeout={ 300 }>
<PurseContextProvider value={ { purseState, dispatchPurseState }}>
<PurseMessageHandler />
<div className="row row-cols-2 g-0">
{ currencies && currencies.map((set, index) =>
{ currencies && currencies.map((currency, index) =>
{
if(displayedCurrencies.indexOf(set.type) === -1) return null;
if(displayedCurrencies.indexOf(currency.type) === -1) return null;
return <CurrencyView key={ index } currencySet={ set } />
return <CurrencyView key={ index } currency={ currency } />
}) }
</div>
</TransitionAnimation>
</PurseContextProvider>
);
}

View File

@ -0,0 +1,14 @@
import { createContext, FC, useContext } from 'react';
import { IPurseContext, PurseContextProps } from './PurseContext.types';
const PurseContext = createContext<IPurseContext>({
purseState: null,
dispatchPurseState: null
});
export const PurseContextProvider: FC<PurseContextProps> = props =>
{
return <PurseContext.Provider value={ props.value }>{ props.children }</PurseContext.Provider>
}
export const usePurseContext = () => useContext(PurseContext);

View File

@ -0,0 +1,13 @@
import { Dispatch, ProviderProps } from 'react';
import { IPurseAction, IPurseState } from '../reducers/PurseReducer';
export interface IPurseContext
{
purseState: IPurseState;
dispatchPurseState: Dispatch<IPurseAction>;
}
export interface PurseContextProps extends ProviderProps<IPurseContext>
{
}

View File

@ -1,9 +0,0 @@
export class CurrencySet
{
constructor(
public type: number,
public amount: number)
{
}
}

View File

@ -6,7 +6,7 @@ import { CurrencyViewProps } from './CurrencyView.types';
export function CurrencyView(props: CurrencyViewProps): JSX.Element
{
const { currencySet = null } = props;
const { currency = null } = props;
const [ firstRender, setFirstRender ] = useState(true);
const [ isAnimating, setIsAnimating ] = useState(false);
@ -22,22 +22,20 @@ export function CurrencyView(props: CurrencyViewProps): JSX.Element
setIsAnimating(true);
let timeout = setTimeout(() =>
{
setIsAnimating(false)
timeout = null
}, 300);
const timeout = setTimeout(() => setIsAnimating(false), 305);
return () => clearTimeout(timeout);
}, [ firstRender, currencySet ]);
return () =>
{
clearTimeout(timeout);
}
}, [ firstRender, currency ]);
return (
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isAnimating } timeout={ 300 }>
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isAnimating }>
<div className="col pe-1 pb-1">
<div className="d-flex bg-primary rounded border overflow-hidden">
<div className="d-flex flex-grow-1 align-items-center justify-content-end pe-1">{ currencySet.amount }</div>
<div className="bg-secondary"><CurrencyIcon type={ currencySet.type } /></div>
<div className="d-flex flex-grow-1 align-items-center justify-content-end pe-1">{ !isAnimating && currency.amount }</div>
<div className="bg-secondary"><CurrencyIcon type={ currency.type } /></div>
</div>
</div>
</TransitionAnimation>

View File

@ -1,6 +1,6 @@
import { CurrencySet } from './CurrencySet';
import { Currency } from '../utils/Currency';
export interface CurrencyViewProps
{
currencySet: CurrencySet;
currency: Currency;
}

View File

@ -0,0 +1,67 @@
import { Reducer } from 'react';
import { Currency } from '../utils/Currency';
export interface IPurseState
{
currencies: Currency[];
}
export interface IPurseAction
{
type: string;
payload: {
currency?: Currency;
currencies?: Currency[];
}
}
export class PurseActions
{
public static SET_CURRENCY: string = 'PA_SET_CURRENCY';
public static SET_CURRENCIES: string = 'PA_SET_CURRENCIES';
}
export const initialPurse: IPurseState = {
currencies: []
}
export const PurseReducer: Reducer<IPurseState, IPurseAction> = (state, action) =>
{
switch(action.type)
{
case PurseActions.SET_CURRENCY: {
const updated = action.payload.currency;
let didSet = false;
const currencies = state.currencies.map((existing, index) =>
{
if(existing.type !== updated.type) return existing;
didSet = true;
return { ...updated };
});
if(!didSet) currencies.push({ ...updated });
return { ...state, currencies };
}
case PurseActions.SET_CURRENCIES: {
const updated = action.payload.currencies;
const currencies = state.currencies.filter((existing, index) =>
{
if(existing.type !== -1) return null;
return existing;
});
if(updated && updated.length) currencies.push(...updated);
return { ...state, currencies };
}
default:
return state;
}
}

View File

@ -0,0 +1,5 @@
export interface Currency
{
type: number;
amount: number;
}