mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 14:39:58 +01:00
[MIRROR] Changes how tgui handles static data [MDB IGNORE] (#11401)
* Changes how tgui handles static data (#64757) * Changes how tgui handles static data Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
This commit is contained in:
@@ -31,6 +31,8 @@
|
||||
var/closing = FALSE
|
||||
/// The status/visibility of the UI.
|
||||
var/status = UI_INTERACTIVE
|
||||
/// Timed refreshing state
|
||||
var/refreshing = FALSE
|
||||
/// Topic state used to determine status/interactability.
|
||||
var/datum/ui_state/state = null
|
||||
/// Rate limit client refreshes to prevent DoS.
|
||||
@@ -180,11 +182,17 @@
|
||||
/datum/tgui/proc/send_full_update(custom_data, force)
|
||||
if(!user.client || !initialized || closing)
|
||||
return
|
||||
if(!COOLDOWN_FINISHED(src, refresh_cooldown))
|
||||
refreshing = TRUE
|
||||
addtimer(CALLBACK(src, .proc/send_full_update), TGUI_REFRESH_FULL_UPDATE_COOLDOWN, TIMER_UNIQUE)
|
||||
return
|
||||
refreshing = FALSE
|
||||
var/should_update_data = force || status >= UI_UPDATE
|
||||
window.send_message("update", get_payload(
|
||||
custom_data,
|
||||
with_data = should_update_data,
|
||||
with_static_data = TRUE))
|
||||
COOLDOWN_START(src, refresh_cooldown, TGUI_REFRESH_FULL_UPDATE_COOLDOWN)
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -215,6 +223,7 @@
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"interface" = interface,
|
||||
"refreshing" = refreshing,
|
||||
"window" = list(
|
||||
"key" = window_key,
|
||||
"size" = window_size,
|
||||
@@ -303,9 +312,8 @@
|
||||
switch(type)
|
||||
if("ready")
|
||||
// Send a full update when the user manually refreshes the UI
|
||||
if (initialized && COOLDOWN_FINISHED(src, refresh_cooldown))
|
||||
if(initialized)
|
||||
send_full_update()
|
||||
COOLDOWN_START(src, refresh_cooldown, TGUI_REFRESH_FULL_UPDATE_COOLDOWN)
|
||||
initialized = TRUE
|
||||
if("pingReply")
|
||||
initialized = TRUE
|
||||
|
||||
@@ -123,15 +123,17 @@
|
||||
/datum/tgui_modal/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/tgui_modal/ui_data(mob/user)
|
||||
/datum/tgui_modal/ui_static_data(mob/user)
|
||||
. = list()
|
||||
.["autofocus"] = autofocus
|
||||
.["buttons"] = buttons
|
||||
.["message"] = message
|
||||
.["preferences"] = list()
|
||||
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["title"] = title
|
||||
|
||||
/datum/tgui_modal/ui_data(mob/user)
|
||||
. = list()
|
||||
if(timeout)
|
||||
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
|
||||
|
||||
@@ -143,16 +143,15 @@
|
||||
|
||||
/datum/tgui_list_input/ui_static_data(mob/user)
|
||||
. = list()
|
||||
.["init_value"] = default || items[1]
|
||||
.["items"] = items
|
||||
.["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["message"] = message
|
||||
.["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["title"] = title
|
||||
|
||||
/datum/tgui_list_input/ui_data(mob/user)
|
||||
. = list()
|
||||
.["init_value"] = default || items[1]
|
||||
.["message"] = message
|
||||
.["preferences"] = list()
|
||||
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["title"] = title
|
||||
if(timeout)
|
||||
.["timeout"] = clamp((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS), 0, 1)
|
||||
|
||||
|
||||
@@ -136,16 +136,18 @@
|
||||
/datum/tgui_input_number/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/tgui_input_number/ui_data(mob/user)
|
||||
/datum/tgui_input_number/ui_static_data(mob/user)
|
||||
. = list()
|
||||
.["init_value"] = default // Default is a reserved keyword
|
||||
.["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["max_value"] = max_value
|
||||
.["message"] = message
|
||||
.["min_value"] = min_value
|
||||
.["preferences"] = list()
|
||||
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["title"] = title
|
||||
|
||||
/datum/tgui_input_number/ui_data(mob/user)
|
||||
. = list()
|
||||
if(timeout)
|
||||
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
|
||||
|
||||
@@ -147,16 +147,18 @@
|
||||
/datum/tgui_input_text/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/tgui_input_text/ui_data(mob/user)
|
||||
/datum/tgui_input_text/ui_static_data(mob/user)
|
||||
. = list()
|
||||
.["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["max_length"] = max_length
|
||||
.["message"] = message
|
||||
.["multiline"] = multiline
|
||||
.["placeholder"] = default // Default is a reserved keyword
|
||||
.["preferences"] = list()
|
||||
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
|
||||
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
|
||||
.["title"] = title
|
||||
|
||||
/datum/tgui_input_text/ui_data(mob/user)
|
||||
. = list()
|
||||
if(timeout)
|
||||
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
|
||||
|
||||
@@ -253,6 +253,7 @@ type BackendState<TData> = {
|
||||
title: string,
|
||||
status: number,
|
||||
interface: string,
|
||||
refreshing: boolean,
|
||||
window: {
|
||||
key: string,
|
||||
size: [number, number],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Loader } from './common/Loader';
|
||||
import { Preferences } from './common/InputButtons';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { KEY_ENTER, KEY_ESCAPE, KEY_LEFT, KEY_RIGHT, KEY_SPACE, KEY_TAB } from '../../common/keycodes';
|
||||
import { Autofocus, Box, Button, Flex, Section, Stack } from '../components';
|
||||
@@ -8,8 +7,9 @@ import { Window } from '../layouts';
|
||||
type AlertModalData = {
|
||||
autofocus: boolean;
|
||||
buttons: string[];
|
||||
large_buttons: boolean;
|
||||
message: string;
|
||||
preferences: Preferences;
|
||||
swapped_buttons: boolean;
|
||||
timeout: number;
|
||||
title: string;
|
||||
};
|
||||
@@ -22,19 +22,18 @@ export const AlertModal = (_, context) => {
|
||||
const {
|
||||
autofocus,
|
||||
buttons = [],
|
||||
message,
|
||||
preferences,
|
||||
large_buttons,
|
||||
message = '',
|
||||
timeout,
|
||||
title,
|
||||
} = data;
|
||||
const { large_buttons } = preferences;
|
||||
const [selected, setSelected] = useLocalState<number>(context, 'selected', 0);
|
||||
// Dynamically sets window height
|
||||
// Dynamically sets window dimensions
|
||||
const windowHeight
|
||||
= 115
|
||||
+ (message.length > 30 ? Math.ceil(message.length / 3) : 0)
|
||||
+ (message.length && large_buttons ? 5 : 0)
|
||||
+ (buttons.length > 2 ? buttons.length * 25 : 0);
|
||||
= 115
|
||||
+ (message.length > 30 ? Math.ceil(message.length / 4) : 0)
|
||||
+ (message.length && large_buttons ? 5 : 0);
|
||||
const windowWidth = 325 + (buttons.length > 2 ? 55 : 0);
|
||||
const onKey = (direction: number) => {
|
||||
if (selected === 0 && direction === KEY_DECREMENT) {
|
||||
setSelected(buttons.length - 1);
|
||||
@@ -46,8 +45,8 @@ export const AlertModal = (_, context) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Window height={windowHeight} title={title} width={325}>
|
||||
{timeout && <Loader value={timeout} />}
|
||||
<Window height={windowHeight} title={title} width={windowWidth}>
|
||||
{!!timeout && <Loader value={timeout} />}
|
||||
<Window.Content
|
||||
onKeyDown={(e) => {
|
||||
const keyCode = window.event ? e.which : e.keyCode;
|
||||
@@ -59,19 +58,20 @@ export const AlertModal = (_, context) => {
|
||||
act('choose', { choice: buttons[selected] });
|
||||
} else if (keyCode === KEY_ESCAPE) {
|
||||
act('cancel');
|
||||
} else if (
|
||||
keyCode === KEY_LEFT
|
||||
|| (e.shiftKey && keyCode === KEY_TAB)
|
||||
) {
|
||||
} else if (keyCode === KEY_LEFT) {
|
||||
e.preventDefault();
|
||||
onKey(KEY_DECREMENT);
|
||||
} else if (keyCode === KEY_RIGHT || keyCode === KEY_TAB) {
|
||||
} else if (keyCode === KEY_TAB || keyCode === KEY_RIGHT) {
|
||||
e.preventDefault();
|
||||
onKey(KEY_INCREMENT);
|
||||
}
|
||||
}}>
|
||||
<Section fill>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item grow m={1}>
|
||||
<Box color="label" overflow="hidden">{message}</Box>
|
||||
<Box color="label" overflow="hidden">
|
||||
{message}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
{!!autofocus && <Autofocus />}
|
||||
@@ -91,19 +91,16 @@ export const AlertModal = (_, context) => {
|
||||
*/
|
||||
const ButtonDisplay = (props, context) => {
|
||||
const { data } = useBackend<AlertModalData>(context);
|
||||
const { buttons = [], preferences } = data;
|
||||
const { buttons = [], large_buttons, swapped_buttons } = data;
|
||||
const { selected } = props;
|
||||
const { large_buttons, swapped_buttons } = preferences;
|
||||
const buttonDirection
|
||||
= (buttons.length > 2 ? 'column' : 'row')
|
||||
+ (!swapped_buttons ? '-reverse' : '');
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align="center"
|
||||
direction={buttonDirection}
|
||||
direction={!swapped_buttons ? 'row-reverse' : 'row'}
|
||||
fill
|
||||
justify="space-around">
|
||||
justify="space-around"
|
||||
wrap>
|
||||
{buttons?.map((button, index) =>
|
||||
!!large_buttons && buttons.length < 3 ? (
|
||||
<Flex.Item grow key={index}>
|
||||
@@ -132,9 +129,9 @@ const ButtonDisplay = (props, context) => {
|
||||
*/
|
||||
const AlertButton = (props, context) => {
|
||||
const { act, data } = useBackend<AlertModalData>(context);
|
||||
const { preferences } = data;
|
||||
const { large_buttons } = preferences;
|
||||
const { large_buttons } = data;
|
||||
const { button, selected } = props;
|
||||
const buttonWidth = button.length > 7 ? button.length : 7;
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -146,7 +143,8 @@ const AlertButton = (props, context) => {
|
||||
pr={2}
|
||||
pt={large_buttons ? 0.33 : 0}
|
||||
selected={selected}
|
||||
textAlign="center">
|
||||
textAlign="center"
|
||||
width={!large_buttons && buttonWidth}>
|
||||
{!large_buttons ? button : button.toUpperCase()}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
import { Loader } from './common/Loader';
|
||||
import { InputButtons, Preferences } from './common/InputButtons';
|
||||
import { InputButtons } from './common/InputButtons';
|
||||
import { Button, Input, Section, Stack } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { KEY_A, KEY_DOWN, KEY_ESCAPE, KEY_ENTER, KEY_UP, KEY_Z } from '../../common/keycodes';
|
||||
import { Window } from '../layouts';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
|
||||
type ListInputData = {
|
||||
items: string[];
|
||||
message: string;
|
||||
init_value: string;
|
||||
preferences: Preferences;
|
||||
items: string[];
|
||||
large_buttons: boolean;
|
||||
message: string;
|
||||
timeout: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const ListInputModal = (_, context) => {
|
||||
const { act, data } = useBackend<ListInputData>(context);
|
||||
const { items = [], message, init_value, preferences, timeout, title } = data;
|
||||
const { large_buttons } = preferences;
|
||||
const {
|
||||
items = [],
|
||||
message = "",
|
||||
init_value,
|
||||
large_buttons,
|
||||
timeout,
|
||||
title,
|
||||
} = data;
|
||||
const [selected, setSelected] = useLocalState<number>(
|
||||
context,
|
||||
'selected',
|
||||
@@ -98,7 +104,7 @@ export const ListInputModal = (_, context) => {
|
||||
);
|
||||
// Dynamically changes the window height based on the message.
|
||||
const windowHeight
|
||||
= 325 + Math.ceil(message?.length / 3) + (large_buttons ? 5 : 0);
|
||||
= 325 + Math.ceil(message.length / 3) + (large_buttons ? 5 : 0);
|
||||
// Grabs the cursor when no search bar is visible.
|
||||
if (!searchBarVisible) {
|
||||
setTimeout(() => document!.getElementById(selected.toString())?.focus(), 1);
|
||||
@@ -131,15 +137,16 @@ export const ListInputModal = (_, context) => {
|
||||
buttons={
|
||||
<Button
|
||||
compact
|
||||
icon={searchBarVisible ? "search" : "font"}
|
||||
icon={searchBarVisible ? 'search' : 'font'}
|
||||
selected
|
||||
tooltip={searchBarVisible
|
||||
? "Search Mode. Type to search or use arrow keys to select manually."
|
||||
: "Hotkey Mode. Type a letter to jump to the first match. Enter to select."}
|
||||
tooltip={
|
||||
searchBarVisible
|
||||
? 'Search Mode. Type to search or use arrow keys to select manually.'
|
||||
: 'Hotkey Mode. Type a letter to jump to the first match. Enter to select.'
|
||||
}
|
||||
tooltipPosition="left"
|
||||
onClick={() => onSearchBarToggle()}
|
||||
/>
|
||||
|
||||
}
|
||||
className="ListInput__Section"
|
||||
fill
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { Loader } from './common/Loader';
|
||||
import { InputButtons, Preferences } from './common/InputButtons';
|
||||
import { InputButtons } from './common/InputButtons';
|
||||
import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, RestrictedInput, Section, Stack } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type NumberInputData = {
|
||||
init_value: number;
|
||||
large_buttons: boolean;
|
||||
max_value: number | null;
|
||||
message: string;
|
||||
min_value: number | null;
|
||||
init_value: number;
|
||||
preferences: Preferences;
|
||||
timeout: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const NumberInputModal = (_, context) => {
|
||||
const { act, data } = useBackend<NumberInputData>(context);
|
||||
const { message, init_value, preferences, timeout, title } = data;
|
||||
const { large_buttons } = preferences;
|
||||
const { init_value, large_buttons, message = "", timeout, title }
|
||||
= data;
|
||||
const [input, setInput] = useLocalState(context, 'input', init_value);
|
||||
const onChange = (value: number) => {
|
||||
if (value === input) {
|
||||
@@ -34,8 +34,8 @@ export const NumberInputModal = (_, context) => {
|
||||
};
|
||||
// Dynamically changes the window height based on the message.
|
||||
const windowHeight
|
||||
= 130
|
||||
+ Math.ceil(message.length / 3)
|
||||
= 140
|
||||
+ (message.length > 30 ? Math.ceil(message.length / 3) : 0)
|
||||
+ (message.length && large_buttons ? 5 : 0);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Loader } from './common/Loader';
|
||||
import { InputButtons, Preferences } from './common/InputButtons';
|
||||
import { InputButtons } from './common/InputButtons';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { KEY_ENTER, KEY_ESCAPE } from '../../common/keycodes';
|
||||
import { Box, Section, Stack, TextArea } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type TextInputData = {
|
||||
large_buttons: boolean;
|
||||
max_length: number;
|
||||
message: string;
|
||||
multiline: boolean;
|
||||
placeholder: string;
|
||||
preferences: Preferences;
|
||||
timeout: number;
|
||||
title: string;
|
||||
};
|
||||
@@ -18,15 +18,14 @@ type TextInputData = {
|
||||
export const TextInputModal = (_, context) => {
|
||||
const { act, data } = useBackend<TextInputData>(context);
|
||||
const {
|
||||
large_buttons,
|
||||
max_length,
|
||||
message,
|
||||
message = "",
|
||||
multiline,
|
||||
placeholder,
|
||||
preferences,
|
||||
timeout,
|
||||
title,
|
||||
} = data;
|
||||
const { large_buttons } = preferences;
|
||||
const [input, setInput] = useLocalState<string>(
|
||||
context,
|
||||
'input',
|
||||
@@ -40,8 +39,8 @@ export const TextInputModal = (_, context) => {
|
||||
};
|
||||
// Dynamically changes the window height based on the message.
|
||||
const windowHeight
|
||||
= 125
|
||||
+ Math.ceil(message.length / 3)
|
||||
= 135
|
||||
+ (message.length > 30 ? Math.ceil(message.length / 4) : 0)
|
||||
+ (multiline || input.length >= 30 ? 75 : 0)
|
||||
+ (message.length && large_buttons ? 5 : 0);
|
||||
|
||||
@@ -95,6 +94,7 @@ const InputArea = (props, context) => {
|
||||
const keyCode = window.event ? event.which : event.keyCode;
|
||||
if (keyCode === KEY_ENTER) {
|
||||
act('submit', { entry: input });
|
||||
event.preventDefault();
|
||||
}
|
||||
if (keyCode === KEY_ESCAPE) {
|
||||
act('cancel');
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useBackend } from '../../backend';
|
||||
import { Box, Button, Flex } from '../../components';
|
||||
|
||||
type InputButtonsData = {
|
||||
preferences: Preferences;
|
||||
large_buttons: boolean;
|
||||
swapped_buttons: boolean;
|
||||
};
|
||||
|
||||
type InputButtonsProps = {
|
||||
@@ -10,14 +11,9 @@ type InputButtonsProps = {
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type Preferences = {
|
||||
large_buttons: boolean;
|
||||
swapped_buttons: boolean;
|
||||
};
|
||||
|
||||
export const InputButtons = (props: InputButtonsProps, context) => {
|
||||
const { act, data } = useBackend<InputButtonsData>(context);
|
||||
const { large_buttons, swapped_buttons } = data.preferences;
|
||||
const { large_buttons, swapped_buttons } = data;
|
||||
const { input, message } = props;
|
||||
const submitButton = (
|
||||
<Button
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { selectBackend } from './backend';
|
||||
import { Icon, Stack } from './components';
|
||||
import { selectDebug } from './debug/selectors';
|
||||
import { Window } from './layouts';
|
||||
|
||||
@@ -33,12 +34,32 @@ const SuspendedWindow = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const RefreshingWindow = () => {
|
||||
return (
|
||||
<Window height={130} title="Loading" width={150}>
|
||||
<Window.Content>
|
||||
<Stack align="center" fill justify="center" vertical>
|
||||
<Stack.Item>
|
||||
<Icon color="blue" name="toolbox" spin size={4} />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
Please wait...
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const getRoutedComponent = store => {
|
||||
const state = store.getState();
|
||||
const { suspended, config } = selectBackend(state);
|
||||
if (suspended) {
|
||||
return SuspendedWindow;
|
||||
}
|
||||
if (config.refreshing) {
|
||||
return RefreshingWindow;
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const debug = selectDebug(state);
|
||||
// Show a kitchen sink
|
||||
|
||||
Reference in New Issue
Block a user