From 11b83c5c2fa6004ae8e28b5b2d92374132f0fd65 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Fri, 26 Jan 2024 05:01:58 +0100 Subject: [PATCH] [MIRROR] Newspapers now use TGUI (#26243) * Newspapers now use TGUI (#80991) ## About The Pull Request Newspapers work as a static newscaster that is not affected by things like D-notices and changing wanted issues after its been printed. It doesn't store comments or get any updates after its been printed. You can also scribble on the paper to leave notes on a specific page, which is a feature I have never seen in my life but it is still here I guess. Minor things I've added: - Sound effect when printing the newspaper in the first place - 2 second do-after when scribbling just for some player feedback and consistency - Balloon alerts - Context tips for scribbling and burning I also fixed an issue with wanted issues on newscasters when there isn't an image. As a minor note, I replaced the instances of ``content`` in Buttons I saw in newscaster's UI because it's marked as deprecated. Too lazy to take a video sorry ![image](https://github.com/tgstation/tgstation/assets/53777086/6944965e-e949-4c22-a6a2-1dbe2f2d09c4) ![image](https://github.com/tgstation/tgstation/assets/53777086/5af44877-6170-424d-9766-46d1ad9f77be) ![image](https://github.com/tgstation/tgstation/assets/53777086/5c5cdfc5-541a-417e-a60d-9522227d0687) ## Why It's Good For The Game Fixes an issue with newscasters and makes newspapers use a nice TGUI that feels more responsive than before. Helps further https://hackmd.io/XLt5MoRvRxuhFbwtk4VAUA even more. ## Changelog :cl: refactor: Newspapers now use TGUI. fix: Fixed the newscaster's wanted section showing a non-existent photo. /:cl: * Newspapers now use TGUI --------- Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> --- .../newscaster/newscaster_machine.dm | 24 +- code/game/machinery/newscaster/newspaper.dm | 295 +++++++++--------- code/modules/admin/verbs/admin_newscaster.dm | 6 +- code/modules/mob/living/silicon/ai/ai.dm | 4 - code/modules/mob/mob.dm | 12 - tgui/packages/tgui/interfaces/Newscaster.jsx | 113 +++---- tgui/packages/tgui/interfaces/Newspaper.tsx | 166 ++++++++++ tgui/packages/tgui/process.ts | 17 + 8 files changed, 406 insertions(+), 231 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/Newspaper.tsx create mode 100644 tgui/packages/tgui/process.ts diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm index 99ea0d31620..e667fe5b96f 100644 --- a/code/game/machinery/newscaster/newscaster_machine.dm +++ b/code/game/machinery/newscaster/newscaster_machine.dm @@ -176,14 +176,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) data["crime_description"] = crime_description var/list/wanted_info = list() if(GLOB.news_network.wanted_issue) - if(GLOB.news_network.wanted_issue.img) + var/has_wanted_issue = !isnull(GLOB.news_network.wanted_issue.img) + if(has_wanted_issue) user << browse_rsc(GLOB.news_network.wanted_issue.img, "wanted_photo.png") wanted_info = list(list( "active" = GLOB.news_network.wanted_issue.active, "criminal" = GLOB.news_network.wanted_issue.criminal, "crime" = GLOB.news_network.wanted_issue.body, "author" = GLOB.news_network.wanted_issue.scanned_user, - "image" = "wanted_photo.png" + "image" = (has_wanted_issue ? "wanted_photo.png" : null) )) //Code breaking down the channels that have been made on-station thus far. ha @@ -431,7 +432,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) return TRUE if("printNewspaper") - print_paper() + print_paper(usr) return TRUE if("createBounty") @@ -600,22 +601,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) * This takes all current feed stories and messages, and prints them onto a newspaper, after checking that the newscaster has been loaded with paper. * The newscaster then prints the paper to the floor. */ -/obj/machinery/newscaster/proc/print_paper() +/obj/machinery/newscaster/proc/print_paper(mob/user) if(paper_remaining <= 0) balloon_alert_to_viewers("out of paper!") return TRUE SSblackbox.record_feedback("amount", "newspapers_printed", 1) - var/obj/item/newspaper/new_newspaper = new /obj/item/newspaper - for(var/datum/feed_channel/iterated_feed_channel in GLOB.news_network.network_channels) - new_newspaper.news_content += iterated_feed_channel - if(GLOB.news_network.wanted_issue.active) - new_newspaper.wantedAuthor = GLOB.news_network.wanted_issue.scanned_user - new_newspaper.wantedCriminal = GLOB.news_network.wanted_issue.criminal - new_newspaper.wantedBody = GLOB.news_network.wanted_issue.body - if(GLOB.news_network.wanted_issue.img) - new_newspaper.wantedPhoto = GLOB.news_network.wanted_issue.img - new_newspaper.forceMove(drop_location()) - new_newspaper.creation_time = GLOB.news_network.last_action + var/obj/item/newspaper/new_newspaper = new(loc) + playsound(loc, SFX_PAGE_TURN, 50, TRUE) + try_put_in_hand(new_newspaper, user) paper_remaining-- /** @@ -677,6 +670,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30) new_feed_comment.author = newscaster_username new_feed_comment.body = comment_text new_feed_comment.time_stamp = station_time_timestamp() + GLOB.news_network.last_action ++ current_message.comments += new_feed_comment usr.log_message("(as [newscaster_username]) commented on message [current_message.return_body(-1)] -- [current_message.body]", LOG_COMMENT) creating_comment = FALSE diff --git a/code/game/machinery/newscaster/newspaper.dm b/code/game/machinery/newscaster/newspaper.dm index d605d6257c3..e67910974a6 100644 --- a/code/game/machinery/newscaster/newspaper.dm +++ b/code/game/machinery/newscaster/newspaper.dm @@ -1,3 +1,8 @@ +/** + * Newspapers + * A static version of the newscaster, that won't update as new stories are added. + * Can be scribbed upon to add extra text for future readers. + */ /obj/item/newspaper name = "newspaper" desc = "An issue of The Griffon, the newspaper circulating aboard Nanotrasen Space Stations." @@ -10,167 +15,171 @@ attack_verb_continuous = list("baps") attack_verb_simple = list("bap") resistance_flags = FLAMMABLE - var/screen = 0 - var/pages = 0 - var/curr_page = 0 + + ///List of news feeed channels the newspaper can see. var/list/datum/feed_channel/news_content = list() - var/scribble="" - var/scribble_page = null - var/wantedAuthor - var/wantedCriminal - var/wantedBody - var/wantedPhoto + ///The time the newspaper was made in terms of newscaster's last action, used to tell the newspaper whether a story should be in it. var/creation_time + ///The page in the newspaper currently being read. 0 is the title screen while the last is the security screen. + var/current_page = 0 + ///The currently scribbled text written in scribble_page + var/scribble_text + ///The page with something scribbled on it, can only have one at a time. + var/scribble_page + + ///Stored information of the wanted criminal's name, if one existed at the time of creation. + var/saved_wanted_criminal + ///Stored information of the wanted criminal's description, if one existed at the time of creation. + var/saved_wanted_body + ///Stored icon of the wanted criminal, if one existed at the time of creation. + var/icon/saved_wanted_icon + +/obj/item/newspaper/Initialize(mapload) + . = ..() + register_context() + creation_time = GLOB.news_network.last_action + for(var/datum/feed_channel/iterated_feed_channel in GLOB.news_network.network_channels) + news_content += iterated_feed_channel + + if(!GLOB.news_network.wanted_issue.active) + return + saved_wanted_criminal = GLOB.news_network.wanted_issue.criminal + saved_wanted_body = GLOB.news_network.wanted_issue.body + if(GLOB.news_network.wanted_issue.img) + saved_wanted_icon = GLOB.news_network.wanted_issue.img + +/obj/item/newspaper/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + if(held_item) + if(istype(held_item, /obj/item/pen)) + context[SCREENTIP_CONTEXT_LMB] = "Scribble" + return CONTEXTUAL_SCREENTIP_SET + if(held_item.get_temperature()) + context[SCREENTIP_CONTEXT_LMB] = "Burn" + return CONTEXTUAL_SCREENTIP_SET /obj/item/newspaper/suicide_act(mob/living/user) - user.visible_message(span_suicide("[user] is focusing intently on [src]! It looks like [user.p_theyre()] trying to commit sudoku... until [user.p_their()] eyes light up with realization!")) - user.say(";JOURNALISM IS MY CALLING! EVERYBODY APPRECIATES UNBIASED REPORTI-GLORF", forced="newspaper suicide") - var/mob/living/carbon/human/H = user - var/obj/W = new /obj/item/reagent_containers/cup/glass/bottle/whiskey(H.loc) - playsound(H.loc, 'sound/items/drink.ogg', rand(10,50), TRUE) - W.reagents.trans_to(H, W.reagents.total_volume, transferred_by = user) - user.visible_message(span_suicide("[user] downs the contents of [W.name] in one gulp! Shoulda stuck to sudoku!")) + user.visible_message(span_suicide(\ + "[user] is focusing intently on [src]! It looks like [user.p_theyre()] trying to commit sudoku... \ + until [user.p_their()] eyes light up with realization!"\ + )) + user.say(";JOURNALISM IS MY CALLING! EVERYBODY APPRECIATES UNBIASED REPORTI-GLORF", forced = "newspaper suicide") + var/obj/item/reagent_containers/cup/glass/bottle/whiskey/last_drink = new(user.loc) + playsound(user, 'sound/items/drink.ogg', vol = rand(10, 50), vary = TRUE) + last_drink.reagents.trans_to(user, last_drink.reagents.total_volume, transferred_by = user) + user.visible_message(span_suicide("[user] downs the contents of [last_drink.name] in one gulp! Shoulda stuck to sudoku!")) return TOXLOSS -/obj/item/newspaper/attack_self(mob/user) - if(!istype(user) || !user.can_read(src)) +/obj/item/newspaper/attackby(obj/item/attacking_item, mob/user, params) + if(burn_paper_product_attackby_check(attacking_item, user)) + SStgui.close_uis(src) return - var/dat - pages = 0 - switch(screen) - if(0) //Cover - dat+="
The Griffon
" - dat+="
Nanotrasen-standard newspaper, for use on Nanotrasen? Space Facilities

" - if(!length(news_content)) - if(wantedAuthor) - dat+="Contents:
" - else - dat+="Other than the title, the rest of the newspaper is unprinted..." - else - dat+="Contents:
" - if(scribble_page == curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "
Next Page
Done reading
" - if(1) // X channel pages inbetween. - for(var/datum/feed_channel/NP in news_content) - pages++ - var/datum/feed_channel/C = news_content[curr_page] - dat += "[C.channel_name] \[created by: [C.return_author(notContent(C.author_censor_time))]\]

" - if(notContent(C.D_class_censor_time)) - dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. Its contents were not transferred to the newspaper at the time of printing." - else - if(!length(C.messages)) - dat+="No Feed stories stem from this channel..." - else - var/i = 0 - for(var/datum/feed_message/MESSAGE in C.messages) - if(MESSAGE.creation_time > creation_time) - if(i == 0) - dat+="No Feed stories stem from this channel..." - break - if(i == 0) - dat+="" - if(scribble_page == curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "

Previous Page
Next Page
" - if(2) //Last page - for(var/datum/feed_channel/NP in news_content) - pages++ - if(wantedAuthor != null) - dat+="
Wanted Issue:


" - dat+="Criminal name: [wantedCriminal]
" - dat+="Description: [wantedBody]
" - dat+="Photo:: " - if(wantedPhoto) - user << browse_rsc(wantedPhoto, "tmp_photow.png") - dat+="
" - else - dat+="None" - else - dat+="Apart from some uninteresting classified ads, there's nothing on this page..." - if(scribble_page == curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "
Previous Page
" - dat+="

[curr_page+1]
" - user << browse(dat, "window=newspaper_main;size=300x400") - onclose(user, "newspaper_main") -/obj/item/newspaper/proc/notContent(list/L) - if(!L.len) + if(!user.can_write(attacking_item)) + return ..() + if(scribble_page == current_page) + user.balloon_alert(user, "already scribbled!") + return + var/new_scribble_text = tgui_input_text(user, "What do you want to scribble?", "Write something") + if(isnull(new_scribble_text)) + return + add_fingerprint(user) + user.balloon_alert(user, "scribbling...") + if(!do_after(user, 2 SECONDS, src)) + return + user.balloon_alert(user, "scribbled!") + scribble_page = current_page + scribble_text = new_scribble_text + +///Checks the creation time of the newspaper and compares it to list to see if the list is meant to be censored at the time of printing. +/obj/item/newspaper/proc/censored_check(list/times_censored) + if(!times_censored.len) return FALSE - for(var/i=L.len;i>0;i--) - var/num = abs(L[i]) + for(var/i = times_censored.len; i > 0; i--) + var/num = abs(times_censored[i]) if(creation_time <= num) continue else - if(L[i] > 0) + if(times_censored[i] > 0) return TRUE else return FALSE return FALSE -/obj/item/newspaper/Topic(href, href_list) - var/mob/living/U = usr - ..() - if((src in U.contents) || (isturf(loc) && in_range(src, U))) - U.set_machine(src) - if(href_list["next_page"]) - if(curr_page == pages+1) - return //Don't need that at all, but anyway. - if(curr_page == pages) //We're at the middle, get to the end - screen = 2 - else - if(curr_page == 0) //We're at the start, get to the middle - screen=1 - curr_page++ - playsound(loc, SFX_PAGE_TURN, 50, TRUE) - else if(href_list["prev_page"]) - if(curr_page == 0) - return - if(curr_page == 1) - screen = 0 - else - if(curr_page == pages+1) //we're at the end, let's go back to the middle. - screen = 1 - curr_page-- - playsound(loc, SFX_PAGE_TURN, 50, TRUE) - if(ismob(loc)) - attack_self(loc) +/obj/item/newspaper/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Newspaper", name) + ui.open() -/obj/item/newspaper/attackby(obj/item/W, mob/living/user, params) - if(burn_paper_product_attackby_check(W, user)) +/obj/item/newspaper/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) return - if(istype(W, /obj/item/pen)) - if(!user.can_write(W)) - return - if(scribble_page == curr_page) - to_chat(user, span_warning("There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?")) + switch(action) + if("next_page") + //We're at the very end, nowhere else to go. + if(current_page == news_content.len + 1) + return TRUE + current_page++ + if("prev_page") + //We haven't started yet, nowhere else to go. + if(!current_page) + return TRUE + current_page-- else - var/s = tgui_input_text(user, "Write something", "Newspaper") - if (!s) - return - if(!user.can_perform_action(src)) - return - scribble_page = curr_page - scribble = s - attack_self(user) - add_fingerprint(user) - else - return ..() + return TRUE + SStgui.update_uis(src) + playsound(src, SFX_PAGE_TURN, 50, TRUE) + return TRUE + +/obj/item/newspaper/ui_static_data(mob/user) + var/list/data = list() + data["channels"] = list() + for(var/datum/feed_channel/news_channels as anything in news_content) + data["channels"] += list(list( + "name" = news_channels.channel_name, + "page_number" = news_content.Find(news_channels), + )) + return data + +/obj/item/newspaper/ui_data(mob/user) + var/list/data = list() + data["current_page"] = current_page + data["scribble_message"] = (scribble_page == current_page) ? scribble_text : null + if(saved_wanted_icon) + user << browse_rsc(saved_wanted_icon, "wanted_photo.png") + data["wanted_criminal"] = saved_wanted_criminal + data["wanted_body"] = saved_wanted_body + data["wanted_photo"] = (saved_wanted_icon ? "wanted_photo.png" : null) + + var/list/channel_data = list() + if(!current_page || (current_page == news_content.len + 1)) + channel_data["channel_name"] = null + channel_data["author_name"] = null + channel_data["is_censored"] = null + channel_data["channel_messages"] = list() + data["channel_data"] = list(channel_data) + return data + var/datum/feed_channel/current_channel = news_content[current_page] + if(istype(current_channel)) + channel_data["channel_name"] = current_channel.channel_name + channel_data["author_name"] = current_channel.return_author(censored_check(current_channel.author_censor_time)) + channel_data["is_censored"] = censored_check(current_channel.D_class_censor_time) + channel_data["channel_messages"] = list() + for(var/datum/feed_message/feed_messages as anything in current_channel.messages) + if(feed_messages.creation_time > creation_time) + data["channel_has_messages"] = FALSE + break + data["channel_has_messages"] = TRUE + var/has_image = FALSE + if(feed_messages.img) + has_image = TRUE + user << browse_rsc(feed_messages.img, "tmp_photo[feed_messages.message_ID].png") + channel_data["channel_messages"] += list(list( + "message" = "-[feed_messages.return_body(censored_check(feed_messages.body_censor_time))]", + "photo" = (has_image ? "tmp_photo[feed_messages.message_ID].png" : null), + "author" = feed_messages.return_author(censored_check(feed_messages.author_censor_time)), + )) + data["channel_data"] = list(channel_data) + return data diff --git a/code/modules/admin/verbs/admin_newscaster.dm b/code/modules/admin/verbs/admin_newscaster.dm index 0a25ebaca65..2f6870394c2 100644 --- a/code/modules/admin/verbs/admin_newscaster.dm +++ b/code/modules/admin/verbs/admin_newscaster.dm @@ -78,14 +78,15 @@ data["crime_description"] = crime_description var/list/wanted_info = list() if(GLOB.news_network.wanted_issue) - if(GLOB.news_network.wanted_issue.img) + var/has_wanted_issue = !isnull(GLOB.news_network.wanted_issue.img) + if(has_wanted_issue) user << browse_rsc(GLOB.news_network.wanted_issue.img, "wanted_photo.png") wanted_info = list(list( "active" = GLOB.news_network.wanted_issue.active, "criminal" = GLOB.news_network.wanted_issue.criminal, "crime" = GLOB.news_network.wanted_issue.body, "author" = GLOB.news_network.wanted_issue.scanned_user, - "image" = "wanted_photo.png" + "image" = (has_wanted_issue ? "wanted_photo.png" : null) )) //Code breaking down the channels that have been made on-station thus far. ha @@ -320,6 +321,7 @@ new_feed_comment.body = comment_text new_feed_comment.time_stamp = station_time_timestamp() current_message.comments += new_feed_comment + GLOB.news_network.last_action ++ usr.log_message("(as an admin) commented on message [current_message.return_body(-1)] -- [current_message.body]", LOG_COMMENT) creating_comment = FALSE diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 052d10e494f..096afab907d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -496,10 +496,6 @@ if(incapacitated()) return - if (href_list["mach_close"]) - var/t1 = "window=[href_list["mach_close"]]" - unset_machine() - src << browse(null, t1) if (href_list["switchcamera"]) switchCamera(locate(href_list["switchcamera"]) in GLOB.cameranet.cameras) if (href_list["showalerts"]) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 817e9ce9474..b9a82194609 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -916,18 +916,6 @@ set hidden = TRUE set category = null return -/** - * Topic call back for any mob - * - * * Unset machines if "mach_close" sent - * * refresh the inventory of machines in range if "refresh" sent - * * handles the strip panel equip and unequip as well if "item" sent - */ -/mob/Topic(href, href_list) - if(href_list["mach_close"]) - var/t1 = "window=[href_list["mach_close"]]" - unset_machine() - src << browse(null, t1) /** * Controls if a mouse drop succeeds (return null if it doesnt) diff --git a/tgui/packages/tgui/interfaces/Newscaster.jsx b/tgui/packages/tgui/interfaces/Newscaster.jsx index a15fc8ef9ba..431e7d606e6 100644 --- a/tgui/packages/tgui/interfaces/Newscaster.jsx +++ b/tgui/packages/tgui/interfaces/Newscaster.jsx @@ -6,7 +6,6 @@ */ import { decodeHtmlEntities } from 'common/string'; -import { marked } from 'marked'; import { useState } from 'react'; import { useBackend, useSharedState } from '../backend'; @@ -23,7 +22,7 @@ import { Tabs, TextArea, } from '../components'; -import { sanitizeText } from '../sanitize'; +import { processedText } from '../process'; import { BountyBoardContent } from './BountyBoard'; import { UserDetails } from './Vending'; @@ -90,8 +89,8 @@ const NewscasterChannelCreation = (props) => { Enter channel name here: @@ -181,9 +183,9 @@ const NewscasterCommentCreation = (props) => { Enter comment: @@ -236,7 +239,7 @@ const NewscasterWantedScreen = (props) => { return null; } return ( - + {wanted.map((activeWanted) => ( <> @@ -246,22 +249,27 @@ const NewscasterWantedScreen = (props) => { ? 'Active Wanted Issue:' : 'Dismissed Wanted Issue:'}
@@ -293,22 +303,25 @@ const NewscasterWantedScreen = (props) => { icon="camera" selected={photo_data} disabled={!security_mode} - content={photo_data ? 'Remove photo' : 'Attach photo'} onClick={() => act('togglePhoto')} - /> + > + {photo_data ? 'Remove photo' : 'Attach photo'} +
) : ( @@ -392,28 +405,29 @@ const NewscasterChannelBox = (props) => { {!!admin_mode && ( )}
@@ -493,20 +510,6 @@ const NewscasterChannelSelector = (props) => { ); }; -const processedText = (value) => { - const textHtml = { - __html: sanitizeText( - marked(value, { - breaks: true, - smartypants: true, - smartLists: true, - baseUrl: 'thisshouldbreakhttp', - }), - ), - }; - return textHtml; -}; - /** This is where the channels comments get spangled out (tm) */ const NewscasterChannelMessages = (props) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/Newspaper.tsx b/tgui/packages/tgui/interfaces/Newspaper.tsx new file mode 100644 index 00000000000..2629a398c95 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Newspaper.tsx @@ -0,0 +1,166 @@ +import { BooleanLike } from '../../common/react'; +import { useBackend } from '../backend'; +import { Box, Button, Divider, Image, Section } from '../components'; +import { Window } from '../layouts'; +import { processedText } from '../process'; + +type Data = { + current_page: number; + scribble_message: string | null; + channel_has_messages: BooleanLike; + channels: ChannelNames[]; + channel_data: ChannelData[]; + wanted_criminal: string | null; + wanted_body: string | null; + wanted_photo: string | null; +}; + +type ChannelNames = { + name: string | null; + page_number: number; +}; + +type ChannelData = { + channel_name: string | null; + author_name: string | null; + is_censored: BooleanLike; + channel_messages: ChannelMessages[]; +}; + +type ChannelMessages = { + message: string | null; + photo: string | null; + author: string | null; +}; + +export const Newspaper = (props) => { + const { act, data } = useBackend(); + const { channels = [], current_page, scribble_message } = data; + + return ( + + + {current_page === channels.length + 1 ? ( + + ) : current_page ? ( + + ) : ( + + )} + {!!scribble_message && ( + + {scribble_message} + + )} +
+ + +
+
+
+ ); +}; + +const NewspaperIntro = (props) => { + const { act, data } = useBackend(); + const { channels = [], wanted_criminal = [] } = data; + + return ( +
+ + The Griffon + + + For use on Space Facilities only! + + Table of Contents: + {channels.map((channel) => ( + + Page {channel.page_number || 0}: {channel.name} + + ))} + {!!wanted_criminal && ( + Last Page: Important Security Announcement + )} +
+ ); +}; + +const NewspaperChannel = (props) => { + const { act, data } = useBackend(); + const { channel_has_messages, channel_data = [] } = data; + + return ( +
+ {channel_data.map((individual_channel) => ( + + + {individual_channel.channel_name} + + + Channel made by: {individual_channel.author_name} + + {channel_has_messages ? ( + <> + {individual_channel.channel_messages.map((message) => ( + <> + + + {!!message.photo && } + Written by: {message.author} + + + + ))} + + ) : ( + 'No feed stories stem from this channel...' + )} + + ))} +
+ ); +}; + +const NewspaperEnding = (props) => { + const { act, data } = useBackend(); + const { wanted_criminal, wanted_body, wanted_photo } = data; + + return ( +
+ {wanted_criminal ? ( + <> + + Wanted Issue + + Criminal Name: {wanted_criminal} + Description: {wanted_body} + {!!wanted_photo && } + + ) : ( + 'Apart from some uninteresting classified ads, theres nothing in this page...' + )} +
+ ); +}; diff --git a/tgui/packages/tgui/process.ts b/tgui/packages/tgui/process.ts new file mode 100644 index 00000000000..62b992d16fb --- /dev/null +++ b/tgui/packages/tgui/process.ts @@ -0,0 +1,17 @@ +import { marked } from 'marked'; + +import { sanitizeText } from './sanitize'; + +export const processedText = (value) => { + const textHtml = { + __html: sanitizeText( + marked(value, { + breaks: true, + smartypants: true, + smartLists: true, + baseUrl: 'thisshouldbreakhttp', + }), + ), + }; + return textHtml; +};