TGUI say: Upgrades chat input with modern features (#23907)

* initial stuff (broken)

* initial stuff (works)

* fix most other things

* fix thinking indicator

* file name moment

* contra review

* better logs

* snowball feedback

* expanded drag handles

* Update code/modules/tgui_input/say_modal/tgui_say_modal.dm

Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com>

* Update code/modules/tgui_input/say_modal/tgui_say_modal.dm

Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com>

* Drag handle

* fix missing colours

* TGUI rebuild (im clever)

* Make msay useable by admins again (i hope)

* fixed blacklist channel leaks due to lack of user eyes

* clear old TGUIsay macros on keybind update

* TGUI say tweaks

* Update code/modules/tgui_input/say_modal/tgui_say_modal.dm

* whisper is now a channel

* remove placeholder, shorten width

* tidies prefs, fix lightmode, refactor typing/thinking indicators

* clarify more prefs

* TG Typing Indicators and increace layer

* modifiers?

* makes the TGUI say macros serverside

* add checks for muted stuff

* dgamer review

* fix talk and type issue

* Update code/datums/keybindings/communication_keybinds.dm

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>

* review and binary stuff

* Removes force say from TGUI say

* updated

* Apply suggestions from code review

Co-authored-by: Aylong <69762909+AyIong@users.noreply.github.com>

* remove verb

* le rebuild

* better behaviour on illegal entries

---------

Co-authored-by: Aylong <alexanderkitsa@gmail.com>
Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com>
Co-authored-by: GDN <Roanrichards1@Gmail.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: Aylong <69762909+AyIong@users.noreply.github.com>
This commit is contained in:
S34N
2024-02-15 19:31:29 +01:00
committed by GitHub
co-authored by Contrabang Luc Aylong Aylong GDN
parent c120721cde
commit 3be2d5f8b6
60 changed files with 1932 additions and 401 deletions
@@ -0,0 +1,123 @@
/**
* Creates a JSON encoded message to open TGUI say modals properly.
*
* Arguments:
* channel - The channel to open the modal in.
* Returns:
* string - A JSON encoded message to open the modal.
*/
/client/proc/tgui_say_create_open_command(channel)
var/message = TGUI_CREATE_MESSAGE("open", list(
channel = channel,
))
return "\".output tgui_say.browser:update [message]\""
/**
* The tgui say modal. This initializes an input window which hides until
* the user presses one of the speech hotkeys. Once something is entered, it will
* delegate the speech to the proper channel.
*/
/datum/tgui_say
/// The user who opened the window
var/client/client
/// The modal window
var/datum/tgui_window/window
/// Boolean for whether the tgui_say was opened by the user.
var/window_open
/** Creates the new input window to exist in the background. */
/datum/tgui_say/New(client/client, id)
src.client = client
window = new(client, id)
winset(client, "tgui_say", "size=1,1;is-visible=0;")
window.subscribe(src, PROC_REF(on_message))
window.is_browser = TRUE
/**
* After a brief period, injects the scripts into
* the window to listen for open commands.
*/
/datum/tgui_say/proc/initialize()
set waitfor = FALSE
// Sleep to defer initialization to after client constructor
sleep(3)
window.initialize(
strict_mode = TRUE,
fancy = TRUE,
inline_css = file("tgui/public/tgui-say.bundle.css"),
inline_js = file("tgui/public/tgui-say.bundle.js"),
);
/**
* Ensures nothing funny is going on window load.
* Minimizes the window, sets max length, closes all
* typing and thinking indicators. This is triggered
* as soon as the window sends the "ready" message.
*/
/datum/tgui_say/proc/load()
window_open = FALSE
winset(client, "tgui_say", "pos=848,500;size=275,30;is-visible=0;")
window.send_message("props", list(
"lightMode" = (client.prefs.toggles2 & PREFTOGGLE_2_ENABLE_TGUI_SAY_LIGHT_MODE),
"maxLength" = MAX_MESSAGE_LEN,
))
stop_thinking()
return TRUE
/**
* Sets the window as "opened" server side, though it is already
* visible to the user. We do this to set local vars &
* start typing (if enabled and in an IC channel).
*
* Arguments:
* payload - A list containing the channel the window was opened in.
*/
/datum/tgui_say/proc/open(payload)
if(!payload?["channel"])
CRASH("No channel provided to an open TGUI-Say")
window_open = TRUE
switch(payload["channel"])
if(ME_CHANNEL, RADIO_CHANNEL, SAY_CHANNEL, WHISPER_CHANNEL)
start_thinking()
return TRUE
/**
* Closes the window serverside. Closes any open chat bubbles
* regardless of preference.
*/
/datum/tgui_say/proc/close()
window_open = FALSE
stop_thinking()
stop_typing()
/**
* The equivalent of ui_act, this waits on messages from the window
* and delegates actions.
*/
/datum/tgui_say/proc/on_message(type, payload)
switch(type)
if("ready")
load()
return TRUE
if("open")
open(payload)
return TRUE
if("close")
close()
return TRUE
if("thinking")
if(payload["visible"] == TRUE)
start_thinking()
return TRUE
if(payload["visible"] == FALSE)
stop_thinking()
return TRUE
return FALSE
if("typing")
start_typing(payload["isMeChannel"])
return TRUE
if("entry")
handle_entry(payload)
return TRUE
return FALSE
@@ -0,0 +1,61 @@
/**
* Delegates the speech to the proper channel.
*
* Arguments:
* entry - the text to broadcast
* channel - the channel to broadcast in
* Returns:
* boolean - on success or failure
*/
/datum/tgui_say/proc/delegate_speech(entry, channel)
switch(channel)
if(SAY_CHANNEL)
client.mob.say_verb(entry)
return TRUE
if(RADIO_CHANNEL)
client.mob.say_verb((isliving(client.mob) ? ";" : "") + entry)
return TRUE
if(WHISPER_CHANNEL)
client.mob.whisper(entry)
return TRUE
if(ME_CHANNEL)
client.mob.me_verb(entry)
return TRUE
if(OOC_CHANNEL)
client.ooc(entry)
return TRUE
if(LOOC_CHANNEL)
client.looc(entry)
return TRUE
if(ADMIN_CHANNEL)
client.cmd_admin_say(entry)
return TRUE
if(MENTOR_CHANNEL)
client.cmd_mentor_say(entry)
return TRUE
if(DSAY_CHANNEL)
client.dsay(entry)
return TRUE
return FALSE
/**
* Handles text entry and forced speech.
*
* Arguments:
* payload - a string list containing entry & channel
* Returns:
* boolean - success or failure
*/
/datum/tgui_say/proc/handle_entry(payload)
if(!payload?["channel"] || !payload["entry"])
var/hacker_man_ckey = usr.client.ckey
qdel(usr.client)
message_admins("[hacker_man_ckey] was kicked for attemping to send a null message to TGUI-say.")
CRASH("[hacker_man_ckey] entered in a null payload to the chat window.")
if(length(payload["entry"]) > MAX_MESSAGE_LEN)
var/hacker_man_ckey = usr.client.ckey
qdel(usr.client)
message_admins("[hacker_man_ckey] was kicked for attemping to bypass TGUI-say character limits.")
CRASH("[hacker_man_ckey] has entered more characters than allowed into a TGUI-Say.")
delegate_speech(payload["entry"], payload["channel"])
return TRUE
@@ -0,0 +1,41 @@
/** Sets the mob as "thinking" - with indicator */
/datum/tgui_say/proc/start_thinking()
if(!client?.mob || !window_open)
return FALSE
/// Special exemptions
if(isabductor(client.mob))
return FALSE
client.mob.set_thinking_indicator(TRUE)
addtimer(CALLBACK(src, PROC_REF(stop_thinking)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE)
/** Removes typing/thinking indicators and flags the mob as not thinking */
/datum/tgui_say/proc/stop_thinking()
if(!client?.mob)
return FALSE
client.mob.set_thinking_indicator(FALSE)
/**
* Handles the user typing. After a brief period of inactivity,
* signals the client mob to revert to the "thinking" icon.
*/
/datum/tgui_say/proc/start_typing(me = FALSE)
if(!client?.mob)
return FALSE
client.mob.set_typing_indicator(FALSE)
client.mob.set_thinking_indicator(FALSE)
if(!window_open)
return FALSE
client.mob.set_typing_indicator(TRUE, me)
addtimer(CALLBACK(src, PROC_REF(stop_typing)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE)
/**
* Callback to remove the typing indicator after a brief period of inactivity.
* If the user was typing IC, the thinking indicator is shown.
*/
/datum/tgui_say/proc/stop_typing()
if(!client?.mob)
return FALSE
client.mob.set_typing_indicator(FALSE)
if(!window_open)
return FALSE
client.mob.set_thinking_indicator(TRUE)