mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
feat(wired): update source-aware furni and signal UI
This commit is contained in:
@@ -1,185 +1,213 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText, WiredFurniType } from '../../../../api';
|
||||
import { Text } from '../../../../common';
|
||||
import { LocalizeText, WiredFurniType, WiredSelectionVisualizer } from '../../../../api';
|
||||
import { Button, Text } from '../../../../common';
|
||||
import { useWired } from '../../../../hooks';
|
||||
import { WiredSourcesSelector } from '../WiredSourcesSelector';
|
||||
import { WiredActionBaseView } from './WiredActionBaseView';
|
||||
|
||||
const ANTENNA_PICKED = 0;
|
||||
const ANTENNA_TRIGGER = 1;
|
||||
const ANTENNA_INTERACTION_TYPES = [ 'antenna' ];
|
||||
|
||||
const FORWARD_NONE = 0;
|
||||
const FORWARD_TRIGGER = 1;
|
||||
const FORWARD_SELECTOR = 200;
|
||||
const FORWARD_SIGNAL = 201;
|
||||
const SOURCE_TRIGGER = 0;
|
||||
const SOURCE_SELECTED = 100;
|
||||
|
||||
const FURNI_FORWARD_OPTIONS = [
|
||||
{ value: FORWARD_TRIGGER, label: 'wiredfurni.params.sources.furni.0' },
|
||||
{ value: FORWARD_SELECTOR, label: 'wiredfurni.params.sources.furni.200' },
|
||||
{ value: FORWARD_SIGNAL, label: 'wiredfurni.params.sources.furni.201' },
|
||||
];
|
||||
const FORWARD_ITEM_DELIMITER = ';';
|
||||
|
||||
const USER_FORWARD_OPTIONS = [
|
||||
{ value: FORWARD_TRIGGER, label: 'wiredfurni.params.sources.users.0' },
|
||||
{ value: FORWARD_SELECTOR, label: 'wiredfurni.params.sources.users.200' },
|
||||
{ value: FORWARD_SIGNAL, label: 'wiredfurni.params.sources.users.201' },
|
||||
];
|
||||
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);
|
||||
};
|
||||
|
||||
const serializeForwardIds = (ids: number[]): string =>
|
||||
{
|
||||
if(!ids || !ids.length) return '';
|
||||
|
||||
return ids.filter(id => (id > 0)).join(FORWARD_ITEM_DELIMITER);
|
||||
};
|
||||
|
||||
export const WiredActionSendSignalView: FC<{}> = () =>
|
||||
{
|
||||
const [ antennaSource, setAntennaSource ] = useState(ANTENNA_PICKED);
|
||||
const [ furniForward, setFurniForward ] = useState(FORWARD_NONE);
|
||||
const [ userForward, setUserForward ] = useState(FORWARD_NONE);
|
||||
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 [ showAdvanced, setShowAdvanced ] = useState(false);
|
||||
const [ antennaIds, setAntennaIds ] = useState<number[]>([]);
|
||||
const [ forwardFurniIds, setForwardFurniIds ] = useState<number[]>([]);
|
||||
const [ selectionMode, setSelectionMode ] = useState<SelectionMode>('antenna');
|
||||
|
||||
const { trigger = null, setIntParams } = useWired();
|
||||
const { trigger = null, furniIds = [], setFurniIds = null, setIntParams = null, setStringParam = null, setAllowedInteractionTypes = null } = useWired();
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!trigger) return;
|
||||
|
||||
const p = trigger.intData;
|
||||
if(p.length >= 1) setAntennaSource(p[0]);
|
||||
if(p.length >= 2) setFurniForward(p[1]);
|
||||
if(p.length >= 3) setUserForward(p[2]);
|
||||
if(p.length >= 4) setSignalPerFurni(p[3] === 1);
|
||||
if(p.length >= 5) setSignalPerUser(p[4] === 1);
|
||||
if(p.length > 1) setFurniSource(p[1]);
|
||||
else setFurniSource(SOURCE_TRIGGER);
|
||||
|
||||
if(p.length >= 1 && (p[0] !== ANTENNA_PICKED || p[1] !== FORWARD_NONE ||
|
||||
p[2] !== FORWARD_NONE || (p.length >= 4 && p[3] === 1) || (p.length >= 5 && p[4] === 1)))
|
||||
{
|
||||
setShowAdvanced(true);
|
||||
}
|
||||
if(p.length > 2) setUserSource(p[2]);
|
||||
else setUserSource(SOURCE_TRIGGER);
|
||||
|
||||
setSignalPerFurni(p.length > 3 && p[3] === 1);
|
||||
setSignalPerUser(p.length > 4 && p[4] === 1);
|
||||
|
||||
setAntennaIds(trigger.selectedItems ?? []);
|
||||
setForwardFurniIds(parseForwardIds(trigger.stringData));
|
||||
setSelectionMode('antenna');
|
||||
}, [ trigger ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(selectionMode === 'antenna') setAllowedInteractionTypes(ANTENNA_INTERACTION_TYPES);
|
||||
else setAllowedInteractionTypes(null);
|
||||
|
||||
return () => setAllowedInteractionTypes(null);
|
||||
}, [ selectionMode, setAllowedInteractionTypes ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(selectionMode === 'antenna') setAntennaIds(furniIds);
|
||||
else setForwardFurniIds(furniIds);
|
||||
}, [ furniIds, selectionMode ]);
|
||||
|
||||
const applySelection = useCallback((nextIds: number[]) =>
|
||||
{
|
||||
if(!setFurniIds) return;
|
||||
|
||||
setFurniIds(prev =>
|
||||
{
|
||||
if(prev && prev.length) WiredSelectionVisualizer.clearSelectionShaderFromFurni(prev);
|
||||
if(nextIds && nextIds.length) WiredSelectionVisualizer.applySelectionShaderToFurni(nextIds);
|
||||
|
||||
return [ ...nextIds ];
|
||||
});
|
||||
}, [ setFurniIds ]);
|
||||
|
||||
const switchSelection = useCallback((mode: SelectionMode) =>
|
||||
{
|
||||
if(mode === selectionMode) return;
|
||||
if(mode === 'furni' && furniSource !== SOURCE_SELECTED) return;
|
||||
|
||||
const nextIds = (mode === 'antenna') ? antennaIds : forwardFurniIds;
|
||||
applySelection(nextIds);
|
||||
setSelectionMode(mode);
|
||||
}, [ selectionMode, furniSource, antennaIds, forwardFurniIds, applySelection ]);
|
||||
|
||||
const onChangeFurniSource = (next: number) =>
|
||||
{
|
||||
if(forwardFurniIds.length) setForwardFurniIds([]);
|
||||
|
||||
if(selectionMode === 'furni')
|
||||
{
|
||||
applySelection(antennaIds);
|
||||
setSelectionMode('antenna');
|
||||
}
|
||||
|
||||
setFurniSource(next);
|
||||
};
|
||||
|
||||
const save = useCallback(() =>
|
||||
{
|
||||
if(selectionMode === 'furni')
|
||||
{
|
||||
setSelectionMode('antenna');
|
||||
applySelection(antennaIds);
|
||||
}
|
||||
|
||||
const antennaSource = (antennaIds && antennaIds.length) ? antennaIds[0] : 0;
|
||||
|
||||
setIntParams([
|
||||
antennaSource,
|
||||
furniForward,
|
||||
userForward,
|
||||
furniSource,
|
||||
userSource,
|
||||
signalPerFurni ? 1 : 0,
|
||||
signalPerUser ? 1 : 0,
|
||||
0,
|
||||
]);
|
||||
}, [ antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, setIntParams ]);
|
||||
|
||||
setStringParam(serializeForwardIds(forwardFurniIds));
|
||||
}, [ selectionMode, antennaIds, furniSource, userSource, signalPerFurni, signalPerUser, forwardFurniIds, setIntParams, setStringParam, applySelection, setSelectionMode ]);
|
||||
|
||||
const selectionLimit = trigger?.maximumItemSelectionCount ?? 0;
|
||||
const forwardSelectionEnabled = (furniSource === SOURCE_SELECTED);
|
||||
|
||||
return (
|
||||
<WiredActionBaseView
|
||||
hasSpecialInput={ true }
|
||||
requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID }
|
||||
cardStyle={ { width: '400px' } }
|
||||
save={ save }>
|
||||
<div className="flex flex-col gap-2">
|
||||
|
||||
<div
|
||||
className="cursor-pointer text-center"
|
||||
onClick={ () => setShowAdvanced(!showAdvanced) }>
|
||||
<Text small underline>
|
||||
{ showAdvanced
|
||||
? LocalizeText('wiredfurni.params.hide_advanced')
|
||||
: LocalizeText('wiredfurni.params.show_advanced') }
|
||||
</Text>
|
||||
cardStyle={ { width: '400px' } }
|
||||
save={ save }
|
||||
footer={ (
|
||||
<WiredSourcesSelector
|
||||
showFurni={ true }
|
||||
showUsers={ true }
|
||||
furniSource={ furniSource }
|
||||
userSource={ userSource }
|
||||
onChangeFurni={ onChangeFurniSource }
|
||||
onChangeUsers={ setUserSource } />
|
||||
) }>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Antenne selezionate</Text>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant={ (selectionMode === 'antenna') ? 'primary' : 'secondary' }
|
||||
onClick={ () => switchSelection('antenna') }>
|
||||
Antenne
|
||||
</Button>
|
||||
<Text small>{ selectionLimit ? `${ antennaIds.length }/${ selectionLimit }` : antennaIds.length }</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text bold>Furni selezionati</Text>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant={ (selectionMode === 'furni') ? 'primary' : 'secondary' }
|
||||
disabled={ !forwardSelectionEnabled }
|
||||
onClick={ () => switchSelection('furni') }>
|
||||
Furni
|
||||
</Button>
|
||||
<Text small>{ selectionLimit ? `${ forwardFurniIds.length }/${ selectionLimit }` : forwardFurniIds.length }</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ showAdvanced && <>
|
||||
|
||||
{ /* --- Antennas --- */ }
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.signal_antenna') }</Text>
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-antenna-toggle"
|
||||
role="switch"
|
||||
checked={ antennaSource === ANTENNA_PICKED }
|
||||
onChange={ () => setAntennaSource(antennaSource === ANTENNA_PICKED ? ANTENNA_TRIGGER : ANTENNA_PICKED) } />
|
||||
<label className="form-check-label" htmlFor="signal-antenna-toggle">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.sources.furni.100') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{ /* --- Furni to forward --- */ }
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.furni.title.signal_forward') }</Text>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="form-check form-switch flex-1 mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-furni-toggle"
|
||||
role="switch"
|
||||
checked={ furniForward !== FORWARD_NONE }
|
||||
onChange={ e => setFurniForward(e.target.checked ? FORWARD_TRIGGER : FORWARD_NONE) } />
|
||||
<label className="form-check-label" htmlFor="signal-furni-toggle">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.sources.furni.200') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
{ furniForward !== FORWARD_NONE &&
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={ { width: 'auto', minWidth: '160px' } }
|
||||
value={ furniForward }
|
||||
onChange={ e => setFurniForward(parseInt(e.target.value)) }>
|
||||
{ FURNI_FORWARD_OPTIONS.map(opt =>
|
||||
<option key={ opt.value } value={ opt.value }>{ LocalizeText(opt.label) }</option>
|
||||
) }
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
|
||||
{ /* --- Users to forward --- */ }
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.sources.users.title.signal_forward') }</Text>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="form-check form-switch flex-1 mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-user-toggle"
|
||||
role="switch"
|
||||
checked={ userForward !== FORWARD_NONE }
|
||||
onChange={ e => setUserForward(e.target.checked ? FORWARD_TRIGGER : FORWARD_NONE) } />
|
||||
<label className="form-check-label" htmlFor="signal-user-toggle">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.sources.users.0') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
{ userForward !== FORWARD_NONE &&
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={ { width: 'auto', minWidth: '160px' } }
|
||||
value={ userForward }
|
||||
onChange={ e => setUserForward(parseInt(e.target.value)) }>
|
||||
{ USER_FORWARD_OPTIONS.map(opt =>
|
||||
<option key={ opt.value } value={ opt.value }>{ LocalizeText(opt.label) }</option>
|
||||
) }
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.signal.options') }</Text>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-per-furni"
|
||||
checked={ signalPerFurni }
|
||||
onChange={ e => setSignalPerFurni(e.target.checked) } />
|
||||
<label className="form-check-label" htmlFor="signal-per-furni">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.signal.split_furni') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-per-user"
|
||||
checked={ signalPerUser }
|
||||
onChange={ e => setSignalPerUser(e.target.checked) } />
|
||||
<label className="form-check-label" htmlFor="signal-per-user">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.signal.split_users') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
</> }
|
||||
|
||||
<Text bold>{ LocalizeText('wiredfurni.params.signal.options') }</Text>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-per-furni"
|
||||
checked={ signalPerFurni }
|
||||
onChange={ e => setSignalPerFurni(e.target.checked) } />
|
||||
<label className="form-check-label" htmlFor="signal-per-furni">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.signal.split_furni') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-check-input"
|
||||
id="signal-per-user"
|
||||
checked={ signalPerUser }
|
||||
onChange={ e => setSignalPerUser(e.target.checked) } />
|
||||
<label className="form-check-label" htmlFor="signal-per-user">
|
||||
<Text small>{ LocalizeText('wiredfurni.params.signal.split_users') }</Text>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</WiredActionBaseView>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user