From 4ffef6be6d983525ae9ccf6752f69050313f48d8 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Sat, 6 May 2023 08:56:14 +0000 Subject: [PATCH] Fixes issues with closing apps on tablets (#75117) ## About The Pull Request - Fixes background apps not reloading the UI - Standardizes opening/closing/backgrounding apps - Simplifies the way apps are closed instead of having 2 procs, one on the PC and one on the program, being named the same thing (wtf) - Removes program states. They existed so computers can know to update their UI every process tick, but since we now do this event based, this is no longer needed, which is good. - Replaces the 'forced' arg from kill_program as it was completely unused, with ``reload_ui``, which is now used to open the UI on close, and not to when we don't want it to. - Closing background apps no longer reloads the entire UI - Responding to an NT Message will no longer open the UI on your face. ## Why It's Good For The Game Closes https://github.com/tgstation/tgstation/issues/75046 Closes https://github.com/tgstation/tgstation/issues/75108 Makes tablet UIs more responsive and lag less, not checking if a program is closed every process to close it, and makes responding to messages not a hassle every time. Also makes the code easier to understand/read, ## Changelog :cl: fix: Tablets' minimize apps feature works again. /:cl: --- code/__DEFINES/modular_computer.dm | 4 -- .../computers/item/computer.dm | 50 +++++++------------ .../computers/item/computer_files.dm | 8 +-- .../computers/item/computer_power.dm | 13 ++--- .../computers/item/computer_ui.dm | 17 ++----- .../computers/machinery/console_presets.dm | 1 - .../file_system/computer_file.dm | 11 ++-- .../modular_computers/file_system/program.dm | 34 ++++++++++--- .../file_system/programs/airestorer.dm | 2 +- .../file_system/programs/alarm.dm | 2 +- .../file_system/programs/antagonist/dos.dm | 5 +- .../file_system/programs/borg_monitor.dm | 2 +- .../file_system/programs/card.dm | 2 +- .../file_system/programs/ntdownloader.dm | 2 +- .../file_system/programs/ntmessenger.dm | 3 +- .../file_system/programs/ntnrc_client.dm | 7 +-- .../file_system/programs/radar.dm | 4 +- .../file_system/programs/signalcommander.dm | 2 +- .../file_system/programs/sm_monitor.dm | 2 +- tgui/packages/tgui/interfaces/NtosNetChat.js | 15 +++--- 20 files changed, 88 insertions(+), 98 deletions(-) diff --git a/code/__DEFINES/modular_computer.dm b/code/__DEFINES/modular_computer.dm index db1460c2b0f..001722eb2dd 100644 --- a/code/__DEFINES/modular_computer.dm +++ b/code/__DEFINES/modular_computer.dm @@ -9,10 +9,6 @@ #define PROGRAM_CONSOLE (1<<0) #define PROGRAM_LAPTOP (1<<1) #define PROGRAM_TABLET (1<<2) -//Program states -#define PROGRAM_STATE_KILLED 0 -#define PROGRAM_STATE_BACKGROUND 1 -#define PROGRAM_STATE_ACTIVE 2 //Program categories #define PROGRAM_CATEGORY_CREW "Crew" #define PROGRAM_CATEGORY_ENGI "Engineering" diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 0cb3f7ca19d..c55a9fccccf 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -148,9 +148,7 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar /obj/item/modular_computer/Destroy() STOP_PROCESSING(SSobj, src) - wipe_program(forced = TRUE) - for(var/datum/computer_file/program/idle as anything in idle_threads) - idle.kill_program(TRUE) + close_all_programs() //Some components will actually try and interact with this, so let's do it later QDEL_NULL(soundloop) QDEL_LIST(stored_files) @@ -476,20 +474,14 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar active_program.event_networkfailure(FALSE) // Active program requires NTNet to run but we've just lost connection. Crash. for(var/datum/computer_file/program/idle_programs as anything in idle_threads) - if(idle_programs.program_state == PROGRAM_STATE_KILLED) - idle_threads.Remove(idle_programs) - continue idle_programs.process_tick(seconds_per_tick) idle_programs.ntnet_status = get_ntnet_status() if(idle_programs.requires_ntnet && !idle_programs.ntnet_status) idle_programs.event_networkfailure(TRUE) if(active_program) - if(active_program.program_state == PROGRAM_STATE_KILLED) - active_program = null - else - active_program.process_tick(seconds_per_tick) - active_program.ntnet_status = get_ntnet_status() + active_program.process_tick(seconds_per_tick) + active_program.ntnet_status = get_ntnet_status() handle_power(seconds_per_tick) // Handles all computer power interaction @@ -571,20 +563,7 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar data["PC_showexitprogram"] = !!active_program // Hides "Exit Program" button on mainscreen return data -///Wipes the computer's current program. Doesn't handle any of the niceties around doing this -/obj/item/modular_computer/proc/wipe_program(forced) - if(!active_program) - return - active_program.kill_program(forced) - active_program = null - -// Relays kill program request to currently active program. Use this to quit current program. -/obj/item/modular_computer/proc/kill_program(forced = FALSE) - wipe_program(forced) - update_appearance() - update_tablet_open_uis(usr) - -/obj/item/modular_computer/proc/open_program(mob/user, datum/computer_file/program/program) +/obj/item/modular_computer/proc/open_program(mob/user, datum/computer_file/program/program, open_ui = TRUE) if(program.computer != src) CRASH("tried to open program that does not belong to this computer") @@ -594,11 +573,12 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar // The program is already running. Resume it. if(program in idle_threads) - program.program_state = PROGRAM_STATE_ACTIVE active_program = program program.alert_pending = FALSE idle_threads.Remove(program) - update_appearance() + if(open_ui) + update_tablet_open_uis(user) + update_appearance(UPDATE_ICON) return TRUE if(!program.is_supported_by_hardware(hardware_flag, 1, user)) @@ -617,8 +597,9 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar active_program = program program.alert_pending = FALSE - update_appearance() - update_tablet_open_uis(user) + if(open_ui) + update_tablet_open_uis(user) + update_appearance(UPDATE_ICON) return TRUE // Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) @@ -650,10 +631,13 @@ GLOBAL_LIST_EMPTY(TabletMessengers) // a list of all active messengers, similar return SSmodular_computers.add_log("[src]: [text]") -/obj/item/modular_computer/proc/shutdown_computer(loud = 1) - kill_program(forced = TRUE) - for(var/datum/computer_file/program/idle_program in idle_threads) - idle_program.kill_program(forced = TRUE) +/obj/item/modular_computer/proc/close_all_programs() + active_program = null + for(var/datum/computer_file/program/idle as anything in idle_threads) + idle_threads.Remove(idle) + +/obj/item/modular_computer/proc/shutdown_computer(loud = TRUE) + close_all_programs() if(looping_sound) soundloop.stop() if(physical && loud) diff --git a/code/modules/modular_computers/computers/item/computer_files.dm b/code/modules/modular_computers/computers/item/computer_files.dm index 8299247c5ea..8d9f9006b07 100644 --- a/code/modules/modular_computers/computers/item/computer_files.dm +++ b/code/modules/modular_computers/computers/item/computer_files.dm @@ -35,10 +35,10 @@ return FALSE if(istype(file_removing, /datum/computer_file/program)) var/datum/computer_file/program/program_file = file_removing - if(program_file.program_state != PROGRAM_STATE_KILLED) - program_file.kill_program(TRUE) - if(program_file.program_state == PROGRAM_STATE_ACTIVE) - active_program = null + if(program_file == active_program) + active_program.kill_program() + for(var/datum/computer_file/program/programs as anything in idle_threads) + programs.kill_program() SEND_SIGNAL(file_removing, COMSIG_MODULAR_COMPUTER_FILE_DELETING) stored_files.Remove(file_removing) diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm index bea16f18546..6b6230ed0d4 100644 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -23,12 +23,13 @@ // Used in following function to reduce copypaste /obj/item/modular_computer/proc/power_failure() - if(enabled) // Shut down the computer - if(active_program) - active_program.event_powerfailure(background = FALSE) - for(var/datum/computer_file/program/programs as anything in idle_threads) - programs.event_powerfailure(background = TRUE) - shutdown_computer(0) + if(!enabled) // Shut down the computer + return + if(active_program) + active_program.event_powerfailure() + for(var/datum/computer_file/program/programs as anything in idle_threads) + programs.event_powerfailure() + shutdown_computer(loud = FALSE) // Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged /obj/item/modular_computer/proc/handle_power(seconds_per_tick) diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 9b8dc2d0ea4..2a76a20ef1b 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -126,7 +126,7 @@ switch(action) if("PC_exit") - kill_program() + active_program.kill_program() return TRUE if("PC_shutdown") shutdown_computer() @@ -134,26 +134,17 @@ if("PC_minimize") if(!active_program) return - //header programs can't be minimized. - if(active_program.header_program) - kill_program() - return TRUE - - idle_threads.Add(active_program) - active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs - - active_program = null - update_appearance() + active_program.background_program() return TRUE if("PC_killprogram") var/prog = params["name"] var/datum/computer_file/program/killed_program = find_file_by_name(prog) - if(!istype(killed_program) || killed_program.program_state == PROGRAM_STATE_KILLED) + if(!istype(killed_program)) return - killed_program.kill_program(forced = TRUE) + killed_program.kill_program() to_chat(usr, span_notice("Program [killed_program.filename].[killed_program.filetype] with PID [rand(100,999)] has been killed.")) return TRUE diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index ba7128a29ff..0236e42ff9d 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -96,7 +96,6 @@ . = ..() var/datum/computer_file/program/chatclient/chatprogram = cpu.find_file_by_name("ntnrc_client") chatprogram.username = "[lowertext(console_department)]_department" - chatprogram.program_state = PROGRAM_STATE_ACTIVE cpu.active_program = chatprogram /obj/machinery/modular_computer/console/preset/cargochat/service diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index 09325a09707..2c08fa8480e 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -23,7 +23,10 @@ /datum/computer_file/Destroy(force) if(computer) - computer.remove_file(src) + if(src == computer.active_program) + computer.active_program = null + if(src in computer.idle_threads) + computer.idle_threads.Remove(src) computer = null if(disk_host) disk_host.remove_file(src) @@ -87,8 +90,8 @@ * Arguments: * * background - Whether the app is running in the background. */ -/datum/computer_file/program/proc/event_powerfailure(background) - kill_program(forced = TRUE) +/datum/computer_file/program/proc/event_powerfailure() + kill_program() /** * Called when a computer program is crashing due to any required connection being shut off. @@ -96,7 +99,7 @@ * * background - Whether the app is running in the background. */ /datum/computer_file/program/proc/event_networkfailure(background) - kill_program(forced = TRUE) + kill_program() if(background) computer.visible_message(span_danger("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error")) else diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 4c73862c0c5..2bf9a04a176 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -7,8 +7,6 @@ var/list/required_access = list() /// List of required access to download or file host the program. Any match will do. var/list/transfer_access = list() - /// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. - var/program_state = PROGRAM_STATE_KILLED /// User-friendly name of this program. var/filedesc = "Unknown Program" /// Short description of this program's function. @@ -158,23 +156,43 @@ if(requires_ntnet) var/obj/item/card/id/ID = computer.computer_id_slot?.GetID() generate_network_log("Connection opened -- Program ID:[filename] User:[ID?"[ID.registered_name]":"None"]") - program_state = PROGRAM_STATE_ACTIVE return TRUE return FALSE /** * Kills the running program * - * Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. - * Arguments: - * * forced - Boolean to determine if this was a forced close. Should be TRUE if the user did not willingly close the program. + * Use this proc to kill the program. + * Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. + * Args: + * - reload_ui - Whether we reload the UI on computer's shutdown. **/ -/datum/computer_file/program/proc/kill_program(forced = FALSE) +/datum/computer_file/program/proc/kill_program() SHOULD_CALL_PARENT(TRUE) - program_state = PROGRAM_STATE_KILLED + + if(src == computer.active_program) + computer.active_program = null + if(computer.enabled) + computer.update_tablet_open_uis(usr) if(src in computer.idle_threads) computer.idle_threads.Remove(src) + if(requires_ntnet) var/obj/item/card/id/ID = computer.computer_id_slot?.GetID() generate_network_log("Connection closed -- Program ID: [filename] User:[ID ? "[ID.registered_name]" : "None"]") + + computer.update_appearance(UPDATE_ICON) + return TRUE + +///Sends the running program to the background/idle threads. Header programs can't be minimized and will kill instead. +/datum/computer_file/program/proc/background_program() + SHOULD_CALL_PARENT(TRUE) + if(header_program) + return kill_program() + + computer.idle_threads.Add(src) + computer.active_program = null + + computer.update_tablet_open_uis(usr) + computer.update_appearance(UPDATE_ICON) return TRUE diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 5ba7670267a..21d66d4b0fc 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -30,7 +30,7 @@ examine_text += span_info("Alt-click to eject the intelliCard.") return examine_text -/datum/computer_file/program/ai_restorer/kill_program(forced) +/datum/computer_file/program/ai_restorer/kill_program() try_eject(forced = TRUE) return ..() diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index f3cb91bc292..e1205f92c6a 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -51,6 +51,6 @@ . = ..(user) GLOB.alarmdisplay += src -/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE) +/datum/computer_file/program/alarm_monitor/kill_program() GLOB.alarmdisplay -= src return ..() diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 0cf205b98c1..4e165ed70d4 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -32,13 +32,12 @@ target = null error = "Connection to destination relay lost." -/datum/computer_file/program/ntnet_dos/kill_program(forced = FALSE) +/datum/computer_file/program/ntnet_dos/kill_program() if(target) target.dos_sources.Remove(src) target = null executed = FALSE - - ..() + return ..() /datum/computer_file/program/ntnet_dos/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) switch(action) diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index 763db0cdb8b..0cda5b5e7c2 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -19,7 +19,7 @@ DL_source = null return ..() -/datum/computer_file/program/borg_monitor/kill_program(forced = FALSE) +/datum/computer_file/program/borg_monitor/kill_program() loglist = null //Not everything is saved if you close an app DL_source = null DL_progress = 0 diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 33373e0fc66..c25ea65628d 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -79,7 +79,7 @@ return FALSE computer.crew_manifest_update = TRUE -/datum/computer_file/program/card_mod/kill_program(forced) +/datum/computer_file/program/card_mod/kill_program() computer.crew_manifest_update = FALSE var/obj/item/card/id/inserted_auth_card = computer.computer_id_slot if(inserted_auth_card) diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index e3b0cf72000..53a68f2c6d5 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -162,6 +162,6 @@ return TRUE return FALSE -/datum/computer_file/program/ntnetdownload/kill_program(forced) +/datum/computer_file/program/ntnetdownload/kill_program() abort_file_download() return ..() diff --git a/code/modules/modular_computers/file_system/programs/ntmessenger.dm b/code/modules/modular_computers/file_system/programs/ntmessenger.dm index 4a141ca2a79..bb6bcaad592 100644 --- a/code/modules/modular_computers/file_system/programs/ntmessenger.dm +++ b/code/modules/modular_computers/file_system/programs/ntmessenger.dm @@ -3,7 +3,6 @@ filedesc = "Direct Messenger" category = PROGRAM_CATEGORY_MISC program_icon_state = "command" - program_state = PROGRAM_STATE_BACKGROUND extended_desc = "This program allows old-school communication with other modular devices." size = 0 undeletable = TRUE // It comes by default in tablets, can't be downloaded, takes no space and should obviously not be able to be deleted. @@ -414,7 +413,7 @@ if(!computer.turn_on(usr, open_ui = FALSE)) return if(computer.active_program != src) - if(!computer.open_program(usr, src)) + if(!computer.open_program(usr, src, open_ui = FALSE)) return if(!href_list["close"] && usr.can_perform_action(computer, FORBID_TELEKINESIS_REACH)) switch(href_list["choice"]) diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index 903293c62ae..52f87f3214f 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -176,7 +176,7 @@ /datum/computer_file/program/chatclient/process_tick(seconds_per_tick) . = ..() var/datum/ntnet_conversation/channel = SSmodular_computers.get_chat_channel_by_id(active_channel) - if(program_state != PROGRAM_STATE_KILLED) + if(src in computer.idle_threads) ui_header = "ntnrc_idle.gif" if(channel) // Remember the last message. If there is no message in the channel remember null. @@ -198,7 +198,7 @@ channel.offline_clients.Remove(src) channel.active_clients.Add(src) -/datum/computer_file/program/chatclient/kill_program(forced = FALSE) +/datum/computer_file/program/chatclient/kill_program() for(var/datum/ntnet_conversation/channel as anything in SSmodular_computers.chat_channels) channel.go_offline(src) active_channel = null @@ -236,7 +236,8 @@ authed = TRUE clients.Add(list(list( "name" = channel_client.username, - "status" = channel_client.program_state, + "online" = (channel_client == channel_client.computer.active_program), + "away" = (channel_client in channel_client.computer.idle_threads), "muted" = (channel_client in channel.muted_clients), "operator" = (channel.channel_operator == channel_client), "ref" = REF(channel_client), diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index d711d4c59e6..85dfebcd7e7 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -29,7 +29,7 @@ return return FALSE -/datum/computer_file/program/radar/kill_program(forced = FALSE) +/datum/computer_file/program/radar/kill_program() objects = list() selected = null STOP_PROCESSING(SSfastprocess, src) @@ -313,7 +313,7 @@ RegisterSignal(SSdcs, COMSIG_GLOB_NUKE_DEVICE_ARMED, PROC_REF(on_nuke_armed)) -/datum/computer_file/program/radar/fission360/kill_program(forced) +/datum/computer_file/program/radar/fission360/kill_program() UnregisterSignal(SSdcs, COMSIG_GLOB_NUKE_DEVICE_ARMED) return ..() diff --git a/code/modules/modular_computers/file_system/programs/signalcommander.dm b/code/modules/modular_computers/file_system/programs/signalcommander.dm index b82518dc6b2..b6b17722430 100644 --- a/code/modules/modular_computers/file_system/programs/signalcommander.dm +++ b/code/modules/modular_computers/file_system/programs/signalcommander.dm @@ -19,7 +19,7 @@ . = ..() set_frequency(signal_frequency) -/datum/computer_file/program/signal_commander/kill_program(forced) +/datum/computer_file/program/signal_commander/kill_program() . = ..() SSradio.remove_object(computer, signal_frequency) diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index d9d830c3b63..517f402922c 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -22,7 +22,7 @@ refresh() /// Apparently destroy calls this [/datum/computer_file/Destroy]. Here just to clean our references. -/datum/computer_file/program/supermatter_monitor/kill_program(forced = FALSE) +/datum/computer_file/program/supermatter_monitor/kill_program() for(var/supermatter in supermatters) clear_supermatter(supermatter) return ..() diff --git a/tgui/packages/tgui/interfaces/NtosNetChat.js b/tgui/packages/tgui/interfaces/NtosNetChat.js index 8b401f9c149..8668168b2a3 100644 --- a/tgui/packages/tgui/interfaces/NtosNetChat.js +++ b/tgui/packages/tgui/interfaces/NtosNetChat.js @@ -68,14 +68,13 @@ export const NtosNetChat = (props, context) => { if (client.operator) { return 'green'; } - switch (client.status) { - case CLIENT_ONLINE: - return 'white'; - case CLIENT_AWAY: - return 'yellow'; - case CLIENT_OFFLINE: - default: - return 'label'; + if (client.online) { + return 'white'; + } + if (client.away) { + return 'yellow'; + } else { + return 'label'; } }; // client from this computer!