5180 fichiers 38.5 Mo 0 téléchargements 238 vues mis à jour il y a 5 heures ui
nitro-v36-with-renderer-v36 / src / hooks / quests / useRewardTrackAdmin.ts
useRewardTrackAdmin.ts 7.3 Ko Télécharger le fichier
import {
    DeleteRewardTrackEntityMessageComposer,
    GetRewardTrackAdminDataMessageComposer,
    RewardTrackAdminDataMessageEvent,
    RewardTrackAdminResultMessageEvent,
    RewardTrackAdminTrack,
    RewardTrackFurniMatch,
    RewardTrackFurniSearchResultMessageEvent,
    RewardTrackTaskLevelInput,
    RewardTrackTextInput,
    SaveRewardTrackMessageComposer,
    SaveRewardTrackPrizeMessageComposer,
    SaveRewardTrackTaskMessageComposer,
    SaveRewardTrackTextsMessageComposer,
    SearchRewardTrackFurniMessageComposer
} from '@octane/renderer';
import { useCallback, useEffect, useRef, useState } from 'react';
import { SendMessageComposer } from '../../api';
import { useMessageEvent } from '../events';

export type RewardTrackAdminEntity = 'track' | 'task' | 'prize' | 'texts';

export interface RewardTrackFurniSearch {
    query: string;
    matches: RewardTrackFurniMatch[];
}

export interface RewardTrackAdminResult {
    success: boolean;
    message: string;
    entity: string;
    trackId: string;
    id: string;
    /** Grows with every answer so the same message can be shown twice. */
    sequence: number;
}

export interface RewardTrackAdminTrackInput {
    id: string;
    theme: string;
    sortOrder: number;
    startsAt: number;
    endsAt: number;
    hasPremium: boolean;
    /** Hundredths: 150 is 1.5x. */
    premiumBoostPercent: number;
    premiumInstantPoints: number;
    premiumCostDiamonds: number;
    premiumCostCredits: number;
    enabled: boolean;
}

export interface RewardTrackAdminTaskInput {
    trackId: string;
    id: string;
    actionType: string;
    parameter: string;
    premium: boolean;
    sortOrder: number;
    levels: RewardTrackTaskLevelInput[];
}

export interface RewardTrackAdminPrizeInput {
    trackId: string;
    id: string;
    requiredPoints: number;
    productItemTypeId: number;
    rewardType: string;
    extraParams: string;
    rewardAmount: number;
    premium: boolean;
    sortOrder: number;
}

type QueuedComposer = Parameters<typeof SendMessageComposer>[0];

/** The server answers a write before the next one may go; a lost answer frees the queue after this long. */
const WRITE_TIMEOUT_MS = 4000;

/**
 * The staff editor's side of the reward track: every stored track (disabled ones included), the
 * choices the server offers, and the writes. Each successful write makes the server reload the
 * tracks for everyone and send this list again. Writes go one at a time: the server takes at most
 * two a second per client, and a reorder or a duplicate sends several.
 */
export const useRewardTrackAdmin = () => {
    const [tracks, setTracks] = useState<RewardTrackAdminTrack[]>([]);
    const [actionTypes, setActionTypes] = useState<string[]>([]);
    const [rewardTypes, setRewardTypes] = useState<string[]>([]);
    const [loaded, setLoaded] = useState(false);
    const [pending, setPending] = useState(false);
    const [lastResult, setLastResult] = useState<RewardTrackAdminResult>(null);
    const [furniSearch, setFurniSearch] = useState<RewardTrackFurniSearch>(null);
    const queue = useRef<QueuedComposer[]>([]);
    const inFlight = useRef(false);
    const timeout = useRef<ReturnType<typeof setTimeout>>(null);

    const sendNext = useCallback(() => {
        if (timeout.current) clearTimeout(timeout.current);

        const next = queue.current.shift();

        if (!next) {
            inFlight.current = false;
            setPending(false);

            return;
        }

        inFlight.current = true;
        setPending(true);
        SendMessageComposer(next);
        timeout.current = setTimeout(() => sendNext(), WRITE_TIMEOUT_MS);
    }, []);

    const write = useCallback(
        (composer: QueuedComposer) => {
            queue.current.push(composer);

            if (!inFlight.current) sendNext();
        },
        [sendNext]
    );

    useEffect(() => () => timeout.current && clearTimeout(timeout.current), []);

    const requestData = useCallback(() => SendMessageComposer(new GetRewardTrackAdminDataMessageComposer()), []);

    const saveTrack = useCallback(
        (input: RewardTrackAdminTrackInput) =>
            write(
                new SaveRewardTrackMessageComposer(
                    input.id,
                    input.theme,
                    input.sortOrder,
                    input.startsAt,
                    input.endsAt,
                    input.hasPremium,
                    input.premiumBoostPercent,
                    input.premiumInstantPoints,
                    input.premiumCostDiamonds,
                    input.premiumCostCredits,
                    input.enabled
                )
            ),
        [write]
    );

    const saveTask = useCallback(
        (input: RewardTrackAdminTaskInput) =>
            write(new SaveRewardTrackTaskMessageComposer(input.trackId, input.id, input.actionType, input.parameter, input.premium, input.sortOrder, input.levels)),
        [write]
    );

    const savePrize = useCallback(
        (input: RewardTrackAdminPrizeInput) =>
            write(
                new SaveRewardTrackPrizeMessageComposer(
                    input.trackId,
                    input.id,
                    input.requiredPoints,
                    input.productItemTypeId,
                    input.rewardType,
                    input.extraParams,
                    input.rewardAmount,
                    input.premium,
                    input.sortOrder
                )
            ),
        [write]
    );

    const saveTexts = useCallback((trackId: string, texts: RewardTrackTextInput[]) => write(new SaveRewardTrackTextsMessageComposer(trackId, texts)), [write]);

    /** A lookup, outside the write queue: the answer carries the query so a stale one can be told apart. */
    const searchFurni = useCallback((query: string) => SendMessageComposer(new SearchRewardTrackFurniMessageComposer(query)), []);

    const deleteEntity = useCallback((entity: RewardTrackAdminEntity, trackId: string, id: string) => write(new DeleteRewardTrackEntityMessageComposer(entity, trackId, id)), [write]);

    useMessageEvent<RewardTrackAdminDataMessageEvent>(RewardTrackAdminDataMessageEvent, (event) => {
        const parser = event.getParser();

        setActionTypes(parser.actionTypes);
        setRewardTypes(parser.rewardTypes);
        setTracks(parser.tracks);
        setLoaded(true);
    });

    useMessageEvent<RewardTrackFurniSearchResultMessageEvent>(RewardTrackFurniSearchResultMessageEvent, (event) => {
        const parser = event.getParser();

        setFurniSearch({ query: parser.query, matches: parser.matches });
    });

    useMessageEvent<RewardTrackAdminResultMessageEvent>(RewardTrackAdminResultMessageEvent, (event) => {
        const parser = event.getParser();

        setLastResult((previous) => ({
            success: parser.success,
            message: parser.message,
            entity: parser.entity,
            trackId: parser.trackId,
            id: parser.id,
            sequence: (previous?.sequence ?? 0) + 1
        }));

        // A refusal drops what was queued behind it: a duplicate whose track failed must not add orphans.
        if (!parser.success) queue.current = [];

        sendNext();
    });

    return { tracks, actionTypes, rewardTypes, loaded, pending, lastResult, furniSearch, requestData, saveTrack, saveTask, savePrize, saveTexts, deleteEntity, searchFurni };
};
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser