nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
actions /
WiredActionSendSignalView.tsx
WiredActionSendSignalView.tsx 11 Ko
Télécharger le fichier
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { LocalizeText, localizeWithFallback, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
import { Text } from '../../../../common';
import { useNotification, useWired } from '../../../../hooks';
import { WiredFurniSelectionSourceRow } from '../WiredFurniSelectionSourceRow';
import { FURNI_SOURCES, USER_SOURCES } from '../WiredSourcesSelector';
import { WiredActionBaseView } from './WiredActionBaseView';
import { Checkbox } from '../../../../common/Checkbox';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
const SOURCE_TRIGGER = 0;
const SOURCE_SELECTED = 100;
type SelectionMode = 'antenna' | 'furni';
const parseForwardIds = (data: string): number[] =>
{
if(!data || !data.length) return [];
const ids = new Set<number>();
for(const part of data.split(/[;,\t]/))
{
const trimmed = part.trim();
if(!trimmed.length) continue;
const value = parseInt(trimmed, 10);
if(!isNaN(value) && value > 0) ids.add(value);
}
return Array.from(ids);
};
export const WiredActionSendSignalView: FC<{}> = () =>
{
const [ furniSource, setFurniSource ] = useState<number>(SOURCE_TRIGGER);
const [ userSource, setUserSource ] = useState<number>(SOURCE_TRIGGER);
const [ signalPerFurni, setSignalPerFurni ] = useState(false);
const [ signalPerUser, setSignalPerUser ] = useState(false);
const [ antennaIds, setAntennaIds ] = useState<number[]>([]);
const [ forwardFurniIds, setForwardFurniIds ] = useState<number[]>([]);
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('antenna');
const highlightedIds = useRef<number[]>([]);
const { trigger = null, furniIds = [], setFurniIds = null, setFurniIds2 = null, setIntParams = null, setStringParam = null, setSourceTypes = null } = useWired();
const { showConfirm = null } = useNotification();
useEffect(() =>
{
if(!trigger) return;
// Official: [split per furni, split per user]. Furni source slot 0 is the antennas, slot 1 the
// furni to forward (100 = the second picks, stuffIds2); user slot 0 the users to forward.
const p = trigger.intData ?? [];
setFurniSource(trigger.furniSourceTypes?.[1] ?? SOURCE_TRIGGER);
setUserSource(trigger.userSourceTypes?.[0] ?? SOURCE_TRIGGER);
setSignalPerFurni(p[0] === 1);
setSignalPerUser(p[1] === 1);
setAntennaIds(trigger.selectedItems ?? []);
setForwardFurniIds(trigger.selectedItems2?.length ? [ ...trigger.selectedItems2 ] : parseForwardIds(trigger.stringData));
setSelectionMode('antenna');
}, [ trigger ]);
useEffect(() =>
{
if(selectionMode === 'antenna') setAntennaIds(furniIds);
else setForwardFurniIds(furniIds);
}, [ furniIds, selectionMode ]);
// AIR tints by pick list, not by the list being edited: the antennas (stuffIds) the first tint, the furni to
// forward (stuffIds2) the blue one.
const syncHighlights = useCallback((nextAntennaIds: number[], nextForwardFurniIds: number[], nextFurniSource: number) =>
{
const visibleForwardIds = (nextFurniSource === SOURCE_SELECTED) ? nextForwardFurniIds : [];
highlightedIds.current = WiredSelectionVisualizer.showPickLists(highlightedIds.current, nextAntennaIds, visibleForwardIds);
}, []);
useEffect(() =>
{
syncHighlights(antennaIds, forwardFurniIds, furniSource);
}, [ antennaIds, forwardFurniIds, furniSource, syncHighlights ]);
useEffect(() =>
{
WiredSelectionVisualizer.setEditingPickList((selectionMode === 'furni') ? 2 : 1);
}, [ selectionMode ]);
const switchSelection = useCallback((mode: SelectionMode) =>
{
if(mode === selectionMode) return;
if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return;
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
const nextForwardFurniIds = (selectionMode === 'furni') ? [ ...furniIds ] : [ ...forwardFurniIds ];
setAntennaIds(nextAntennaIds);
setForwardFurniIds(nextForwardFurniIds);
setSelectionMode(mode);
if(setFurniIds) setFurniIds([ ...((mode === 'antenna') ? nextAntennaIds : nextForwardFurniIds) ]);
}, [ selectionMode, furniSource, furniIds, antennaIds, forwardFurniIds, setFurniIds ]);
const onChangeFurniSource = (next: number) =>
{
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
setAntennaIds(nextAntennaIds);
if(forwardFurniIds.length || selectionMode === 'furni')
{
setForwardFurniIds([]);
}
if(selectionMode === 'furni')
{
if(setFurniIds) setFurniIds([ ...nextAntennaIds ]);
setSelectionMode('antenna');
}
setFurniSource(next);
};
const save = useCallback(() =>
{
const nextAntennaIds = (selectionMode === 'antenna') ? [ ...furniIds ] : [ ...antennaIds ];
const nextForwardFurniIds = (selectionMode === 'furni') ? [ ...furniIds ] : [ ...forwardFurniIds ];
setAntennaIds(nextAntennaIds);
setForwardFurniIds(nextForwardFurniIds);
if(selectionMode === 'furni')
{
setSelectionMode('antenna');
if(setFurniIds) setFurniIds([ ...nextAntennaIds ]);
}
setIntParams([ signalPerFurni ? 1 : 0, signalPerUser ? 1 : 0 ]);
setStringParam('');
setSourceTypes({ furni: [ SOURCE_SELECTED, furniSource ], users: [ userSource ] });
setFurniIds2((furniSource === SOURCE_SELECTED) ? nextForwardFurniIds.filter(id => (id > 0)) : []);
}, [ selectionMode, furniIds, antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, setFurniIds, setFurniIds2, setIntParams, setStringParam, setSourceTypes ]);
// Official: checking a split option first asks to confirm; cancelling leaves it unchecked.
const confirmSplit = (checked: boolean, apply: (value: boolean) => void) =>
{
if(!checked || !showConfirm) return apply(checked);
showConfirm(
localizeWithFallback('wiredfurni.params.signal_warning.desc', 'This option is rarely needed and adds to the wired usage of the room. Make sure you cannot avoid it before continuing.\n\nPress "ok" if you are sure.'),
() => apply(true),
() => apply(false),
null,
null,
localizeWithFallback('wiredfurni.params.signal_warning.title', 'Are you sure?')
);
};
useEffect(() =>
{
return () =>
{
WiredSelectionVisualizer.setEditingPickList(1);
if(!highlightedIds.current.length) return;
WiredSelectionVisualizer.clearSelectionShaderFromFurni(highlightedIds.current);
WiredSelectionVisualizer.clearSecondarySelectionShaderFromFurni(highlightedIds.current);
highlightedIds.current = [];
};
}, []);
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
return (
<NativeTextTree>
<WiredActionBaseView
hasSpecialInput={ true }
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID }
cardStyle={ { width: '400px' } }
save={ save }
selectionPreview={
<div className="flex flex-col gap-2">
<WiredFurniSelectionSourceRow
title={ localizeWithFallback('wiredfurni.params.sources.furni.title.signal_antenna', 'Antenne:') }
titleIsLiteral={ true }
options={ [ { value: SOURCE_SELECTED, label: 'wiredfurni.params.sources.furni.100' } ] }
value={ SOURCE_SELECTED }
selectionKind="primary"
selectionActive={ selectionMode === 'antenna' }
selectionCount={ antennaIds.length }
selectionLimit={ selectionLimit }
selectionEnabledValues={ [ SOURCE_SELECTED ] }
onChange={ () =>
{} }
onSelectionActivate={ () => switchSelection('antenna') } />
<WiredFurniSelectionSourceRow
title={ localizeWithFallback('wiredfurni.params.sources.furni.title.signal_forward', 'Furni to forward:') }
titleIsLiteral={ true }
options={ FURNI_SOURCES }
value={ furniSource }
selectionKind="secondary"
selectionActive={ selectionMode === 'furni' }
selectionCount={ forwardFurniIds.length }
selectionLimit={ selectionLimit }
selectionEnabledValues={ [ SOURCE_SELECTED ] }
onChange={ onChangeFurniSource }
onSelectionActivate={ () => switchSelection('furni') } />
<WiredFurniSelectionSourceRow
title={ localizeWithFallback('wiredfurni.params.sources.users.title.signal_forward', 'Users to forward:') }
titleIsLiteral={ true }
options={ USER_SOURCES }
value={ userSource }
selectionKind="secondary"
selectionActive={ false }
selectionCount={ 0 }
selectionLimit={ 0 }
selectionEnabledValues={ [] }
showSelectionToggle={ false }
onChange={ setUserSource } />
</div>
}
>
<div className="flex flex-col gap-3">
<Text bold>{ localizeWithFallback('wiredfurni.params.signal.send_options', LocalizeText('wiredfurni.params.signal.options')) }</Text>
<div className="form-check">
<Checkbox id="signal-per-furni" checked={ signalPerFurni } onCheckedChange={(nextChecked) => confirmSplit(nextChecked, setSignalPerFurni)} />
<label className="form-check-label" htmlFor="signal-per-furni">
<Text small>{ LocalizeText('wiredfurni.params.signal.split_furni') }</Text>
</label>
</div>
<div className="form-check">
<Checkbox id="signal-per-user" checked={ signalPerUser } onCheckedChange={(nextChecked) => confirmSplit(nextChecked, setSignalPerUser)} />
<label className="form-check-label" htmlFor="signal-per-user">
<Text small>{ LocalizeText('wiredfurni.params.signal.split_users') }</Text>
</label>
</div>
</div>
</WiredActionBaseView>
</NativeTextTree>
);
};
