New chat client / messenger (#10654)

This commit is contained in:
Karolis
2021-01-11 11:04:16 +02:00
committed by GitHub
parent f99e4a29c0
commit 339c531ad7
31 changed files with 858 additions and 846 deletions

View File

@@ -2254,6 +2254,9 @@
#include "code\modules\modular_computers\NTNet\NTNet.dm"
#include "code\modules\modular_computers\NTNet\NTNet_relay.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\message.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\ntnrc.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\user.dm"
#include "code\modules\multiz\_stubs.dm"
#include "code\modules\multiz\basic.dm"
#include "code\modules\multiz\hoist.dm"

View File

@@ -201,6 +201,7 @@
#define PROGRAM_ALL_REGULAR (PROGRAM_CONSOLE | PROGRAM_LAPTOP | PROGRAM_TABLET | PROGRAM_WRISTBOUND | PROGRAM_TELESCREEN)
#define PROGRAM_ALL_HANDHELD (PROGRAM_TABLET | PROGRAM_WRISTBOUND)
#define PROGRAM_STATE_DISABLED -1
#define PROGRAM_STATE_KILLED 0
#define PROGRAM_STATE_BACKGROUND 1
#define PROGRAM_STATE_ACTIVE 2

View File

@@ -126,7 +126,7 @@ var/const/NO_EMAG_ACT = -50
var/assignment = null //can be alt title or the actual job
var/rank = null //actual job
var/dorm = 0 // determines if this ID has claimed a dorm already
var/chat_registered = FALSE // registration for NTNET chat
var/datum/ntnet_user/chat_user
/obj/item/card/id/Destroy()
mob = null
@@ -151,6 +151,8 @@ var/const/NO_EMAG_ACT = -50
/obj/item/card/id/proc/update_name()
name = "ID Card ([src.registered_name] ([src.assignment]))"
if(istype(chat_user))
chat_user.username = chat_user.generateUsernameIdCard(src)
/obj/item/card/id/proc/set_id_photo(var/mob/M)
front = getFlatIcon(M, SOUTH)
@@ -334,6 +336,11 @@ var/const/NO_EMAG_ACT = -50
wear_over_suit = !wear_over_suit
mob_icon_update()
/obj/item/card/id/proc/InitializeChatUser()
if(!istype(chat_user))
chat_user = new()
chat_user.username = chat_user.generateUsernameIdCard(src)
/obj/item/card/id/silver
icon_state = "silver"
item_state = "silver_id"

View File

@@ -131,7 +131,7 @@
// get_debug_type displays this
else if(istext(value))
debug_type = null // it's kinda annoying here; we can tell the type by the quotes
vtext = "\"[value]\""
vtext = "\"[html_encode(value)]\""
else if(isicon(value))
vtext = "[value]"
else if(isfile(value))

View File

@@ -95,6 +95,10 @@
/mob/living/silicon/proc/SetName(pickedName as text)
real_name = pickedName
name = real_name
if(istype(id_card))
if(!istype(id_card.chat_user))
id_card.InitializeChatUser()
id_card.chat_user.username = real_name
/mob/living/silicon/proc/show_laws()
return

View File

@@ -2,9 +2,9 @@ var/global/ntnrc_uid = 0
/datum/ntnet_conversation
var/id
var/title = "Untitled Conversation"
var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator,
var/datum/ntnet_user/operator // "Administrator" of this channel. Creator starts as channel's operator,
var/list/messages = list()
var/list/clients = list()
var/list/users = list()
var/direct = FALSE
var/password
@@ -19,25 +19,32 @@ var/global/ntnrc_uid = 0
operator = "NanoTrasen Information Technology Division" // assign a fake operator
..()
/datum/ntnet_conversation/proc/add_message(var/message, var/username, var/mob/user, var/reply_ref)
log_ntirc("[user.client.ckey]/([username]) : [message]", ckey=key_name(user), conversation=title)
/datum/ntnet_conversation/proc/process_message(var/datum/ntnet_message/message, var/update_ui = TRUE)
var/admin_log = message.format_admin_log()
if (admin_log)
log_ntirc("[message.user.client.ckey]/([message.nuser.username]): [admin_log]", ckey=key_name(message.user), conversation=title)
for(var/datum/computer_file/program/chatclient/C in clients)
if(C.program_state > PROGRAM_STATE_KILLED)
C.computer.output_message("<b>([get_title(C)]) <i>[username]</i>:</b> [message] (<a href='byond://?src=\ref[C];Reply=\ref[src]'>Reply</a>)", 0)
if(!C.silent && C.username != username && C.program_state == PROGRAM_STATE_BACKGROUND)
playsound(C.computer, 'sound/machines/twobeep.ogg', 50, 1)
C.computer.output_message("*[C.ringtone]*")
else if(C.username == username)
ntnet_global.add_log(message, C.computer.network_card, TRUE)
for(var/datum/ntnet_user/U in users)
for(var/datum/computer_file/program/chat_client/Cl in U.clients)
var/notification_text = message.format_chat_notification(src, Cl)
if(notification_text && Cl.can_receive_notification(message.client))
Cl.computer.output_message(notification_text, 0)
if(message.play_sound)
Cl.play_notification_sound(message.client)
message = "[worldtime2text()] [username]: [message]"
messages.Add(message)
var/ntnet_log = message.format_ntnet_log(src)
if(ntnet_log)
ntnet_global.add_log(ntnet_log, message.client.computer.network_card, TRUE)
var/chat_log = message.format_chat_log(src)
if(chat_log)
messages.Add(chat_log)
trim_message_list()
/datum/ntnet_conversation/proc/add_status_message(var/message)
messages.Add("[worldtime2text()] -!- [message]")
trim_message_list()
if(update_ui)
for(var/datum/ntnet_user/U in users)
for(var/datum/computer_file/program/chat_client/Cl)
SSvueui.check_uis_for_change(Cl)
/datum/ntnet_conversation/proc/trim_message_list()
if(messages.len <= 50)
@@ -47,89 +54,119 @@ var/global/ntnrc_uid = 0
if(messages.len <= 50)
return
/datum/ntnet_conversation/proc/add_client(var/datum/computer_file/program/chatclient/C)
if(!istype(C))
return
if (C in clients)
return
clients.Add(C)
// No operator, so we assume the channel was empty. Assign this user as operator.
if(!operator)
changeop(C)
for(var/datum/computer_file/program/chatclient/CC in clients)
if(CC.program_state > PROGRAM_STATE_KILLED && CC != C)
if(!direct)
CC.computer.output_message(FONT_SMALL("<b>([get_title(CC)]) <i>[C.username]</i> has entered the chat.</b>"), 0)
/// EXTERNAL PROCs
/datum/ntnet_conversation/proc/begin_direct(var/datum/computer_file/program/chatclient/CA, var/datum/computer_file/program/chatclient/CB)
if(!istype(CA) || !istype(CB))
return
direct = TRUE
clients.Add(CA)
clients.Add(CB)
add_status_message("[CA.username] has opened direct conversation.")
if(CB.program_state > PROGRAM_STATE_KILLED)
CB.computer.output_message(FONT_SMALL("<b>([get_title(CB)]) <i>[CA.username]</i> has opened direct conversation with you.</b>"), 0)
/datum/ntnet_conversation/proc/remove_client(var/datum/computer_file/program/chatclient/C)
if(!istype(C) || !(C in clients))
return
clients.Remove(C)
// Channel operator left, pick new operator
if(C == operator)
operator = null
if(clients.len)
var/datum/computer_file/program/chatclient/newop = pick(clients)
changeop(newop)
for(var/datum/computer_file/program/chatclient/CC in clients)
if(CC.program_state > PROGRAM_STATE_KILLED && CC != C)
CC.computer.output_message(FONT_SMALL("<b>([get_title(CC)]) <i>[C.username]</i> has left the chat.</b>"), 0)
/datum/ntnet_conversation/proc/changeop(var/datum/computer_file/program/chatclient/newop)
if(istype(newop))
operator = newop
add_status_message("Channel operator status transferred to [newop.username].")
/datum/ntnet_conversation/proc/change_title(var/newtitle, var/datum/computer_file/program/chatclient/client)
if(operator != client)
return 0 // Not Authorised
add_status_message("[client.username] has changed channel title from [get_title(client)] to [newtitle]")
for(var/datum/computer_file/program/chatclient/C in clients)
if(C.program_state > PROGRAM_STATE_KILLED && C != client)
C.computer.output_message(FONT_SMALL("([get_title(C)]) <i>[client.username]</i> has changed the channel title to <b>[newtitle]</b>."), 0)
title = newtitle
/datum/ntnet_conversation/proc/get_title(var/datum/computer_file/program/chatclient/cl = null)
/datum/ntnet_conversation/proc/get_title(var/datum/computer_file/program/chat_client/cl = null)
if(direct)
var/names = list()
for(var/datum/computer_file/program/chatclient/C in clients)
names += C.username
if(cl)
names -= cl.username
for(var/datum/ntnet_user/U in users)
names += U.username
if(istype(cl) && istype(cl.my_user))
names -= cl.my_user.username
return "\[DM] [english_list(names)]"
else
return title
/datum/ntnet_conversation/proc/get_dead_title()
if(direct)
var/names = list()
for(var/datum/computer_file/program/chatclient/C in clients)
names += C.username
return "\[DM] [english_list(names)]"
else
return title
/datum/ntnet_conversation/proc/can_see(var/datum/computer_file/program/chatclient/cl)
if(cl in clients)
return TRUE
/datum/ntnet_conversation/proc/can_see(var/datum/computer_file/program/chat_client/cl)
if(cl.netadmin_mode)
return TRUE
if(istype(cl.my_user))
if(cl.my_user in users)
return TRUE
else
for(var/datum/ntnet_user/user in users)
if(cl in user.clients)
return TRUE
if(!direct)
return TRUE
return FALSE
/datum/ntnet_conversation/proc/can_interact(var/datum/computer_file/program/chat_client/cl)
if(cl.netadmin_mode)
return TRUE
if(istype(cl.my_user))
if(cl.my_user in users)
return TRUE
else
for(var/datum/ntnet_user/user in users)
if(cl in user.clients)
return TRUE
return FALSE
/datum/ntnet_conversation/proc/can_manage(var/datum/computer_file/program/chat_client/cl)
if(cl.netadmin_mode)
return TRUE
if(cl.my_user == operator)
return TRUE
return FALSE
/datum/ntnet_conversation/proc/cl_send(var/datum/computer_file/program/chat_client/Cl, var/message, var/mob/user)
if(!istype(Cl) || !can_interact(Cl))
return
var/datum/ntnet_message/message/msg = new(Cl)
msg.message = message
msg.user = user
process_message(msg)
/datum/ntnet_conversation/proc/cl_join(var/datum/computer_file/program/chat_client/Cl)
if(!istype(Cl) || !can_see(Cl) || direct)
return
var/datum/ntnet_message/join/msg = new(Cl)
Cl.my_user.channels.Add(src)
users.Add(Cl.my_user)
if(!operator)
operator = Cl.my_user
var/datum/ntnet_message/new_op/msg2 = new(Cl)
process_message(msg, FALSE)
process_message(msg2)
return
process_message(msg)
/datum/ntnet_conversation/proc/cl_leave(var/datum/computer_file/program/chat_client/Cl)
if(!istype(Cl) || !istype(Cl.my_user) || !(Cl.my_user in users) || !can_interact(Cl) || direct)
return
var/datum/ntnet_message/leave/msg = new(Cl)
Cl.my_user.channels.Remove(src)
users.Remove(Cl.my_user)
if(operator == Cl.my_user)
if(users.len)
operator = pick(users)
var/datum/ntnet_message/new_op/msg2 = new()
msg2.nuser = operator
process_message(msg, FALSE)
process_message(msg2)
return
process_message(msg)
/datum/ntnet_conversation/proc/cl_change_title(var/datum/computer_file/program/chat_client/Cl, var/newTitle)
if(!istype(Cl) || !istype(Cl.my_user) || !can_manage(Cl) || direct)
return
var/datum/ntnet_message/new_title/msg = new(Cl)
msg.title = newTitle
process_message(msg)
title = newTitle
/datum/ntnet_conversation/proc/cl_set_password(var/datum/computer_file/program/chat_client/Cl, var/newPassword)
if(!istype(Cl) || !istype(Cl.my_user) || !can_manage(Cl) || direct)
return
if(newPassword)
password = newPassword
else
password = FALSE
/datum/ntnet_conversation/proc/cl_kick(var/datum/computer_file/program/chat_client/Cl, var/datum/ntnet_user/target)
if(!istype(Cl) || !istype(Cl.my_user) || !can_manage(Cl) || !(target in users) || direct)
return
var/datum/ntnet_message/kick/msg = new(Cl)
msg.target = target
target.channels.Remove(src)
users.Remove(target)
if(operator == target)
if(users.len)
operator = pick(users)
var/datum/ntnet_message/new_op/msg2 = new()
msg2.nuser = operator
process_message(msg, FALSE)
process_message(msg2)
return
process_message(msg)

View File

@@ -0,0 +1,96 @@
// Container for all essesal state for NTRC message while it's proccessed
/datum/ntnet_message
var/mob/user
var/datum/computer_file/program/chat_client/client
var/datum/ntnet_user/nuser
var/play_sound = FALSE
/datum/ntnet_message/New(var/datum/computer_file/program/chat_client/Pr = null, var/mob/user = null)
if(user)
src.user = user
if(Pr)
client = Pr
nuser = Pr.my_user
// Should be sanitized
/datum/ntnet_message/proc/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
return FALSE
/datum/ntnet_message/proc/format_admin_log(var/datum/ntnet_conversation/Conv)
return FALSE
// Should be sanitized
/datum/ntnet_message/proc/format_ntnet_log(var/datum/ntnet_conversation/Conv)
return FALSE
/datum/ntnet_message/proc/format_chat_log(var/datum/ntnet_conversation/Conv)
return FALSE
/datum/ntnet_message/message
play_sound = TRUE
var/message = ""
/datum/ntnet_message/message/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = "<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i>:</b> [sanitize(message)] (<a href='byond://?src=\ref[Cl]&Reply=\ref[Conv]'>Reply</a>)"
/datum/ntnet_message/message/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] [nuser.username]: [message]"
/datum/ntnet_message/message/format_admin_log(var/datum/ntnet_conversation/Conv)
. = message
/datum/ntnet_message/message/format_ntnet_log(var/datum/ntnet_conversation/Conv)
. = "[sanitize(Conv.get_title())] [nuser.username]: [sanitize(message)]"
/datum/ntnet_message/join/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = FONT_SMALL("<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i> has entered the chat.</b>")
/datum/ntnet_message/join/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has entered the chat."
/datum/ntnet_message/leave/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = FONT_SMALL("<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i> has left the chat.</b>")
/datum/ntnet_message/leave/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has left the chat."
/datum/ntnet_message/new_op/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has become operator."
/datum/ntnet_message/new_title
var/title = ""
/datum/ntnet_message/new_title/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has changed channel title from [Conv.get_title()] to [title]"
/datum/ntnet_message/new_title/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = FONT_SMALL("<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i> has changed the channel title to <i>[sanitize(title)].</i></b>")
/datum/ntnet_message/kick
var/datum/ntnet_user/target
/datum/ntnet_message/kick/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has kicked [target.username] from conversation."
/datum/ntnet_message/kick/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = FONT_SMALL("<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i> has kicked <i>[target.username]</i> from conversation.</b>")
/datum/ntnet_message/direct/format_chat_log(var/datum/ntnet_conversation/Conv)
. = "[worldtime2text()] -!- [nuser.username] has opened direct conversation."
/datum/ntnet_message/direct/format_chat_notification(var/datum/ntnet_conversation/Conv, var/datum/computer_file/program/chat_client/Cl)
. = FONT_SMALL("<b>([sanitize(Conv.get_title(Cl))]) <i>[nuser.username]</i> has opened direct conversation with you.</b>")

View File

@@ -0,0 +1,28 @@
/datum/ntnet
var/list/chat_channels = list()
var/list/chat_clients = list()
var/list/chat_users = list()
/datum/ntnet/proc/begin_conversation(var/datum/computer_file/program/chat_client/Cl, var/title)
if(!istype(Cl) || !istype(Cl.my_user))
return
var/datum/ntnet_conversation/Conv = new(title)
Conv.cl_join(Cl)
return Conv
/datum/ntnet/proc/begin_direct(var/datum/computer_file/program/chat_client/Cl, var/datum/ntnet_user/target)
if(!istype(Cl) || !istype(Cl.my_user) || !istype(target) || istype(Cl.my_user.dm_channels[target], /datum/ntnet_conversation))
return
var/datum/ntnet_conversation/Conv = new()
Conv.direct = TRUE
Conv.users.Add(Cl.my_user)
Conv.users.Add(target)
target.dm_channels[Cl.my_user] = Conv
Cl.my_user.dm_channels[target] = Conv
var/datum/ntnet_message/direct/msg = new(Cl)
Conv.process_message(msg)

View File

@@ -0,0 +1,21 @@
/datum/ntnet_user
var/username
var/list/channels = list()
var/list/dm_channels = list()
var/list/clients = list()
/datum/ntnet_user/New()
. = ..()
ntnet_global.chat_users.Add(src)
/datum/ntnet_user/Destroy(force)
. = ..()
ntnet_global.chat_users.Remove(src)
/datum/ntnet_user/proc/generateUsernameIdCard(var/obj/item/card/id/card)
if(!card)
return "Unknown"
return "[card.registered_name] ([card.assignment])"
/datum/ntnet_user/proc/generateUsernameSilicon(var/mob/living/silicon/silicon)
return silicon.name

View File

@@ -11,8 +11,6 @@ var/global/datum/ntnet/ntnet_global = new()
var/list/available_software = list()
var/list/available_software_presets = list()
var/list/available_news = list()
var/list/chat_channels = list()
var/list/chat_clients = list()
var/list/fileservers = list()
var/list/datum/ntnet_account/users = list()
// Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly.

View File

@@ -98,7 +98,6 @@
/obj/item/modular_computer/Destroy()
kill_program(TRUE)
if(registered_id)
registered_id.chat_registered = FALSE
registered_id = null
for(var/obj/item/computer_hardware/CH in src.get_all_components())
uninstall_component(null, CH)
@@ -197,7 +196,7 @@
if(network_card)
return network_card.get_signal(specific_action)
else
return 0
return FALSE
/obj/item/modular_computer/proc/add_log(var/text)
if(!get_ntnet_status())
@@ -366,6 +365,7 @@
/obj/item/modular_computer/proc/enable_service(service, mob/user, var/datum/computer_file/program/S = null)
. = FALSE
if(!S)
S = hard_drive?.find_file_by_name(service)
@@ -382,9 +382,11 @@
return
// Start service
if(S.service_activate())
if(S.service_enable())
enabled_services += S
S.service_state = PROGRAM_STATE_ACTIVE
return TRUE
/obj/item/modular_computer/proc/disable_service(service, mob/user, var/datum/computer_file/program/S = null)
@@ -399,8 +401,8 @@
enabled_services -= S
// Stop service
S.service_deactivate()
S.service_state = PROGRAM_STATE_KILLED
S.service_disable()
S.service_state = PROGRAM_STATE_DISABLED
/obj/item/modular_computer/proc/output_message(var/message, var/message_range)
message_range += message_output_range
@@ -434,12 +436,13 @@
if(!istype(id))
output_error("No ID card found!")
return FALSE
if(id.chat_registered)
output_error("This card is already registered to another account!")
return FALSE
id.chat_registered = TRUE
registered_id = id
if(hard_drive)
for(var/datum/computer_file/program/P in hard_drive.stored_files)
P.event_registered()
output_notice("Registration successful!")
playsound(get_turf(src), 'sound/machines/ping.ogg', 10, 0)
return registered_id
@@ -448,13 +451,12 @@
if(!registered_id)
return FALSE
registered_id.chat_registered = FALSE
registered_id = null
if(hard_drive)
var/datum/computer_file/program/P = hard_drive.find_file_by_name("ntnrc_client")
for(var/datum/computer_file/program/P in hard_drive.stored_files)
P.event_unregistered()
registered_id = null
output_message(SPAN_NOTICE("\The [src] beeps: \"Successfully unregistered ID!\""))
playsound(get_turf(src), 'sound/machines/ping.ogg', 20, 0)
return TRUE
@@ -476,7 +478,6 @@
return TRUE
/obj/item/modular_computer/proc/silence_notifications()
for (var/datum/computer_file/program/P in hard_drive.stored_files)
if (istype(P))
P.event_silentmode()
silent = !silent
for (var/datum/computer_file/program/P in hard_drive.stored_files)
P.event_silentmode()

View File

@@ -3,7 +3,8 @@
visible_message(SPAN_WARNING("\The [src]'s screen flickers briefly and then goes dark."))
if(active_program)
active_program.event_powerfailure(FALSE)
for(var/datum/computer_file/program/PRG in idle_threads)
for(var/datum/computer_file/program/PRG in hard_drive.stored_files)
if(PRG != active_program)
PRG.event_powerfailure(TRUE)
shutdown_computer(FALSE)
power_has_failed = TRUE

View File

@@ -4,6 +4,7 @@
name = "Modular Computer"
desc = "A modular computer. You shouldn't see this."
var/lexical_name = "computer"
var/enabled = FALSE // Whether the computer is turned on.
var/screen_on = TRUE // Whether the computer is active/opened/it's screen is on.
var/working = TRUE // Whether the computer is working.

View File

@@ -1,5 +1,6 @@
/obj/item/modular_computer/handheld
name = "tablet computer"
lexical_name = "tablet"
desc = "A portable device for your needs on the go."
desc_info = "To deploy the charging cable on this device, either drag and drop it over a nearby APC, or click on the APC with the computer in hand."
icon = 'icons/obj/modular_tablet.dmi'

View File

@@ -1,6 +1,7 @@
/obj/item/modular_computer/laptop
anchored = TRUE
name = "laptop computer"
lexical_name = "laptop"
desc = "A portable computer."
desc_info = "You can alt-click the laptop while it's set down on surface to open it up and work with it. Left clicking while it is open will allow you to operate it."
hardware_flag = PROGRAM_LAPTOP

View File

@@ -1,5 +1,6 @@
/obj/item/modular_computer/handheld/pda
name = "PDA"
lexical_name = "tablet"
desc = "The latest in portable microcomputer solutions from Thinktronic Systems, LTD."
icon = 'icons/obj/pda.dmi'
icon_state = "pda"

View File

@@ -53,7 +53,7 @@
/obj/item/modular_computer/silicon/install_default_programs()
hard_drive.store_file(new /datum/computer_file/program/filemanager(src))
hard_drive.store_file(new /datum/computer_file/program/ntnetdownload(src))
hard_drive.store_file(new /datum/computer_file/program/chatclient(src))
hard_drive.store_file(new /datum/computer_file/program/chat_client(src))
hard_drive.remove_file(hard_drive.find_file_by_name("clientmanager"))
addtimer(CALLBACK(src, .proc/register_chat), 1 SECOND)
@@ -62,24 +62,6 @@
enable_computer(null, TRUE) // passing null because we don't want the UI to open
minimize_program()
/obj/item/modular_computer/silicon/verb/send_pda_message()
set category = "AI IM"
set name = "Send Direct Message"
set src in usr
if (usr.stat == DEAD)
to_chat(usr, "You can't send PDA messages because you are dead!")
return
var/datum/computer_file/program/chatclient/CL = hard_drive.find_file_by_name("ntnrc_client")
if(!istype(CL))
output_error("Chat client not installed!")
return
else if(CL.program_state == PROGRAM_STATE_KILLED)
run_program("ntnrc_client")
CL.direct_message()
if(CL.channel)
CL.add_message(CL.send_message())
/obj/item/modular_computer/silicon/robot/drone/install_default_programs()
hard_drive.store_file(new /datum/computer_file/program/filemanager(src))
hard_drive.store_file(new /datum/computer_file/program/ntnetdownload(src))

View File

@@ -1,5 +1,6 @@
/obj/item/modular_computer/handheld/wristbound
name = "wristbound computer"
lexical_name = "wristbound"
desc = "A portable wristbound device for your needs on the go. Quite comfortable."
desc_fluff = "A NanoTrasen design, this wristbound computer allows the user to quickly and safely access critical info, without taking their hands out of the equation."
icon = 'icons/obj/modular_wristbound.dmi'

View File

@@ -24,7 +24,7 @@
var/computer_emagged = FALSE // Set to TRUE if computer that's running us was emagged. Computer updates this every Process() tick
var/ui_header // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /nano/images/status_icons. Be careful not to use too large images!
var/color = "#FFFFFF" // The color of light the computer should emit when this program is open.
var/service_state = PROGRAM_STATE_KILLED // PROGRAM_STATE_KILLED or PROGRAM_STATE_ACTIVE - specifies whether this program's service is running.
var/service_state = PROGRAM_STATE_DISABLED // PROGRAM_STATE_KILLED or PROGRAM_STATE_ACTIVE - specifies whether this program's service is running.
var/silent = FALSE
/datum/computer_file/program/New(var/obj/item/modular_computer/comp)
@@ -196,27 +196,6 @@
if(computer)
return computer.get_header_data()
// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure.
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(var/mob/user)
if(can_run(user, 1) || !requires_access_to_run)
if(nanomodule_path)
NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src)
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
program_state = PROGRAM_STATE_ACTIVE
return TRUE
return FALSE
// 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.
/datum/computer_file/program/proc/kill_program(var/forced = 0)
program_state = PROGRAM_STATE_KILLED
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
if(NM)
qdel(NM)
NM = null
return TRUE
// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation.
// It returns 0 if it can't run or if NanoModule was used instead. I suggest using NanoModules where applicable.
@@ -248,15 +227,6 @@
else
return -1
// Is called when program service is being activated
// Returns 1 if service startup was sucessfull
/datum/computer_file/program/proc/service_activate()
return FALSE
// Is called when program service is being deactivated
/datum/computer_file/program/proc/service_deactivate()
return
/datum/computer_file/program/proc/message_dead(var/message)
for(var/mob/M in player_list)
if(M.stat == DEAD && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))

View File

@@ -1,6 +1,47 @@
// Events are sent to the program by the computer.
// Always include a parent call when overriding an event.
// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure.
// When implementing new program based device, use this to run the program.
/datum/computer_file/program/proc/run_program(var/mob/user)
if(can_run(user, 1) || !requires_access_to_run)
if(nanomodule_path)
NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src)
if(requires_ntnet && network_destination)
generate_network_log("Connection opened to [network_destination].")
program_state = PROGRAM_STATE_ACTIVE
return TRUE
return FALSE
// 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.
/datum/computer_file/program/proc/kill_program(var/forced = 0)
program_state = PROGRAM_STATE_KILLED
if(network_destination)
generate_network_log("Connection to [network_destination] closed.")
if(NM)
qdel(NM)
NM = null
return TRUE
// Is called when program service is being activated
// Returns 1 if service startup was sucessfull
/datum/computer_file/program/proc/service_activate()
return FALSE
// Is called when program service is being deactivated
/datum/computer_file/program/proc/service_deactivate()
return
// Is called when program service is being activated for first time.
/datum/computer_file/program/proc/service_enable()
return service_activate()
// Is called when program service is being deactivated without
/datum/computer_file/program/proc/service_disable()
return service_deactivate()
/// SECOND ORDER EVENTS
// Called when the ID card is removed from computer. ID is removed AFTER this proc.
/datum/computer_file/program/proc/event_idremoved(var/background)
return
@@ -9,11 +50,16 @@
return
// Called when an ID is unregistered from the device.
/datum/computer_file/program/proc/event_unregistered(var/background)
/datum/computer_file/program/proc/event_unregistered()
return
// Called when an ID is unregistered from the device.
/datum/computer_file/program/proc/event_registered()
return
// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss.
/datum/computer_file/program/proc/event_powerfailure(var/background)
if(program_state > PROGRAM_STATE_KILLED)
kill_program(TRUE)
// Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met.

View File

@@ -32,7 +32,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/power_monitor(comp),
new /datum/computer_file/program/alarm_monitor(comp),
@@ -56,7 +56,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/power_monitor(comp),
new /datum/computer_file/program/alarm_monitor(comp),
@@ -81,7 +81,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/game/sudoku(comp),
@@ -108,7 +108,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/suit_sensors(comp),
new /datum/computer_file/program/records/medical(comp),
@@ -130,7 +130,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/suit_sensors(comp),
@@ -155,7 +155,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/ntnetmonitor(comp),
new /datum/computer_file/program/aidiag(comp),
@@ -178,7 +178,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/ntnetmonitor(comp),
new /datum/computer_file/program/aidiag(comp),
@@ -201,7 +201,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/ntnetmonitor(comp),
@@ -225,7 +225,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/card_mod(comp),
new /datum/computer_file/program/comm(comp, FALSE),
@@ -246,7 +246,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/civilian/cargocontrol(comp),
new /datum/computer_file/program/card_mod(comp),
@@ -269,7 +269,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/card_mod(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/camera_monitor(comp),
@@ -317,7 +317,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/camera_monitor(comp),
new /datum/computer_file/program/comm(comp),
@@ -341,7 +341,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/camera_monitor(comp),
new /datum/computer_file/program/comm(comp),
@@ -367,7 +367,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/camera_monitor(comp),
new /datum/computer_file/program/digitalwarrant(comp),
new /datum/computer_file/program/penal_mechs(comp),
@@ -391,7 +391,7 @@
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/camera_monitor(comp),
@@ -415,7 +415,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/game/arcade(comp),
new /datum/computer_file/program/game/sudoku(comp),
@@ -435,7 +435,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/civilian/janitor(comp),
new /datum/computer_file/program/game/arcade(comp),
@@ -468,7 +468,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargocontrol(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/civilian/cargodelivery(comp),
@@ -488,7 +488,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargodelivery(comp),
new /datum/computer_file/program/ntsl2_interpreter(comp)
)
@@ -506,7 +506,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/game/sudoku(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/records/employment(comp),
@@ -525,7 +525,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/camera_monitor(comp),
new /datum/computer_file/program/alarm_monitor(comp),
@@ -594,7 +594,7 @@
new /datum/computer_file/program/filemanager(comp),
new /datum/computer_file/program/manifest(comp),
new /datum/computer_file/program/newsbrowser(comp),
new /datum/computer_file/program/chatclient(comp),
new /datum/computer_file/program/chat_client(comp),
new /datum/computer_file/program/merchant(comp)
)
return _prg_list

View File

@@ -1,381 +1,253 @@
/datum/computer_file/program/chatclient
/datum/computer_file/program/chat_client
filename = "ntnrc_client"
filedesc = "Chat Client"
program_icon_state = "command"
extended_desc = "This program allows communication over the NTNRC network."
extended_desc = "This program allows communication over the NTRC network."
size = 2
requires_ntnet = TRUE
requires_ntnet_feature = NTNET_COMMUNICATION
network_destination = "NTNRC server"
ui_header = "ntnrc_idle.gif"
program_type = PROGRAM_TYPE_ALL
network_destination = "NTRC server"
available_on_ntnet = TRUE
nanomodule_path = /datum/nano_module/program/computer_chatclient
color = LIGHT_COLOR_GREEN
silent = FALSE
var/last_message // Used to generate the toolbar icon
var/username
var/datum/ntnet_conversation/channel
var/operator_mode = FALSE // Channel operator mode
var/datum/ntnet_user/my_user
var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords)
var/set_offline = FALSE // appear "invisible"
var/list/directmessagechannels = list()
var/ringtone = "beep"
var/syndi_auth = FALSE
/datum/computer_file/program/chatclient/New(var/obj/item/modular_computer/comp)
..(comp)
if(!comp)
return
/datum/computer_file/program/chatclient/Destroy()
ntnet_global.chat_clients -= src
/datum/computer_file/program/chat_client/Destroy()
return ..()
/datum/computer_file/program/chatclient/Topic(href, href_list)
/datum/computer_file/program/chat_client/proc/can_receive_notification(var/datum/computer_file/program/chat_client/from)
return ((program_state > PROGRAM_STATE_KILLED || service_state > PROGRAM_STATE_KILLED) && from != src && get_signal(NTNET_COMMUNICATION))
/datum/computer_file/program/chat_client/proc/play_notification_sound(var/datum/computer_file/program/chat_client/from)
if(!silent && src != from && program_state == PROGRAM_STATE_BACKGROUND)
playsound(computer, 'sound/machines/twobeep.ogg', 50, 1)
computer.output_message("[icon2html(computer, world)] *[ringtone]*", 2)
/datum/computer_file/program/chat_client/Topic(href, href_list)
if(..())
return TRUE
if(href_list["PRG_toggleringer"])
. = TRUE
silent = !silent
if(href_list["PRG_setringtone"])
. = TRUE
var/t = input(usr, "Please enter new ringtone", filedesc, ringtone) as text|null
if(!usr.Adjacent(computer) || !t)
return
if(href_list["ringtone"])
var/newRingtone = href_list["ringtone"]
var/obj/item/device/uplink/hidden/H = computer.hidden_uplink
if(istype(H) && H.check_trigger(usr, lowertext(t), lowertext(H.pda_code)))
if(istype(H) && H.check_trigger(usr, lowertext(newRingtone), lowertext(H.pda_code)))
to_chat(usr, SPAN_NOTICE("\The [computer] softly beeps."))
syndi_auth = TRUE
SSnanoui.close_uis(NM)
SSvueui.close_uis(src)
else
t = sanitize(t, 20)
ringtone = t
newRingtone = sanitize(newRingtone, 20)
ringtone = newRingtone
SSvueui.check_uis_for_change(src)
if(href_list["PRG_speak"])
. = TRUE
add_message(send_message())
if(href_list["Reply"])
. = TRUE
var/datum/ntnet_conversation/C = locate(href_list["Reply"]) in ntnet_global.chat_channels
if(!istype(C))
to_chat(usr, SPAN_WARNING("The target channel couldn't be found and has likely been deleted!"))
// User only commands
if(!istype(my_user))
return
var/message = send_message()
if(!(C in ntnet_global.chat_channels))
to_chat(usr, SPAN_WARNING("The target channel couldn't be found and has likely been deleted!"))
// Following actions require signal
if(!get_signal(NTNET_COMMUNICATION))
return
add_message(message, C)
if(href_list["PRG_joinchannel"])
. = TRUE
var/datum/ntnet_conversation/C
for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels)
if(chan.id == text2num(href_list["PRG_joinchannel"]))
C = chan
break
if(!C)
return TRUE
if(netadmin_mode)
channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others.
return TRUE
if(C.password)
if(href_list["send"])
var/mob/living/user = usr
var/password = sanitize(input(user, "Access Denied. Enter password:"))
if(C?.password == password)
C.add_client(src)
channel = C
return TRUE
C.add_client(src)
message_dead(FONT_SMALL("<b>([C.get_dead_title()]) A new client ([username]) has entered the chat.</b>"))
channel = C
if(href_list["PRG_leavechannel"])
. = TRUE
if(channel && !channel.direct)
channel.remove_client(src)
message_dead(FONT_SMALL(FONT_SMALL("<b>([channel.get_dead_title()]) A client ([username]) has left the chat.</b>")))
channel = null
if(href_list["PRG_backtomain"])
. = TRUE
channel = null
if(href_list["PRG_newchannel"])
. = TRUE
var/mob/living/user = usr
var/channel_title = sanitize(input(user, "Enter channel name or leave blank to cancel:"))
if(!channel_title)
return
var/datum/ntnet_conversation/C = new /datum/ntnet_conversation(channel_title)
C.add_client(src)
C.operator = src
channel = C
message_dead(FONT_SMALL("<b>([channel.get_dead_title()]) A new channel has been made by [username].</b>"))
if(href_list["PRG_toggleadmin"])
. = TRUE
var/datum/ntnet_conversation/conv = locate(href_list["send"]["target"])
var/message = href_list["send"]["message"]
if(istype(conv) && message)
if(ishuman(user))
user.visible_message("[SPAN_BOLD("\The [user]")] taps on [user.get_pronoun("his")] [computer.lexical_name]'s screen.")
conv.cl_send(src, message, user)
if(href_list["join"])
var/datum/ntnet_conversation/conv = locate(href_list["join"]["target"])
var/password = href_list["join"]["password"]
if(istype(conv))
if(conv.password)
if(conv.password == password)
conv.cl_join(src)
else
// How do I alert of password invalid?
else
conv.cl_join(src)
if(href_list["leave"])
var/datum/ntnet_conversation/conv = locate(href_list["leave"])
if(istype(conv))
conv.cl_leave(src)
SSvueui.check_uis_for_change(src)
if(href_list["kick"])
var/datum/ntnet_conversation/conv = locate(href_list["kick"]["target"])
var/datum/ntnet_user/tUser = locate(href_list["kick"]["user"])
if(istype(conv) && istype(tUser))
conv.cl_kick(src, tUser)
if(href_list["set_password"])
var/datum/ntnet_conversation/conv = locate(href_list["set_password"]["target"])
var/password = href_list["set_password"]["password"]
if(istype(conv))
conv.cl_set_password(src, password)
if(href_list["change_title"])
var/datum/ntnet_conversation/conv = locate(href_list["change_title"]["target"])
var/newTitle = href_list["change_title"]["title"]
if(istype(conv))
conv.cl_change_title(src, newTitle)
if(href_list["new_channel"])
ntnet_global.begin_conversation(src, sanitize(href_list["new_channel"]))
if(href_list["delete"])
var/datum/ntnet_conversation/conv = locate(href_list["delete"])
if(istype(conv) && conv.can_manage(src))
ntnet_global.chat_channels.Remove(conv)
qdel(conv)
SSvueui.check_uis_for_change(src)
if(href_list["direct"])
var/datum/ntnet_user/tUser = locate(href_list["direct"])
ntnet_global.begin_direct(src, tUser)
if(href_list["toggleadmin"])
if(netadmin_mode)
netadmin_mode = FALSE
if(channel)
channel.remove_client(src) // We shouldn't be in channel's user list, but just in case...
channel = null
return TRUE
var/mob/living/user = usr
if(can_run(usr, 1, access_network))
if(channel)
var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No")
if(response == "Yes")
if(channel)
channel.remove_client(src)
channel = null
else
return
var/mob/living/user = usr
if(can_run(user, TRUE, access_network))
netadmin_mode = TRUE
if(href_list["PRG_changename"])
. = TRUE
var/mob/living/user = usr
var/new_name = sanitize(input(user, "Enter new nickname or leave blank to cancel:"))
if(!new_name)
return TRUE
var/comp_name = ckey(new_name)
for(var/cl in ntnet_global.chat_clients)
var/datum/computer_file/program/chatclient/C = cl
if(ckey(C.username) == comp_name || comp_name == "cancel")
alert(user, "This nickname is already taken.")
return TRUE
for(var/datum/ntnet_conversation/channel in ntnet_global.chat_channels)
if(src in channel.clients)
channel.add_status_message("[username] is now known as [new_name].")
username = new_name
if(href_list["PRG_savelog"])
. = TRUE
if(!channel)
return
var/mob/living/user = usr
var/logname = input(user, "Enter desired logfile name (.log) or leave blank to cancel:")
if(!logname || !channel)
return TRUE
var/datum/computer_file/data/logfile = new /datum/computer_file/data/logfile()
// Now we will generate HTML-compliant file that can actually be viewed/printed.
logfile.filename = logname
logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]"
for(var/logstring in channel.messages)
logfile.stored_data += "[logstring]\[BR\]"
logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]"
logfile.calculate_size()
if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile))
if(!computer)
// This program shouldn't even be runnable without computer.
crash_with("Var computer is null!")
return TRUE
if(!computer.hard_drive)
computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.")
else // In 99.9% cases this will mean our HDD is full
computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.")
if(href_list["PRG_renamechannel"])
. = TRUE
if(!operator_mode || !channel)
return TRUE
var/mob/living/user = usr
var/newname = sanitize(input(user, "Enter new channel name or leave blank to cancel:"))
if(!newname || !channel)
return
channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.")
channel.title = newname
if(href_list["PRG_deletechannel"])
. = TRUE
if(channel && ((channel.operator == src) || netadmin_mode))
qdel(channel)
channel = null
if(href_list["PRG_setpassword"])
. = TRUE
if(!channel || ((channel.operator != src) && !netadmin_mode))
return TRUE
var/mob/living/user = usr
var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:"))
if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode))
return TRUE
if(newpassword == "nopassword")
channel.password = ""
else
channel.password = newpassword
if(href_list["PRG_directmessage"])
. = TRUE
direct_message(usr)
/datum/computer_file/program/chatclient/proc/send_message()
SSvueui.check_uis_for_change(src)
if(href_list["Reply"])
var/mob/living/user = usr
var/datum/ntnet_conversation/conv = locate(href_list["Reply"])
var/message = input(user, "Enter message or leave blank to cancel: ")
if(istype(conv) && message)
if(ishuman(user))
user.visible_message("[SPAN_BOLD("\The [user]")] taps on [user.get_pronoun("his")] computer's screen.")
var/message = sanitize(input(user, "Enter a message to send: ") as null|text)
if(!message)
return
return message
user.visible_message("[SPAN_BOLD("\The [user]")] taps on [user.get_pronoun("his")] [computer.lexical_name]'s screen.")
conv.cl_send(src, message, user)
/datum/computer_file/program/chatclient/proc/add_message(var/message, var/datum/ntnet_conversation/specific_channel)
if(!message)
return
var/datum/ntnet_conversation/sent_channel
if(specific_channel)
sent_channel = specific_channel
/datum/computer_file/program/chat_client/service_activate()
. = ..()
if(istype(my_user) && get_signal(NTNET_COMMUNICATION))
activate_chat_client()
return TRUE
else
sent_channel = channel
if(!sent_channel) // panikk - geeves
return
sent_channel.add_message(message, username, usr)
message_dead(FONT_SMALL("<b>([sent_channel.get_dead_title()]) [username]:</b> [message]"))
/datum/computer_file/program/chatclient/proc/direct_message(var/mob/user)
var/clients = list()
var/names = list()
for(var/cl in ntnet_global.chat_clients - src)
var/datum/computer_file/program/chatclient/C = cl
if(C.set_offline)
continue
clients[C.username] = C
names += C.username
if(!length(names))
to_chat(user, SPAN_WARNING("You are the only user with an active account!"))
return
var/picked = input(user, "Select with whom you would like to start a conversation.") as null|anything in names
if(!picked)
return
var/datum/computer_file/program/chatclient/otherClient = clients[picked]
if(picked)
if(directmessagechannels[otherClient])
channel = directmessagechannels[otherClient]
return
var/datum/ntnet_conversation/C = new /datum/ntnet_conversation("", TRUE)
C.begin_direct(src, otherClient)
channel = C
directmessagechannels[otherClient] = C
otherClient.directmessagechannels[src] = C
/datum/computer_file/program/chatclient/process_tick()
..()
if(program_state != PROGRAM_STATE_KILLED)
ui_header = "ntnrc_idle.gif"
if(channel)
// Remember the last message. If there is no message in the channel remember null.
if(length(channel.messages) > 1) // len - 1 = 0 and that's array out of bounds
last_message = channel.messages[channel.messages.len - 1]
else
last_message = null
else
last_message = null
return 1
if(channel?.messages?.len)
ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif"
else
ui_header = "ntnrc_idle.gif"
/datum/computer_file/program/chatclient/kill_program(var/forced = FALSE)
if(!forced)
var/confirm = alert("Are you sure you want to close the NTNRC Client? You will not be reachable via messaging if you do so.", "Close?", "Yes", "No")
if((confirm != "Yes") || (CanUseTopic(usr) != STATUS_INTERACTIVE))
return FALSE
ntnet_global.chat_clients -= src
/datum/computer_file/program/chat_client/service_deactivate()
. = ..()
deactivate_chat_client()
channel = null
..(forced)
return TRUE
/datum/computer_file/program/chat_client/process_tick()
. = ..()
/datum/computer_file/program/chatclient/run_program(var/mob/user)
if(!computer)
return
if(!istype(computer, /obj/item/modular_computer/silicon))
if((!computer.registered_id && !computer.register_account(src)))
return
if(!(src in ntnet_global.chat_clients))
ntnet_global.chat_clients += src
if(!username)
username = username_from_id()
return ..(user)
/datum/computer_file/program/chat_client/kill_program(var/forced = FALSE)
return ..(forced)
/datum/computer_file/program/chatclient/proc/username_from_id()
/datum/computer_file/program/chat_client/run_program(var/mob/user)
if(!istype(my_user))
if(istype(computer, /obj/item/modular_computer/silicon))
var/obj/item/modular_computer/silicon/SC = computer
return SC.computer_host.name
if(!computer.registered_id)
return "Unknown"
return "[computer.registered_id.registered_name] ([computer.registered_id.assignment])"
/datum/computer_file/program/chatclient/event_unregistered()
..()
computer.set_autorun(filename)
ntnet_global.chat_clients -= src
kill_program(TRUE)
/datum/computer_file/program/chatclient/event_silentmode()
..()
if(computer.silent != silent)
silent = computer.silent
/datum/nano_module/program/computer_chatclient
name = "Chat Client"
/datum/nano_module/program/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(!ntnet_global || !ntnet_global.chat_channels)
var/mob/living/silicon/S = SC.computer_host
S.id_card.InitializeChatUser()
my_user = S.id_card.chat_user
else
if((!computer.registered_id && !computer.register_account(src)))
return
if(service_state == PROGRAM_STATE_DISABLED)
if(!computer.enable_service(null, user, src))
return
return ..(user)
var/datum/computer_file/program/chatclient/C = program
/datum/computer_file/program/chat_client/event_registered()
. = ..()
computer.registered_id.InitializeChatUser()
my_user = computer.registered_id.chat_user
if(service_state > PROGRAM_STATE_KILLED)
activate_chat_client()
if(C.computer.hidden_uplink && C.syndi_auth)
if(alert(user, "Resume or close and secure?", name, "Resume", "Close") == "Resume")
C.computer.hidden_uplink.trigger(user)
/datum/computer_file/program/chat_client/event_unregistered()
. = ..()
if(service_state > PROGRAM_STATE_KILLED)
deactivate_chat_client()
my_user = null
/datum/computer_file/program/chat_client/event_silentmode()
. = ..()
silent = computer.silent
/datum/computer_file/program/chat_client/ui_interact(var/mob/user)
if(computer.hidden_uplink && syndi_auth)
if(alert(user, "Resume or close and secure?", filedesc, "Resume", "Close") == "Resume")
computer.hidden_uplink.trigger(user)
return
else
C.syndi_auth = FALSE
syndi_auth = FALSE
var/list/data = list()
if(program)
data = list("_PC" = program.get_header_data())
if(!istype(C))
return
data["adminmode"] = C.netadmin_mode
if(C.channel)
data["title"] = C.channel.get_title(C)
var/list/messages[0]
for(var/M in C.channel.messages)
messages.Add(list(list(
"msg" = M
)))
data["messages"] = messages
var/list/clients[0]
for(var/datum/computer_file/program/chatclient/cl in C.channel.clients)
clients.Add(list(list(
"name" = cl.username,
"active" = cl.program_state > PROGRAM_STATE_KILLED
)))
data["clients"] = clients
C.operator_mode = (C.channel.operator == C) ? 1 : 0
data["is_operator"] = C.operator_mode || C.netadmin_mode
data["is_direct"] = C.channel.direct
else // Channel selection screen
var/list/all_channels[0]
for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels)
if(conv && conv.title && conv.can_see(program))
all_channels.Add(list(list(
"chan" = conv.get_title(C),
"id" = conv.id,
"con" = (program in conv.clients)
)))
data["all_channels"] = all_channels
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700, state = state)
ui.auto_update_layout = 1
ui.set_initial_data(data)
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-chat-index", 600, 500, capitalize(filedesc))
ui.open()
ui.set_auto_update(TRUE)
/datum/computer_file/program/chat_client/vueui_transfer(oldobj)
SSvueui.transfer_uis(oldobj, src, "mcomputer-chat-index", 600, 500, capitalize(filedesc))
return TRUE
/datum/computer_file/program/chat_client/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
. = ..()
data = . || data || list()
// Gather data for computer header
var/headerdata = get_header_data(data["_PC"])
if(headerdata)
data["_PC"] = headerdata
. = data
data["service"] = service_state > PROGRAM_STATE_KILLED
data["registered"] = istype(my_user)
data["signal"] = get_signal(NTNET_COMMUNICATION)
data["ringtone"] = ringtone
data["netadmin_mode"] = netadmin_mode
data["can_netadmin_mode"] = can_run(user, FALSE, access_network)
if(data["registered"] && data["service"] && data["signal"])
data["channels"] = list()
for(var/c in ntnet_global.chat_channels)
var/datum/ntnet_conversation/Channel = c
if(istype(Channel) && Channel.can_see(src))
var/ref = ref(Channel)
var/can_interact = Channel.can_interact(src)
var/can_manage = Channel.can_manage(src)
data["channels"][ref] = list(
"title" = Channel.get_title(src),
"direct" = Channel.direct,
"password" = !!Channel.password,
"can_interact" = can_interact,
"can_manage" = can_manage
)
if(can_interact)
data["channels"][ref]["msg"] = Channel.messages
data["channels"][ref]["users"] = list()
for(var/datum/ntnet_user/U in Channel.users)
var/uref = ref(U)
data["channels"][ref]["users"][uref] = U.username
data["users"] = list()
for(var/u in ntnet_global.chat_users)
var/datum/ntnet_user/nUser = u
if(nUser != my_user)
var/ref = ref(nUser)
data["users"][ref] = nUser.username
return data
/datum/computer_file/program/chat_client/proc/activate_chat_client()
if(!istype(my_user))
return
if(!(src in my_user.clients))
my_user.clients.Add(src)
if(!(src in ntnet_global.chat_clients))
ntnet_global.chat_clients.Add(src)
/datum/computer_file/program/chat_client/proc/deactivate_chat_client()
if(!istype(my_user))
return
if(src in my_user.clients)
my_user.clients.Remove(src)
if(src in ntnet_global.chat_clients)
ntnet_global.chat_clients.Remove(src)

View File

@@ -70,7 +70,7 @@
computer.enrolled = 2 // private devices
computer.hard_drive.store_file(new /datum/computer_file/program/filemanager(computer))
computer.hard_drive.store_file(new /datum/computer_file/program/ntnetdownload(computer))
computer.hard_drive.store_file(new /datum/computer_file/program/chatclient(computer))
computer.hard_drive.store_file(new /datum/computer_file/program/chat_client(computer))
return TRUE
//Set´s up the programs from the preset

View File

@@ -0,0 +1,6 @@
author: Karolis2011
delete-after: True
changes:
- rscadd: "Modular computer chat client UI and internals been revamped."
- tweak: "Newest chat messages are now at the top instead of bottom of chat window."
- rscadd: "Now you can use chat client simultaneously on multiple devices."

View File

@@ -1,84 +0,0 @@
{{if data.adminmode}}
<h1>ADMINISTRATIVE MODE</h1>
{{/if}}
{{if data.title}}
<div class="itemLabel">
Current channel:
</div>
<div class="itemContent">
{{:data.title}}
</div>
<div class="itemLabel">
Operator access:
</div>
<div class="itemContent">
{{if data.is_operator}}
<b>Enabled</b>
{{else}}
<b>Disabled</b>
{{/if}}
</div>
<div class="itemLabel">
Chat Controls:
</div>
<div class="itemContent">
<table>
<tr><td>{{:helper.link("Return to channel list", null, {'PRG_backtomain' : 1})}}
<tr><td>{{:helper.link("Direct message", null, {'PRG_directmessage' : 1})}}
<tr><td>{{:helper.link("Change nickname", null, {'PRG_changename' : 1})}}
<tr><td>{{:helper.link("Toggle administration mode", null, {'PRG_toggleadmin' : 1})}}
{{if !data.is_direct}}
<tr><td>{{:helper.link("Leave channel", null, {'PRG_leavechannel' : 1})}}
{{/if}}
<tr><td>{{:helper.link("Save log to local drive", null, {'PRG_savelog' : 1})}}
{{if data.is_operator}}
<tr><td>{{:helper.link("Rename channel", null, {'PRG_renamechannel' : 1})}}
<tr><td>{{:helper.link("Set password", null, {'PRG_setpassword' : 1})}}
<tr><td>{{:helper.link("Delete channel", null, {'PRG_deletechannel' : 1})}}
{{/if}}
</table>
</div>
<b>Connected Users</b><br>
{{for data.clients}}
{{:value.name}}
{{if !value.active}}
<i>(Away)</i>
{{/if}}
<br>
{{/for}}
<b>Chat Window</b>
<div class="statusDisplay" style="overflow: auto;">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{for data.messages}}
{{:value.msg}}<br>
{{/for}}
</div>
</div>
</div>
<div class="itemLabel">
Messaging:
</div>
<div class="itemContent">
<table>
<tr><td>{{:helper.link("Send message", null, {'PRG_speak' : 1})}}</td></tr>
</table>
</div>
{{else}}
<b>Controls:</b>
<table>
<tr><td>{{:helper.link("Direct message", null, {'PRG_directmessage' : 1})}}
<tr><td>{{:helper.link("Change nickname", null, {'PRG_changename' : 1})}}
<tr><td>{{:helper.link("New Channel", null, {'PRG_newchannel' : 1})}}
<tr><td>{{:helper.link("Toggle Administration Mode", null, {'PRG_toggleadmin' : 1})}}
<tr><td>{{:helper.link("Toggle Ringer", null, {'PRG_toggleringer' : 1})}}
<tr><td>{{:helper.link("Set Ringtone", null, {'PRG_setringtone' : 1})}}
</table>
<b>Available channels:</b>
<table>
{{for data.all_channels}}
<tr><td>{{:helper.link(value.chan, null, {'PRG_joinchannel' : value.id}, null, value.con ? 'selected' : null)}}<br>
{{/for}}
</table>
{{/if}}

View File

@@ -1,101 +0,0 @@
<i>Welcome to NT IT client manager</i><hr>
<div>This is the Client Management Application of the Nanotrasen IT Department. It is used ensure that the clients comply with the corporate IT policy, enroll new clients and manage clients remotely</div>
<br>
{{if data.error_message}}
<div class="itemContent" style="color: red; text-align: center; width: 100%;">
<b>Error:</b> {{:data.error_message}}
</div>
<br>
{{/if}}
<div>
<h2>Status</h2>
<div class="item">
<div class="itemLabel">
Enrollment Status:
</div>
<div class="itemContent">
{{if data.status_enrollment == 0}}
Unconfigured
{{else data.status_enrollment == 1}}
Work Device
{{else data.status_enrollment == 2}}
Private Device
{{else}}
Error - Contact the IT Department
{{/if}}
</div>
</div>
{{if data.status_enrollment == 1}}
<div class="item">
<div class="itemLabel">
Policy Compliance Status:
</div>
<div class="itemContent">
{{if data.status_compliance == 0}}
Not Compliant
{{else data.status_compliance == 1}}
Compliant
{{else}}
Error - Contact the IT Department
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Remote Management Status:
</div>
<div class="itemContent">
{{if data.status_remote == 0}}
Inactive
{{else data.status_remote == 1}}
Active
{{else}}
Error - Contact the IT Department
{{/if}}
</div>
</div>
{{/if}}
{{if data.status_enrollment == 0}}
</div>
<div>
<div><h2>Device Enrollment</h2></div>
<table>
<tr>
<td width="150px">
<div><b>Device Type:</b></div>
<div>
{{:helper.link('Company', null, { "PRG_dev_type" : 1 }, data.dev_type == 1 ? 'selected' : null)}}
{{:helper.link('Private', null, { "PRG_dev_type" : 2 }, data.dev_type == 2 ? 'selected' : null)}}
</div>
</td>
<td>
{{if data.dev_type == 1}}
<div><b>Device Preset:</b></div>
<div>
{{for data.dev_presets}}
<div>{{:helper.link(value.display_name,null,{"PRG_dev_preset" : value.name}, value.name == data.dev_preset ? 'selected' : null)}}</div>
{{/for}}
</div>
{{/if}}
</td>
</tr>
<tr>
<td>
<div><b>Enroll Device:</b></div>
<div>{{:helper.link('Confirm', null, { "PRG_enroll" : 1 })}}</div>
</td>
</tr>
</table>
</div>
{{else data.status_enrollment == 1}}
<div>
<h2>Control</h2>
<div>{{:helper.link("Request Support",null,{"PRG_req_support" : 1}, 'disabled')}}</div>
<div>{{:helper.link("Reset Device",null,{"PRG_reset_device" : 1}, 'disabled')}}</div>
</div>
{{else data.status_enrollment == 2}}
<div>
<h2>Control</h2>
<div>{{:helper.link("Reset Device",null,{"PRG_reset_device" : 1}, 'disabled')}}</div>
</div>
{{/if}}

View File

@@ -1,132 +0,0 @@
<i>Welcome to software download utility. Please select which software you wish to download.</i><hr>
{{if data.error}}
<h2>Download Error</h2>
<div class="itemLabel">
Information:
</div>
<div class="itemContent">
{{:data.error}}
</div>
<div class="itemLabel">
Reset Program:
</div>
<div class="itemContent">
{{:helper.link("RESET", null, {'PRG_reseterror' : 1})}}
</div>
{{else data.downloadname}}
<h2>Download Running</h2>
<i>Please wait...</i>
<div class="itemLabel">
File description:
</div>
<div class="itemContent">
{{:data.downloaddesc}}
</div>
<div class="itemLabel">
File name:
</div>
<div class="itemContent">
{{:data.downloadname}}
</div>
<div class="itemLabel">
File size:
</div>
<div class="itemContent">
{{:data.downloadcompletion}}GQ / {{:data.downloadsize}}GQ
</div>
<div class="itemLabel">
Transfer Rate:
</div>
<div class="itemContent">
{{:data.downloadspeed}} GQ/s
</div>
<div class="itemLabel">
Download progress:
</div>
<div class="itemContent">
{{:helper.displayBar(data.downloadcompletion, 0, data.downloadsize, 'good')}}
</div>
{{else}}
<h2>Primary software repository</h2>
<div class="itemLabel">
Hard drive:
</div>
<div class="itemContent">
{{:helper.displayBar(data.disk_used, 0, data.disk_size, 'good')}}
{{:data.disk_used}}GQ / {{:data.disk_size}}GQ
</div>
<br>
{{for data.downloadable_programs}}
<hr>
<div class = "item">
<div class="itemLabel">
Program name:
</div>
<div class="itemContent">
<b>{{:value.filedesc}}</b>
</div>
<div class="itemLabel">
File name:
</div>
<div class="itemContent">
{{:value.filename}} ({{:value.size}} GQ)
</div>
<div class="itemLabel">
Description:
</div>
<div class="itemContent">
{{:value.fileinfo}}
</div>
<div class="itemLabel">
File controls:
</div>
<div class="itemContent">
{{:helper.link("Download", null, {'PRG_downloadfile' : value.filename})}}
</div>
</div>
{{empty}}
<hr><br>
{{if data.locked}}
<div class="itemContent" style="color: red; text-align: center; width: 100%;"><b>ERROR: This terminal is locked, users are disallowed from downloading software. Please contact your system administrator.</b></div>
{{else}}
<div class="itemContent" style="text-align: center; width: 100%;"><b>There are no available programs for your clearance level at this time.</b></div>
{{/if}}
<br>
{{/for}}
{{if data.hackedavailable}}
<h2>*UNKNOWN* software repository</h2>
<i>Please note that NanoTrasen does not recommend download of software from non-official servers.</i>
{{for data.hacked_programs}}
<div class = "item">
<div class="itemLabel">
Program name:
</div>
<div class="itemContent">
{{:value.filedesc}}
</div>
<div class="itemLabel">
File name:
</div>
<div class="itemContent">
{{:value.filename}} ({{:value.size}} GQ)
</div>
<div class="itemLabel">
Description:
</div>
<div class="itemContent">
{{:value.fileinfo}}
</div>
<div class="itemLabel">
File controls:
</div>
<div class="itemContent">
{{:helper.link("Download", null, {'PRG_downloadfile' : value.filename})}}
</div>
</div>
{{/for}}
{{/if}}
{{/if}}
<br><br>
<div style="position: fixed; bottom: 5px;"><hr><i>NTOS v2.0.6 Copyright NanoTrasen 2450 - 2460</i></div>

View File

@@ -0,0 +1,42 @@
<template>
<div>
<vui-button v-if="password == null" @click="join">{{ ch.title }}</vui-button>
<template v-else>
<input type="text" v-model="password" @keydown.enter="join_with">
<vui-button :params="{join: {target: re, password: password}}">Join {{ ch.title }}</vui-button>
</template>
</div>
</template>
<script>
import { sendToTopic } from '@/utils'
export default {
data() {
return {
password: null,
}
},
methods: {
join() {
if(this.ch.password) {
this.password = ''
} else {
sendToTopic({join: {target: this.re}})
}
},
join_with() {
sendToTopic({join: {target: this.re, password: this.password}})
}
},
props: {
ch: {
type: Object,
default: {},
},
re: {
type: String,
default: "",
},
},
}
</script>

View File

@@ -0,0 +1,119 @@
<template>
<div>
<h3>{{ channel.title }}</h3>
<div>
<template v-if="!channel.direct">
<vui-button :params="{leave: reference}" @click="$emit('on-leave')">Leave</vui-button>
</template>
<template v-if="channel.can_manage">
<template v-if="password != null">
<input type="text" v-model="password" @keydown.enter="set_password">
<vui-button @click="set_password">Set password</vui-button>
</template>
<vui-button v-else-if="!channel.direct" @click="password = ''">Set password</vui-button>
<template v-if="title != null">
<input type="text" v-model="title" @keydown.enter="set_title">
<vui-button @click="set_title">Change title</vui-button>
</template>
<vui-button v-else-if="!channel.direct" @click="title = channel.title">Change title</vui-button>
<vui-button :params="{delete: reference}" @click="$emit('on-leave')">Delete channel</vui-button>
</template>
</div>
<div>
<div v-for="(user, uref) in channel.users" :key="uref">
{{ user }}
<vui-button v-if="channel.can_manage && !channel.direct" :params="{kick: {target: reference, user: uref}}">Kick</vui-button>
</div>
</div>
<div class="message-chat" ref="chat">
<div v-for="(msg, index) in messages" :key="index">{{ msg }}</div>
</div>
<div class="message-container">
<input class="message-input" type="text" v-model="send_buffer" @keydown.enter="send_msg"/>
<vui-button @click="send_msg" class="message-send">Send</vui-button>
</div>
</div>
</template>
<script>
import { sendToTopic } from '@/utils'
export default {
data() {
return {
s: this.$root.$data.state,
send_buffer: "",
password: null,
title: null,
wasAtBottom: true,
}
},
computed: {
channel() {
return this.s.channels[this.reference]
},
messages() {
return this.channel.msg
}
},
props: {
reference: {
type: String,
default: "",
},
},
methods: {
send_msg() {
sendToTopic({
send: { message: this.send_buffer, target: this.reference },
})
this.send_buffer = ""
},
set_password() {
sendToTopic({set_password: {target: this.reference, password: this.password}})
this.password = null
},
set_title() {
sendToTopic({change_title: {target: this.reference, title: this.title}})
this.title = null
},
scrollBottom() {
let chat = this.$refs.chat
chat.scrollTop = chat.scrollHeight
}
},
mounted() {
this.scrollBottom()
},
beforeUpdate() {
let chat = this.$refs.chat
this.wasAtBottom = (chat.scrollHeight - chat.scrollTop === chat.clientHeight)
},
updated() {
if(this.wasAtBottom) {
this.scrollBottom()
}
}
}
</script>
<style lang="scss" scoped>
.message-chat {
width: 100%;
background: #000000;
color: #ffffff;
border: 1px solid #40628a;
padding: 0.4em;
box-sizing: border-box;
height: 20em;
overflow-y: scroll;
}
.message-container {
display: flex;
}
.message-input {
width: 80%;
flex-grow: 1;
}
</style>

View File

@@ -0,0 +1,41 @@
<template>
<div>
<template v-if="channelTitle != null">
<input type="text" v-model="channelTitle" @keydown.enter="new_channel()">
<vui-button @click="new_channel()">New channel</vui-button>
</template>
<vui-button v-else @click="channelTitle = ''">New channel</vui-button><br>
<view-mcomputer-chat-channel-btn v-for="ref in displayed" :key="ref" :re="ref" :ch="s.channels[ref]"/>
<h2>Users:</h2>
<vui-input-search :input="users" v-model="users_result" :keys="['user']"/><br/>
<div v-for="u in users_result" :key="u.ref"><vui-button :params="{direct: u.ref}">{{ u.user }}</vui-button></div>
<pre>{{ users_result }}</pre>
</div>
</template>
<script>
import { sendToTopic } from '@/utils'
export default {
data() {
return {
s: this.$root.$data.state,
channelTitle: null,
users_result: []
};
},
methods: {
new_channel() {
sendToTopic({new_channel: this.channelTitle})
this.channelTitle = null
}
},
computed: {
displayed() {
return Object.keys(this.s.channels).filter(key => !this.s.channels[key].can_interact)
},
users() {
return Object.entries(this.s.users).map(v => ({ref: v[0], user: v[1]}))
}
}
}
</script>

View File

@@ -0,0 +1,49 @@
<template>
<div>
<h2 v-if="!s.service" class="red">Chat service is not enabled, please enble it from main menu.</h2>
<h2 v-else-if="!s.registered" class="red">No registered user detected.</h2>
<h2 v-else-if="!s.signal" class="red">No network signal.</h2>
<template v-else>
<div>
<vui-button v-if="s.can_netadmin_mode || s.netadmin_mode" :class="{ on: s.netadmin_mode }" :params="{toggleadmin: 1}">Admin Mode</vui-button>
Ringtone:
<vui-button v-if="ringtone == null" @click="ringtone = s.ringtone">{{ s.ringtone }}</vui-button>
<template v-else>
<input type="text" v-model="ringtone" @keypress.enter="set_ringtone">
<vui-button @click="set_ringtone">Set ringtone</vui-button>
</template>
</div>
<div>
<vui-button :class="{ on: active == null }" @click="active = null">Explore</vui-button>
<vui-button v-for="ref in tab_channels" :key="ref" :class="{ on: active == ref }" @click="active = ref">{{ s.channels[ref].title }}</vui-button>
</div>
<hr>
<view-mcomputer-chat-explore v-if="active == null"/>
<view-mcomputer-chat-chat v-else :reference="active" @on-leave="active = null"/>
</template>
</div>
</template>
<script>
import { sendToTopic } from '@/utils'
export default {
data() {
return {
s: this.$root.$data.state,
active: null,
ringtone: null
};
},
computed: {
tab_channels() {
return Object.keys(this.s.channels).filter(key => this.s.channels[key].can_interact)
}
},
methods: {
set_ringtone() {
sendToTopic({ringtone: this.ringtone})
this.ringtone = null
}
}
}
</script>