5180 fichiers 38.5 Mo 0 téléchargements 473 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / navigator / views / raid-protection / NavigatorRaidProtectionView.tsx
NavigatorRaidProtectionView.tsx 12.4 Ko Télécharger le fichier
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { LocalizeText, localizeWithFallback, NotificationBubbleType } from '../../../../api';
import {
    IRaidProtectionEditable,
    RAID_PROTECTION_ACTION_KICK,
    RAID_PROTECTION_BAN_DURATIONS,
    RAID_PROTECTION_DURATION_FALLBACKS,
    RAID_PROTECTION_GUARD_DURATIONS,
    RAID_PROTECTION_SENSITIVITIES,
    RAID_PROTECTION_SENSITIVITY_FALLBACKS,
    RAID_PROTECTION_SENSITIVITY_KEYS,
    raidProtectionNeedsConfirmation,
    raidProtectionSaveResultKey
} from '../../../../api/navigator/RaidProtection';
import { OctaneCardContentView, OctaneCardHeaderView, OctaneCardView } from '../../../../common';
import { BaseButton } from '../../../../common/BaseButton';
import { Checkbox } from '../../../../common/Checkbox';
import { UbuntuDropmenuView } from '../../../../common/dropmenu/UbuntuDropmenuView';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import {
    isCurrentRaidProtectionRoom,
    sendRaidProtectionSave,
    useNavigatorData,
    useNotificationActions,
    useRaidProtectionController,
    useRaidProtectionStore
} from '../../../../hooks';

const text = (key: string, fallback: string) => localizeWithFallback(`raid.protection.settings.${ key }`, fallback);

const sensitivityOptions = () => RAID_PROTECTION_SENSITIVITIES.map((value, index) => ({
    value,
    label: text(`sensitivity.${ RAID_PROTECTION_SENSITIVITY_KEYS[index] }`, RAID_PROTECTION_SENSITIVITY_FALLBACKS[index])
}));

const durationOptions = (durations: readonly number[]) => durations.map(value => ({
    value,
    label: text(`duration.${ value }`, RAID_PROTECTION_DURATION_FALLBACKS[value] ?? `${ value }`)
}));

/**
 * AIR raid_protection_settings_xml (navigator/raidprotection/RaidProtectionSettingsView): the advice
 * card, protection on/off with detection sensitivity, action and ban duration, the guard card, the
 * raid status line and cancel / save. Opened from the room info button (navigator/raidprotection/<id>).
 */
export const NavigatorRaidProtectionView: FC<{}> = () =>
{
    const { simpleAlert = null, showConfirm = null, showSingleBubble = null } = useNotificationActions();
    const { navigatorData } = useNavigatorData();
    const visibleRoomId = useRaidProtectionStore(state => state.visibleRoomId);
    const settings = useRaidProtectionStore(state => state.settings);
    const savingRoomId = useRaidProtectionStore(state => state.savingRoomId);
    const [ form, setForm ] = useState<IRaidProtectionEditable | null>(null);
    const [ confirming, setConfirming ] = useState(false);
    const confirmTokenRef = useRef(0);

    const onSaveResult = useCallback((resultCode: number) =>
    {
        const message = LocalizeText(raidProtectionSaveResultKey(resultCode));

        if(resultCode === 0) showSingleBubble?.(message, NotificationBubbleType.INFO);
        else simpleAlert?.(message, null, null, null, text('title', 'Raid protection'));
    }, [ showSingleBubble, simpleAlert ]);

    useRaidProtectionController(onSaveResult);

    const visible = visibleRoomId > 0 && settings?.roomId === visibleRoomId;

    // AIR update(): the controls follow every settings packet for the room the window shows.
    useEffect(() =>
    {
        if(!visible || !settings) return;

        setForm({
            roomId: settings.roomId,
            enabled: settings.enabled,
            sensitivity: settings.sensitivity,
            actionType: settings.actionType,
            banDurationSeconds: settings.banDurationSeconds,
            guardEnabled: settings.guardEnabled,
            guardDurationSeconds: settings.guardDurationSeconds,
            guardSensitivity: settings.guardSensitivity
        });
    }, [ visible, settings ]);

    // A closed window drops a pending confirmation (AIR _r7fcbccc37fb3a9).
    useEffect(() =>
    {
        if(visible) return;

        confirmTokenRef.current++;
        setConfirming(false);
    }, [ visible ]);

    if(!visible || !form || !settings) return null;

    const enteredRoomId = navigatorData?.enteredGuestRoom?.roomId ?? 0;
    const busy = confirming || savingRoomId === form.roomId;
    const update = (patch: Partial<IRaidProtectionEditable>) => setForm(previous => (previous ? { ...previous, ...patch } : previous));
    const close = () => useRaidProtectionStore.getState().close();

    const save = () =>
    {
        const store = useRaidProtectionStore.getState();
        const stored = store.settings;

        if(!stored || stored.roomId !== form.roomId || store.savingRoomId !== 0) return;

        if(!isCurrentRaidProtectionRoom(form.roomId, enteredRoomId) || store.manageableRoomId !== form.roomId)
        {
            store.clearRoom(form.roomId);

            return;
        }

        if(!raidProtectionNeedsConfirmation(stored, form))
        {
            sendRaidProtectionSave(form, false);

            return;
        }

        const token = ++confirmTokenRef.current;
        const requested = form;

        setConfirming(true);
        showConfirm?.(
            LocalizeText(stored.incidentActive ? 'raid.protection.settings.confirm.active' : 'raid.protection.settings.confirm.inactive'),
            () =>
            {
                setConfirming(false);

                const current = useRaidProtectionStore.getState();

                // AIR onConfirmation: only when nothing changed while the question was open.
                if(token !== confirmTokenRef.current || current.settings !== stored || current.settings.enabled || current.savingRoomId !== 0) return;
                if(!isCurrentRaidProtectionRoom(requested.roomId, enteredRoomId) || current.manageableRoomId !== requested.roomId) return;

                sendRaidProtectionSave(requested, true);
            },
            () => setConfirming(false),
            null,
            null,
            text('confirm.title', 'Turn on raid protection?')
        );
    };

    const protectionDisabled = !form.enabled;
    const banDisabled = !form.enabled || form.actionType === RAID_PROTECTION_ACTION_KICK;
    const guardDisabled = !form.enabled || !form.guardEnabled;
    const inactiveText = settings.lastRaidAtEpochSeconds > 0
        ? localizeWithFallback('raid.protection.settings.status.inactive.last', 'There is no raid going on. Last raid: %timestamp%', [ 'timestamp' ], [ new Date(settings.lastRaidAtEpochSeconds * 1000).toLocaleString() ])
        : text('status.inactive', 'There is no raid going on');

    return (
        <NativeTextTree>
            <OctaneCardView className="octane-raid-protection" frameStyle={ 3 } isResizable={ false } theme="primary-slim" uniqueKey="raid-protection-settings">
                <OctaneCardHeaderView headerText={ text('title', 'Raid protection') } onCloseClick={ close } />
                <OctaneCardContentView className="octane-raid-protection__content" overflow="hidden">
                    <div className="octane-raid-protection__advice">
                        { text('warning', 'Switch this on when you expect an unwanted raid. Start busy games or events with a lower sensitivity.') }
                    </div>
                    <div className="octane-raid-protection__card is-protection">
                        <label className="octane-raid-protection__toggle">
                            <Checkbox aria-label={ text('enabled', 'Turn on raid protection') } checked={ form.enabled } onCheckedChange={ checked => update({ enabled: checked }) } />
                            <span>{ text('enabled', 'Turn on raid protection') }</span>
                        </label>
                        <div className="octane-raid-protection__separator" />
                        <div className="octane-raid-protection__controls" data-disabled={ protectionDisabled || undefined }>
                            <div className="octane-raid-protection__row">
                                <span>{ text('detection.label', 'Sensitivity') }</span>
                                <UbuntuDropmenuView disabled={ protectionDisabled } label={ text('detection.label', 'Sensitivity') } options={ sensitivityOptions() } value={ form.sensitivity } onSelect={ value => update({ sensitivity: Number(value) }) } />
                            </div>
                            <div className="octane-raid-protection__row">
                                <span>{ text('action.label', 'When a raid is detected') }</span>
                                <UbuntuDropmenuView
                                    disabled={ protectionDisabled }
                                    label={ text('action.label', 'When a raid is detected') }
                                    options={ [
                                        { value: 0, label: text('action.kick', 'Kick the player out of the room') },
                                        { value: 1, label: text('action.temporary_ban', 'Temporary ban') }
                                    ] }
                                    value={ form.actionType }
                                    onSelect={ value => update({ actionType: Number(value) }) } />
                            </div>
                            <div className="octane-raid-protection__row" data-disabled={ banDisabled || undefined }>
                                <span>{ text('ban_duration', 'Ban length') }</span>
                                <UbuntuDropmenuView disabled={ banDisabled } label={ text('ban_duration', 'Ban length') } options={ durationOptions(RAID_PROTECTION_BAN_DURATIONS) } value={ form.banDurationSeconds } onSelect={ value => update({ banDurationSeconds: Number(value) }) } />
                            </div>
                        </div>
                    </div>
                    <div className="octane-raid-protection__card is-guard">
                        <label className="octane-raid-protection__toggle">
                            <Checkbox aria-label={ text('guard.enabled', 'Keep suspicious players out during a raid') } checked={ form.guardEnabled } onCheckedChange={ checked => update({ guardEnabled: checked }) } />
                            <span>{ text('guard.enabled', 'Keep suspicious players out during a raid') }</span>
                        </label>
                        <div className="octane-raid-protection__description">
                            { text('guard.description', 'During a raid suspicious players can be kept out of the room as well') }
                        </div>
                        <div className="octane-raid-protection__controls" data-disabled={ guardDisabled || undefined }>
                            <div className="octane-raid-protection__row">
                                <span>{ text('guard.duration', 'Keep players out for') }</span>
                                <UbuntuDropmenuView disabled={ guardDisabled } label={ text('guard.duration', 'Keep players out for') } options={ durationOptions(RAID_PROTECTION_GUARD_DURATIONS) } value={ form.guardDurationSeconds } onSelect={ value => update({ guardDurationSeconds: Number(value) }) } />
                            </div>
                            <div className="octane-raid-protection__row">
                                <span>{ text('guard.sensitivity', 'How strict should this be?') }</span>
                                <UbuntuDropmenuView disabled={ guardDisabled } label={ text('guard.sensitivity', 'How strict should this be?') } options={ sensitivityOptions() } value={ form.guardSensitivity } onSelect={ value => update({ guardSensitivity: Number(value) }) } />
                            </div>
                        </div>
                    </div>
                    <div className="octane-raid-protection__status" role="status">
                        { settings.incidentActive
                            ? <strong className="is-active">{ text('status.active', 'A raid is going on right now') }</strong>
                            : <span>{ inactiveText }</span> }
                    </div>
                    <div className="octane-raid-protection__actions">
                        <BaseButton className="octane-raid-protection__button is-cancel" type="button" onClick={ close }>
                            { text('cancel', 'Cancel') }
                        </BaseButton>
                        <BaseButton className="octane-raid-protection__button is-save" disabled={ busy } type="button" onClick={ save }>
                            { text('save', 'Save') }
                        </BaseButton>
                    </div>
                </OctaneCardContentView>
            </OctaneCardView>
        </NativeTextTree>
    );
};
15Sujets
1Dépôts
20Membres
3En 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