From eb65b5721c870657ca3c3c3cb4e13d32d2bf5f32 Mon Sep 17 00:00:00 2001 From: CHOMPStation2 <58959929+CHOMPStation2@users.noreply.github.com> Date: Sat, 25 Nov 2023 05:36:55 -0700 Subject: [PATCH] [MIRROR] tgchat part 1 (#7273) Co-authored-by: Heroman3003 <31296024+Heroman3003@users.noreply.github.com> Co-authored-by: Selis --- .gitignore | 9 + code/__defines/chat.dm | 26 + code/__defines/logging.dm | 2 + code/__defines/subsystems.dm | 21 + code/__defines/tgui.dm | 6 +- code/_global_vars/_regexes.dm | 2 + code/_helpers/logging.dm | 20 - code/_helpers/logging/ui.dm | 36 + code/modules/asset_cache/asset_list_items.dm | 32 - .../modules/asset_cache/assets/fontawesome.dm | 9 + code/modules/asset_cache/assets/jquery.dm | 4 + code/modules/asset_cache/assets/tgfont.dm | 6 + code/modules/asset_cache/assets/tgui.dm | 15 + code/modules/client/client defines.dm | 38 +- interface/fonts.dm | 6 + tgui/packages/tgui-panel/Notifications.js | 28 + tgui/packages/tgui-panel/Panel.js | 128 ++ .../tgui-panel/audio/NowPlayingWidget.js | 109 ++ tgui/packages/tgui-panel/audio/hooks.js | 17 + tgui/packages/tgui-panel/audio/index.js | 10 + tgui/packages/tgui-panel/audio/middleware.js | 37 + tgui/packages/tgui-panel/audio/player.js | 117 ++ tgui/packages/tgui-panel/audio/reducer.js | 50 + tgui/packages/tgui-panel/audio/selectors.js | 7 + .../tgui-panel/chat/ChatPageSettings.js | 89 ++ tgui/packages/tgui-panel/chat/ChatPanel.js | 73 ++ tgui/packages/tgui-panel/chat/ChatTabs.js | 68 + tgui/packages/tgui-panel/chat/actions.js | 21 + tgui/packages/tgui-panel/chat/constants.js | 148 +++ tgui/packages/tgui-panel/chat/index.js | 11 + tgui/packages/tgui-panel/chat/middleware.js | 178 +++ tgui/packages/tgui-panel/chat/model.js | 56 + tgui/packages/tgui-panel/chat/reducer.js | 183 +++ tgui/packages/tgui-panel/chat/renderer.js | 608 +++++++++ .../tgui-panel/chat/replaceInTextNode.js | 204 +++ tgui/packages/tgui-panel/chat/selectors.js | 17 + tgui/packages/tgui-panel/game/actions.js | 11 + tgui/packages/tgui-panel/game/constants.js | 7 + tgui/packages/tgui-panel/game/hooks.js | 12 + tgui/packages/tgui-panel/game/index.js | 9 + tgui/packages/tgui-panel/game/middleware.js | 53 + tgui/packages/tgui-panel/game/reducer.js | 39 + tgui/packages/tgui-panel/game/selectors.js | 7 + tgui/packages/tgui-panel/index.js | 114 ++ tgui/packages/tgui-panel/package.json | 13 + tgui/packages/tgui-panel/panelFocus.js | 47 + .../packages/tgui-panel/ping/PingIndicator.js | 27 + tgui/packages/tgui-panel/ping/actions.js | 25 + tgui/packages/tgui-panel/ping/constants.js | 11 + tgui/packages/tgui-panel/ping/index.js | 9 + tgui/packages/tgui-panel/ping/middleware.js | 60 + tgui/packages/tgui-panel/ping/reducer.js | 46 + tgui/packages/tgui-panel/ping/selectors.js | 7 + tgui/packages/tgui-panel/reconnect.tsx | 37 + .../tgui-panel/settings/SettingsPanel.js | 311 +++++ tgui/packages/tgui-panel/settings/actions.js | 26 + .../packages/tgui-panel/settings/constants.js | 39 + tgui/packages/tgui-panel/settings/hooks.js | 20 + tgui/packages/tgui-panel/settings/index.js | 10 + .../tgui-panel/settings/middleware.js | 83 ++ tgui/packages/tgui-panel/settings/model.js | 20 + tgui/packages/tgui-panel/settings/reducer.js | 175 +++ .../packages/tgui-panel/settings/selectors.js | 12 + .../tgui-panel/styles/components/Chat.scss | 100 ++ .../styles/components/Notifications.scss | 26 + .../tgui-panel/styles/components/Ping.scss | 28 + tgui/packages/tgui-panel/styles/main.scss | 62 + .../tgui-panel/styles/tgchat/chat-dark.scss | 1111 ++++++++++++++++ .../tgui-panel/styles/tgchat/chat-light.scss | 1144 +++++++++++++++++ .../tgui-panel/styles/themes/light.scss | 89 ++ tgui/packages/tgui-panel/telemetry.js | 90 ++ tgui/packages/tgui-panel/themes.js | 138 ++ tgui/packages/tgui/backend.ts | 8 + tgui/packages/tgui/components/Section.tsx | 1 + .../tgui/styles/components/Section.scss | 22 +- tgui/packages/tgui/styles/layouts/Layout.scss | 3 +- tgui/packages/tgui_ch/backend.ts | 8 + tgui/packages/tgui_ch/components/Section.tsx | 1 + .../tgui_ch/styles/components/Section.scss | 22 +- .../tgui_ch/styles/layouts/Layout.scss | 3 +- tgui/public/tgui-panel.bundle.css | 2 + tgui/public/tgui-panel.bundle.js | 1 + tgui/public/tgui.bundle.css | 24 +- tgui/public/tgui.bundle.js | 2 +- tgui/webpack.config.js | 4 + tgui/yarn.lock | 22 +- tools/build/build.js | 4 +- vorestation.dme | 9 + 88 files changed, 6467 insertions(+), 78 deletions(-) create mode 100644 code/__defines/chat.dm create mode 100644 code/__defines/logging.dm create mode 100644 code/_global_vars/_regexes.dm create mode 100644 code/_helpers/logging/ui.dm create mode 100644 code/modules/asset_cache/assets/fontawesome.dm create mode 100644 code/modules/asset_cache/assets/jquery.dm create mode 100644 code/modules/asset_cache/assets/tgfont.dm create mode 100644 code/modules/asset_cache/assets/tgui.dm create mode 100644 interface/fonts.dm create mode 100644 tgui/packages/tgui-panel/Notifications.js create mode 100644 tgui/packages/tgui-panel/Panel.js create mode 100644 tgui/packages/tgui-panel/audio/NowPlayingWidget.js create mode 100644 tgui/packages/tgui-panel/audio/hooks.js create mode 100644 tgui/packages/tgui-panel/audio/index.js create mode 100644 tgui/packages/tgui-panel/audio/middleware.js create mode 100644 tgui/packages/tgui-panel/audio/player.js create mode 100644 tgui/packages/tgui-panel/audio/reducer.js create mode 100644 tgui/packages/tgui-panel/audio/selectors.js create mode 100644 tgui/packages/tgui-panel/chat/ChatPageSettings.js create mode 100644 tgui/packages/tgui-panel/chat/ChatPanel.js create mode 100644 tgui/packages/tgui-panel/chat/ChatTabs.js create mode 100644 tgui/packages/tgui-panel/chat/actions.js create mode 100644 tgui/packages/tgui-panel/chat/constants.js create mode 100644 tgui/packages/tgui-panel/chat/index.js create mode 100644 tgui/packages/tgui-panel/chat/middleware.js create mode 100644 tgui/packages/tgui-panel/chat/model.js create mode 100644 tgui/packages/tgui-panel/chat/reducer.js create mode 100644 tgui/packages/tgui-panel/chat/renderer.js create mode 100644 tgui/packages/tgui-panel/chat/replaceInTextNode.js create mode 100644 tgui/packages/tgui-panel/chat/selectors.js create mode 100644 tgui/packages/tgui-panel/game/actions.js create mode 100644 tgui/packages/tgui-panel/game/constants.js create mode 100644 tgui/packages/tgui-panel/game/hooks.js create mode 100644 tgui/packages/tgui-panel/game/index.js create mode 100644 tgui/packages/tgui-panel/game/middleware.js create mode 100644 tgui/packages/tgui-panel/game/reducer.js create mode 100644 tgui/packages/tgui-panel/game/selectors.js create mode 100644 tgui/packages/tgui-panel/index.js create mode 100644 tgui/packages/tgui-panel/package.json create mode 100644 tgui/packages/tgui-panel/panelFocus.js create mode 100644 tgui/packages/tgui-panel/ping/PingIndicator.js create mode 100644 tgui/packages/tgui-panel/ping/actions.js create mode 100644 tgui/packages/tgui-panel/ping/constants.js create mode 100644 tgui/packages/tgui-panel/ping/index.js create mode 100644 tgui/packages/tgui-panel/ping/middleware.js create mode 100644 tgui/packages/tgui-panel/ping/reducer.js create mode 100644 tgui/packages/tgui-panel/ping/selectors.js create mode 100644 tgui/packages/tgui-panel/reconnect.tsx create mode 100644 tgui/packages/tgui-panel/settings/SettingsPanel.js create mode 100644 tgui/packages/tgui-panel/settings/actions.js create mode 100644 tgui/packages/tgui-panel/settings/constants.js create mode 100644 tgui/packages/tgui-panel/settings/hooks.js create mode 100644 tgui/packages/tgui-panel/settings/index.js create mode 100644 tgui/packages/tgui-panel/settings/middleware.js create mode 100644 tgui/packages/tgui-panel/settings/model.js create mode 100644 tgui/packages/tgui-panel/settings/reducer.js create mode 100644 tgui/packages/tgui-panel/settings/selectors.js create mode 100644 tgui/packages/tgui-panel/styles/components/Chat.scss create mode 100644 tgui/packages/tgui-panel/styles/components/Notifications.scss create mode 100644 tgui/packages/tgui-panel/styles/components/Ping.scss create mode 100644 tgui/packages/tgui-panel/styles/main.scss create mode 100644 tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss create mode 100644 tgui/packages/tgui-panel/styles/tgchat/chat-light.scss create mode 100644 tgui/packages/tgui-panel/styles/themes/light.scss create mode 100644 tgui/packages/tgui-panel/telemetry.js create mode 100644 tgui/packages/tgui-panel/themes.js create mode 100644 tgui/public/tgui-panel.bundle.css create mode 100644 tgui/public/tgui-panel.bundle.js diff --git a/.gitignore b/.gitignore index 1014f4af5f..bb83978219 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ vchat.db* *.lk *.int *.backup + ### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Linux.gitignore *~ @@ -117,6 +118,14 @@ $RECYCLE.BIN/ #Sublime *.sublime-project *.sublime-workspace +*.before +*.pyc +*.pid +cfg/ + +#Ignore everything in datafolder and subdirectories +/data/**/* +/tmp/**/* #Visual studio stuff *.vscode/* diff --git a/code/__defines/chat.dm b/code/__defines/chat.dm new file mode 100644 index 0000000000..37624e1f4d --- /dev/null +++ b/code/__defines/chat.dm @@ -0,0 +1,26 @@ +/*! + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +#define MESSAGE_TYPE_SYSTEM "system" +#define MESSAGE_TYPE_LOCALCHAT "localchat" +#define MESSAGE_TYPE_PLOCALCHAT "plocalchat" +#define MESSAGE_TYPE_RADIO "radio" +#define MESSAGE_TYPE_NIF "nif" +#define MESSAGE_TYPE_INFO "info" +#define MESSAGE_TYPE_WARNING "warning" +#define MESSAGE_TYPE_DEADCHAT "deadchat" +#define MESSAGE_TYPE_OOC "ooc" +#define MESSAGE_TYPE_LOOC "looc" +#define MESSAGE_TYPE_ADMINPM "adminpm" +#define MESSAGE_TYPE_MENTORPM "mentorpm" +#define MESSAGE_TYPE_COMBAT "combat" +#define MESSAGE_TYPE_ADMINCHAT "adminchat" +#define MESSAGE_TYPE_PRAYER "prayer" +#define MESSAGE_TYPE_MODCHAT "modchat" +#define MESSAGE_TYPE_RLOOC "rlooc" +#define MESSAGE_TYPE_EVENTCHAT "eventchat" +#define MESSAGE_TYPE_ADMINLOG "adminlog" +#define MESSAGE_TYPE_ATTACKLOG "attacklog" +#define MESSAGE_TYPE_DEBUG "debug" diff --git a/code/__defines/logging.dm b/code/__defines/logging.dm new file mode 100644 index 0000000000..07a121d9bb --- /dev/null +++ b/code/__defines/logging.dm @@ -0,0 +1,2 @@ +#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) +#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index e1765d1ff6..5ef094101a 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -49,6 +49,26 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME) #define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list +//! ### SS initialization hints +/** + * Negative values incidate a failure or warning of some kind, positive are good. + * 0 and 1 are unused so that TRUE and FALSE are guarenteed to be invalid values. + */ + +/// Subsystem failed to initialize entirely. Print a warning, log, and disable firing. +#define SS_INIT_FAILURE -2 + +/// The default return value which must be overriden. Will succeed with a warning. +#define SS_INIT_NONE -1 + +/// Subsystem initialized sucessfully. +#define SS_INIT_SUCCESS 2 + +/// Successful, but don't print anything. Useful if subsystem was disabled. +#define SS_INIT_NO_NEED 3 + +//! ### SS initialization load orders + // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. @@ -105,6 +125,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_ORBIT 7 #define FIRE_PRIORITY_VOTE 8 #define FIRE_PRIORITY_INSTRUMENTS 9 +#define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_AI 10 #define FIRE_PRIORITY_GARBAGE 15 #define FIRE_PRIORITY_ALARM 20 diff --git a/code/__defines/tgui.dm b/code/__defines/tgui.dm index 1215382ce7..96664cf2b3 100644 --- a/code/__defines/tgui.dm +++ b/code/__defines/tgui.dm @@ -4,9 +4,9 @@ #define TGUI_WINDOW_HARD_LIMIT 9 /// Maximum ping timeout allowed to detect zombie windows -#define TGUI_PING_TIMEOUT 4 SECONDS +#define TGUI_PING_TIMEOUT (4 SECONDS) /// Used for rate-limiting to prevent DoS by excessively refreshing a TGUI window -#define TGUI_REFRESH_FULL_UPDATE_COOLDOWN 5 SECONDS +#define TGUI_REFRESH_FULL_UPDATE_COOLDOWN (1 SECONDS) /// Window does not exist #define TGUI_WINDOW_CLOSED 0 @@ -36,4 +36,4 @@ #define TGUI_MODAL_OPEN 1 #define TGUI_MODAL_DELEGATE 2 #define TGUI_MODAL_ANSWER 3 -#define TGUI_MODAL_CLOSE 4 \ No newline at end of file +#define TGUI_MODAL_CLOSE 4 diff --git a/code/_global_vars/_regexes.dm b/code/_global_vars/_regexes.dm new file mode 100644 index 0000000000..7e9ea99d1f --- /dev/null +++ b/code/_global_vars/_regexes.dm @@ -0,0 +1,2 @@ +//These are a bunch of regex datums for use /((any|every|no|some|head|foot)where(wolf)?\sand\s)+(\.[\.\s]+\s?where\?)?/i +GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://")) diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index ddc1857932..9eb7a28b7a 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -270,10 +270,6 @@ if(Debug2) WRITE_LOG(diary, "TOPIC: [text]") -/proc/log_href(text) - // Configs are checked by caller - WRITE_LOG(href_logfile, "HREF: [text]") - /proc/log_unit_test(text) to_world_log("## UNIT_TEST: [text]") @@ -283,22 +279,6 @@ #define log_reftracker(msg) #endif -/proc/log_tgui(user_or_client, text) - if(!text) - stack_trace("Pointless log_tgui message") - return - var/entry = "" - if(!user_or_client) - entry += "no user" -// else if(istype(user_or_client, /mob)) //CHOMP Edit commenting out these blocks because it just seems to do nothing except spam the logs with... nothing. -// var/mob/user = user_or_client -// entry += "[user.ckey] (as [user])" -// else if(istype(user_or_client, /client)) -// var/client/client = user_or_client -// entry += "[client.ckey]" - entry += ":\n[text]" - WRITE_LOG(diary, entry) - /proc/log_asset(text) WRITE_LOG(diary, "ASSET: [text]") diff --git a/code/_helpers/logging/ui.dm b/code/_helpers/logging/ui.dm new file mode 100644 index 0000000000..ed6a0b6588 --- /dev/null +++ b/code/_helpers/logging/ui.dm @@ -0,0 +1,36 @@ +/proc/log_href(text) + //WRITE_LOG(GLOB.world_href_log, "HREF: [text]") + WRITE_LOG(href_logfile, "HREF: [text]") + +/** + * Appends a tgui-related log entry. All arguments are optional. + */ +/proc/log_tgui(user, message, context, + datum/tgui_window/window, + datum/src_object) + var/entry = "" + // Insert user info + if(!user) + entry += "" + else if(istype(user, /mob)) + var/mob/mob = user + entry += "[mob.ckey] (as [mob] at [mob.x],[mob.y],[mob.z])" + else if(istype(user, /client)) + var/client/client = user + entry += "[client.ckey]" + // Insert context + if(context) + entry += " in [context]" + else if(window) + entry += " in [window.id]" + // Resolve src_object + if(!src_object && window?.locked_by) + src_object = window.locked_by.src_object + // Insert src_object info + if(src_object) + entry += "\nUsing: [src_object.type] [REF(src_object)]" + // Insert message + if(message) + entry += "\n[message]" + //WRITE_LOG(GLOB.tgui_log, entry) + WRITE_LOG(diary, entry) diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index b6f6e4ed44..bbae84b79c 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -1,13 +1,4 @@ //DEFINITIONS FOR ASSET DATUMS START HERE. - -/datum/asset/simple/tgui - // keep_local_name = TRUE - assets = list( - "tgui.bundle.js" = file("tgui/public/tgui.bundle.js"), - "tgui.bundle.css" = file("tgui/public/tgui.bundle.css"), - ) - - /datum/asset/simple/headers assets = list( "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', @@ -156,11 +147,6 @@ // /datum/asset/simple/fontawesome // ) -/datum/asset/simple/jquery - assets = list( - "jquery.min.js" = 'code/modules/tooltip/jquery.min.js', - ) - // /datum/asset/simple/goonchat // assets = list( // "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', @@ -169,24 +155,6 @@ // "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css', // ) -/datum/asset/simple/fontawesome - assets = list( - "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', - "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', - "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', - "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', - "font-awesome.css" = 'html/font-awesome/css/all.min.css', - "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' - ) - -/datum/asset/simple/tgfont - assets = list( - "tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"), - "tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"), - "tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"), - ) - - // /datum/asset/spritesheet/goonchat // name = "chat" diff --git a/code/modules/asset_cache/assets/fontawesome.dm b/code/modules/asset_cache/assets/fontawesome.dm new file mode 100644 index 0000000000..72af739f4e --- /dev/null +++ b/code/modules/asset_cache/assets/fontawesome.dm @@ -0,0 +1,9 @@ +/datum/asset/simple/fontawesome + assets = list( + "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', + "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', + "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', + "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', + "font-awesome.css" = 'html/font-awesome/css/all.min.css', + "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' + ) diff --git a/code/modules/asset_cache/assets/jquery.dm b/code/modules/asset_cache/assets/jquery.dm new file mode 100644 index 0000000000..2a28293fbb --- /dev/null +++ b/code/modules/asset_cache/assets/jquery.dm @@ -0,0 +1,4 @@ +/datum/asset/simple/jquery + assets = list( + "jquery.min.js" = 'code/modules/tooltip/jquery.min.js', + ) diff --git a/code/modules/asset_cache/assets/tgfont.dm b/code/modules/asset_cache/assets/tgfont.dm new file mode 100644 index 0000000000..48db3e4e67 --- /dev/null +++ b/code/modules/asset_cache/assets/tgfont.dm @@ -0,0 +1,6 @@ +/datum/asset/simple/tgfont + assets = list( + "tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"), + "tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"), + "tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"), + ) diff --git a/code/modules/asset_cache/assets/tgui.dm b/code/modules/asset_cache/assets/tgui.dm new file mode 100644 index 0000000000..6874c24eea --- /dev/null +++ b/code/modules/asset_cache/assets/tgui.dm @@ -0,0 +1,15 @@ +/datum/asset/simple/tgui + // keep_local_name = TRUE + assets = list( + "tgui.bundle.js" = file("tgui/public/tgui.bundle.js"), + "tgui.bundle.css" = file("tgui/public/tgui.bundle.css"), + ) + +/* Comment will be removed in later part +/datum/asset/simple/tgui_panel + // keep_local_name = TRUE + assets = list( + "tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"), + "tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"), + ) +*/ diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 7ffe616393..13b80f1529 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -1,11 +1,42 @@ +/** + * Client datum + * + * A datum that is created whenever a user joins a BYOND world, one will exist for every active connected + * player + * + * when they first connect, this client object is created and [/client/New] is called + * + * When they disconnect, this client object is deleted and [/client/Del] is called + * + * All client topic calls go through [/client/Topic] first, so a lot of our specialised + * topic handling starts here + */ /client - ////////////////////// - //BLACK MAGIC THINGS// - ////////////////////// + /** + * This line makes clients parent type be a datum + * + * By default in byond if you define a proc on datums, that proc will exist on nearly every single type + * from icons to images to atoms to mobs to objs to turfs to areas, it won't however, appear on client + * + * instead by default they act like their own independent type so while you can do isdatum(icon) + * and have it return true, you can't do isdatum(client), it will always return false. + * + * This makes writing oo code hard, when you have to consider this extra special case + * + * This line prevents that, and has never appeared to cause any ill effects, while saving us an extra + * pain to think about + * + * This line is widely considered black fucking magic, and the fact it works is a puzzle to everyone + * involved, including the current engine developer, lummox + * + * If you are a future developer and the engine source is now available and you can explain why this + * is the way it is, please do update this comment + */ parent_type = /datum //////////////// //ADMIN THINGS// //////////////// + ///Contains admin info. Null if client is not an admin. var/datum/admins/holder = null var/datum/admins/deadmin_holder = null var/buildmode = 0 @@ -18,6 +49,7 @@ ///////// //OTHER// ///////// + ///Player preferences datum for the client var/datum/preferences/prefs = null var/moving = null var/adminobs = null diff --git a/interface/fonts.dm b/interface/fonts.dm new file mode 100644 index 0000000000..31db4fa961 --- /dev/null +++ b/interface/fonts.dm @@ -0,0 +1,6 @@ +/// A font datum, it exists to define a custom font to use in a span style later. +/datum/font + /// Font name, just so people know what to put in their span style. + var/name + /// The font file we link to. + var/font_family diff --git a/tgui/packages/tgui-panel/Notifications.js b/tgui/packages/tgui-panel/Notifications.js new file mode 100644 index 0000000000..280694a03e --- /dev/null +++ b/tgui/packages/tgui-panel/Notifications.js @@ -0,0 +1,28 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { Flex } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch + +export const Notifications = (props) => { + const { children } = props; + return
{children}
; +}; + +const NotificationsItem = (props) => { + const { rightSlot, children } = props; + return ( + + + {children} + + {rightSlot && ( + {rightSlot} + )} + + ); +}; + +Notifications.Item = NotificationsItem; diff --git a/tgui/packages/tgui-panel/Panel.js b/tgui/packages/tgui-panel/Panel.js new file mode 100644 index 0000000000..e44901c87e --- /dev/null +++ b/tgui/packages/tgui-panel/Panel.js @@ -0,0 +1,128 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { Button, Section, Stack } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch +import { Pane } from 'tgui_ch/layouts'; // CHOMPEdit - tgui_ch +import { NowPlayingWidget, useAudio } from './audio'; +import { ChatPanel, ChatTabs } from './chat'; +import { useGame } from './game'; +import { Notifications } from './Notifications'; +import { PingIndicator } from './ping'; +import { ReconnectButton } from './reconnect'; +import { SettingsPanel, useSettings } from './settings'; + +export const Panel = (props, context) => { + // IE8-10: Needs special treatment due to missing Flex support + if (Byond.IS_LTE_IE10) { + return ; + } + const audio = useAudio(context); + const settings = useSettings(context); + const game = useGame(context); + if (process.env.NODE_ENV !== 'production') { + const { useDebug, KitchenSink } = require('tgui_ch/debug'); // CHOMPEdit - tgui_ch + const debug = useDebug(context); + if (debug.kitchenSink) { + return ; + } + } + return ( + + + +
+ + + + + + + + +
+
+ {audio.visible && ( + +
+ +
+
+ )} + {settings.visible && ( + + + + )} + +
+ + + + + {game.connectionLostAt && ( + }> + You are either AFK, experiencing lag or the connection has + closed. + + )} + {game.roundRestartedAt && ( + + The connection has been closed because the server is + restarting. Please wait while you automatically reconnect. + + )} + +
+
+
+
+ ); +}; + +const HoboPanel = (props, context) => { + const settings = useSettings(context); + return ( + + + + {(settings.visible && ) || ( + + )} + + + ); +}; diff --git a/tgui/packages/tgui-panel/audio/NowPlayingWidget.js b/tgui/packages/tgui-panel/audio/NowPlayingWidget.js new file mode 100644 index 0000000000..4421ae4ddf --- /dev/null +++ b/tgui/packages/tgui-panel/audio/NowPlayingWidget.js @@ -0,0 +1,109 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { toFixed } from 'common/math'; +import { useDispatch, useSelector } from 'common/redux'; +import { Button, Collapsible, Flex, Knob, Section } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch +import { useSettings } from '../settings'; +import { selectAudio } from './selectors'; + +export const NowPlayingWidget = (props, context) => { + const audio = useSelector(context, selectAudio), + dispatch = useDispatch(context), + settings = useSettings(context), + title = audio.meta?.title, + URL = audio.meta?.link, + Artist = audio.meta?.artist || 'Unknown Artist', + upload_date = audio.meta?.upload_date || 'Unknown Date', + album = audio.meta?.album || 'Unknown Album', + duration = audio.meta?.duration, + date = !isNaN(upload_date) + ? upload_date?.substring(0, 4) + + '-' + + upload_date?.substring(4, 6) + + '-' + + upload_date?.substring(6, 8) + : upload_date; + + return ( + + {(audio.playing && ( + + { + +
+ {URL !== 'Song Link Hidden' && ( + + URL: {URL} + + )} + + Duration: {duration} + + {Artist !== 'Song Artist Hidden' && + Artist !== 'Unknown Artist' && ( + + Artist: {Artist} + + )} + {album !== 'Song Album Hidden' && album !== 'Unknown Album' && ( + + Album: {album} + + )} + {upload_date !== 'Song Upload Date Hidden' && + upload_date !== 'Unknown Date' && ( + + Uploaded: {date} + + )} +
+
+ } +
+ )) || ( + + Nothing to play. + + )} + {audio.playing && ( + + + + + +
+ {MESSAGE_TYPES.filter( + (typeDef) => !typeDef.important && !typeDef.admin + ).map((typeDef) => ( + + dispatch( + toggleAcceptedType({ + pageId: page.id, + type: typeDef.type, + }) + ) + }> + {typeDef.name} + + ))} + + {MESSAGE_TYPES.filter( + (typeDef) => !typeDef.important && typeDef.admin + ).map((typeDef) => ( + + dispatch( + toggleAcceptedType({ + pageId: page.id, + type: typeDef.type, + }) + ) + }> + {typeDef.name} + + ))} + +
+ + ); +}; diff --git a/tgui/packages/tgui-panel/chat/ChatPanel.js b/tgui/packages/tgui-panel/chat/ChatPanel.js new file mode 100644 index 0000000000..2a721be04b --- /dev/null +++ b/tgui/packages/tgui-panel/chat/ChatPanel.js @@ -0,0 +1,73 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { shallowDiffers } from 'common/react'; +import { Component, createRef } from 'inferno'; +import { Button } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch +import { chatRenderer } from './renderer'; + +export class ChatPanel extends Component { + constructor() { + super(); + this.ref = createRef(); + this.state = { + scrollTracking: true, + }; + this.handleScrollTrackingChange = (value) => + this.setState({ + scrollTracking: value, + }); + } + + componentDidMount() { + chatRenderer.mount(this.ref.current); + chatRenderer.events.on( + 'scrollTrackingChanged', + this.handleScrollTrackingChange + ); + this.componentDidUpdate(); + } + + componentWillUnmount() { + chatRenderer.events.off( + 'scrollTrackingChanged', + this.handleScrollTrackingChange + ); + } + + componentDidUpdate(prevProps) { + requestAnimationFrame(() => { + chatRenderer.ensureScrollTracking(); + }); + const shouldUpdateStyle = + !prevProps || shallowDiffers(this.props, prevProps); + if (shouldUpdateStyle) { + chatRenderer.assignStyle({ + 'width': '100%', + 'white-space': 'pre-wrap', + 'font-size': this.props.fontSize, + 'line-height': this.props.lineHeight, + }); + } + } + + render() { + const { scrollTracking } = this.state; + return ( + <> +
+ {!scrollTracking && ( + + )} + + ); + } +} diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.js b/tgui/packages/tgui-panel/chat/ChatTabs.js new file mode 100644 index 0000000000..15109b1192 --- /dev/null +++ b/tgui/packages/tgui-panel/chat/ChatTabs.js @@ -0,0 +1,68 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { useDispatch, useSelector } from 'common/redux'; +import { Box, Tabs, Flex, Button } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch +import { changeChatPage, addChatPage } from './actions'; +import { selectChatPages, selectCurrentChatPage } from './selectors'; +import { openChatSettings } from '../settings/actions'; + +const UnreadCountWidget = ({ value }) => ( + + {Math.min(value, 99)} + +); + +export const ChatTabs = (props, context) => { + const pages = useSelector(context, selectChatPages); + const currentPage = useSelector(context, selectCurrentChatPage); + const dispatch = useDispatch(context); + return ( + + + + {pages.map((page) => ( + 0 && ( + + ) + } + onClick={() => + dispatch( + changeChatPage({ + pageId: page.id, + }) + ) + }> + {page.name} + + ))} + + + + + + + ); +}; diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.js b/tgui/packages/tgui-panel/settings/SettingsPanel.js new file mode 100644 index 0000000000..7c3f19baa9 --- /dev/null +++ b/tgui/packages/tgui-panel/settings/SettingsPanel.js @@ -0,0 +1,311 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { toFixed } from 'common/math'; +import { useLocalState } from 'tgui_ch/backend'; // CHOMPEdit - tgui_ch +import { useDispatch, useSelector } from 'common/redux'; +import { Box, Button, ColorBox, Divider, Dropdown, Flex, Input, LabeledList, NumberInput, Section, Stack, Tabs, TextArea } from 'tgui_ch/components'; // CHOMPEdit - tgui_ch +import { ChatPageSettings } from '../chat'; +import { rebuildChat, saveChatToDisk } from '../chat/actions'; +import { THEMES } from '../themes'; +import { changeSettingsTab, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from './actions'; +import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants'; +import { selectActiveTab, selectSettings, selectHighlightSettings, selectHighlightSettingById } from './selectors'; + +export const SettingsPanel = (props, context) => { + const activeTab = useSelector(context, selectActiveTab); + const dispatch = useDispatch(context); + return ( + + +
+ + {SETTINGS_TABS.map((tab) => ( + + dispatch( + changeSettingsTab({ + tabId: tab.id, + }) + ) + }> + {tab.name} + + ))} + +
+
+ + {activeTab === 'general' && } + {activeTab === 'chatPage' && } + {activeTab === 'textHighlight' && } + +
+ ); +}; + +export const SettingsGeneral = (props, context) => { + const { theme, fontFamily, fontSize, lineHeight } = useSelector( + context, + selectSettings + ); + const dispatch = useDispatch(context); + const [freeFont, setFreeFont] = useLocalState(context, 'freeFont', false); + return ( +
+ + + + dispatch( + updateSettings({ + theme: value, + }) + ) + } + /> + + + + + {(!freeFont && ( + + dispatch( + updateSettings({ + fontFamily: value, + }) + ) + } + /> + )) || ( + + dispatch( + updateSettings({ + fontFamily: value, + }) + ) + } + /> + )} + + + +
+ ); +}; + +const TextHighlightSettings = (props, context) => { + const highlightSettings = useSelector(context, selectHighlightSettings); + const dispatch = useDispatch(context); + return ( +
+
+ + {highlightSettings.map((id, i) => ( + + ))} + {highlightSettings.length < MAX_HIGHLIGHT_SETTINGS && ( + +
+ + + + + Can freeze the chat for a while. + + +
+ ); +}; + +const TextHighlightSetting = (props, context) => { + const { id, ...rest } = props; + const highlightSettingById = useSelector(context, selectHighlightSettingById); + const dispatch = useDispatch(context); + const { + highlightColor, + highlightText, + highlightWholeMessage, + matchWord, + matchCase, + } = highlightSettingById[id]; + return ( + + + +