Address AA

This commit is contained in:
mochi
2020-10-04 20:54:47 +02:00
parent 8a9b3c5e74
commit 444a7bfe92
10 changed files with 429 additions and 444 deletions
+4 -8
View File
@@ -119,12 +119,7 @@ GLOBAL_LIST_EMPTY(announced_news_types)
/proc/announce_newscaster_news(datum/news_announcement/news)
var/datum/feed_channel/sendto
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
if(FC.channel_name == news.channel_name)
sendto = FC
break
var/datum/feed_channel/sendto = GLOB.news_network.get_channel_by_name(news.channel_name)
if(!sendto)
sendto = new /datum/feed_channel
sendto.channel_name = news.channel_name
@@ -140,5 +135,6 @@ GLOBAL_LIST_EMPTY(announced_news_types)
sendto.add_message(newMsg)
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
NEWSCASTER.alert_news(news.channel_name)
for(var/nc in GLOB.allNewscasters)
var/obj/machinery/newscaster/NC = nc
NC.alert_news(news.channel_name)
+4 -6
View File
@@ -94,12 +94,10 @@
if(FESTIVAL)
newMsg.body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter")]","coming of age of their [pick("son","daughter")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet."
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
if(FC.channel_name == "Nyx Daily")
FC.add_message(newMsg)
break
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
NEWSCASTER.alert_news("Nyx Daily")
GLOB.news_network.get_channel_by_name("Nyx Daily")?.add_message(newMsg)
for(var/nc in GLOB.allNewscasters)
var/obj/machinery/newscaster/NC = nc
NC.alert_news("Nyx Daily")
/datum/event/economic_event/end()
for(var/good_type in dearer_goods)
+8 -12
View File
@@ -126,12 +126,10 @@
Nyx Daily is offering discount tickets for two to see [random_name(pick(MALE,FEMALE))] live in return for eyewitness reports and up to the minute coverage."
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
if(FC.channel_name == "Nyx Daily")
FC.add_message(newMsg)
break
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
NEWSCASTER.alert_news("Nyx Daily")
GLOB.news_network.get_channel_by_name("Nyx Daily")?.add_message(newMsg)
for(var/nc in GLOB.allNewscasters)
var/obj/machinery/newscaster/NC = nc
NC.alert_news("Nyx Daily")
/datum/event/trivial_news
endWhen = 10
@@ -145,9 +143,7 @@
newMsg.body = pick(file2list("config/news/trivial.txt"))
newMsg.body = replacetext(newMsg.body,"{{AFFECTED}}",affected_dest.name)
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
if(FC.channel_name == "The Gibson Gazette")
FC.add_message(newMsg)
break
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
NEWSCASTER.alert_news("The Gibson Gazette")
GLOB.news_network.get_channel_by_name("The Gibson Gazette")?.add_message(newMsg)
for(var/nc in GLOB.allNewscasters)
var/obj/machinery/newscaster/NC = nc
NC.alert_news("The Gibson Gazette")
+4 -7
View File
@@ -21,10 +21,7 @@
if(!deposit_success)
newMsg.body += "<br>Unfortunately, we were unable to verify the account details provided, so we were unable to transfer the money. Send a cheque containing the sum of $500 to ND 'Stellar Slam' office on the Nyx gateway containing updated details, and your winnings'll be re-sent within the month."
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
if(FC.channel_name == "Nyx Daily")
FC.add_message(newMsg)
break
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
NEWSCASTER.alert_news("Nyx Daily")
GLOB.news_network.get_channel_by_name("Nyx Daily")?.add_message(newMsg)
for(var/nc in GLOB.allNewscasters)
var/obj/machinery/newscaster/NC = nc
NC.alert_news("Nyx Daily")
+3
View File
@@ -15,6 +15,7 @@
* * name - The name
*/
/datum/feed_network/proc/get_channel_by_name(name)
RETURN_TYPE(/datum/feed_channel)
for(var/fc in channels)
var/datum/feed_channel/FC = fc
if(FC.channel_name == name)
@@ -27,6 +28,7 @@
* * author - The author
*/
/datum/feed_network/proc/get_channel_by_author(author)
RETURN_TYPE(/datum/feed_channel)
for(var/fc in channels)
var/datum/feed_channel/FC = fc
if(FC.author == author)
@@ -39,6 +41,7 @@
* * idx - The index
*/
/datum/feed_network/proc/get_channel_by_idx(idx)
RETURN_TYPE(/datum/feed_channel)
if(!ISINDEXSAFE(channels, idx))
return
return channels[idx]
+2 -8
View File
@@ -513,14 +513,8 @@
return
// Find the named channel the user is trying to publish a story to
var/user_name = get_scanned_user(usr)["name"]
var/datum/feed_channel/FC
for(var/fc in GLOB.news_network.channels)
var/datum/feed_channel/_FC = fc
if(_FC.channel_name == channel)
if(_FC.can_publish(usr, user_name))
FC = _FC
break
if(!FC)
var/datum/feed_channel/FC = GLOB.news_network.get_channel_by_name(channel)
if(!FC || !FC.can_publish(usr, user_name))
return
var/datum/feed_message/FM = new
FM.author = usr.can_admin_interact() ? author : scanned_user
+2 -1
View File
@@ -117,7 +117,8 @@
to_chat(user, "<span class='warning'>The paper is full of unintelligible symbols!</span>")
/obj/item/newspaper/Topic(href, href_list)
..()
if(..())
return
var/mob/living/M = usr
if(!Adjacent(M))
return
+24
View File
@@ -239,3 +239,27 @@ export const getGasColor = gasId => {
|| gas.name.toLowerCase() === gasSearchString);
return gas && gas.color;
};
export const timeAgo = (ref_time, now_time) => {
if (ref_time > now_time) {
return "in the future";
}
// deciseconds -> seconds
ref_time = ref_time / 10;
now_time = now_time / 10;
const diff = now_time - ref_time;
if (diff > 3600) {
const hours = Math.round(diff / 3600);
return hours + " hour" + (hours === 1 ? "" : "s") + " ago";
} else if (diff > 60) {
const mins = Math.round(diff / 60);
return mins + " minute" + (mins === 1 ? "" : "s") + " ago";
} else {
const secs = Math.round(diff);
return secs + " second" + (secs === 1 ? "" : "s") + " ago";
}
return "just now";
};
+376 -400
View File
@@ -2,6 +2,7 @@ import { classes } from 'common/react';
import { Fragment } from 'inferno';
import { deleteLocalState, useBackend, useLocalState } from "../backend";
import { Box, Button, Divider, Dropdown, Flex, Icon, Input, LabeledList, Modal, Section } from '../components';
import { timeAgo } from '../constants';
import { Window } from '../layouts';
import { ComplexModal, modalAnswer, modalClose, modalOpen, modalRegisterBodyOverride } from './common/ComplexModal';
import { TemporaryNotice } from './common/TemporaryNotice';
@@ -36,406 +37,6 @@ const jobOpeningCategories = {
},
};
// This handles both creation and editing
const manageChannelModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
// Additional data
const channel = !!modal.args.uid && data.channels.filter(c => c.uid === modal.args.uid).pop();
if (modal.id === "manage_channel" && !channel) {
modalClose(context); // ?
return;
}
const isEditing = modal.id === "manage_channel";
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
// Temp data
const [author, setAuthor] = useLocalState(context, "author", channel?.author || scannedUser || "Unknown");
const [name, setName] = useLocalState(context, "name", channel?.name || "");
const [description, setDescription] = useLocalState(context, "description", channel?.description || "");
const [icon, setIcon] = useLocalState(context, "icon", channel?.icon || "newspaper");
const [isPublic, setIsPublic] = useLocalState(context, "isPublic", isEditing ? !!(channel?.public) : false);
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (channel?.admin === 1) || false);
return (
<Section
level="2"
m="-1rem"
pb="1rem"
title={isEditing ? ("Manage " + channel.name) : "Create New Channel"}>
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Owner">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Name">
<Input
width="100%"
placeholder="50 characters max."
maxLength="50"
value={name}
onInput={(_e, v) => setName(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Description (optional)" verticalAlign="top">
<Input
multiline
width="100%"
placeholder="128 characters max."
maxLength="128"
value={description}
onInput={(_e, v) => setDescription(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Icon">
<Input
disabled={!isAdmin}
value={icon}
width="35%"
mr="0.5rem"
onInput={(_e, v) => setIcon(v)}
/>
<Icon name={icon} size="2" verticalAlign="middle" mr="0.5rem" />
</LabeledList.Item>
<LabeledList.Item label="Accept Public Stories?">
<Button
selected={isPublic}
icon={isPublic ? "toggle-on" : "toggle-off"}
content={isPublic ? "Yes" : "No"}
onClick={() => setIsPublic(!isPublic)}
/>
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this channel will make it editable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={author.trim().length === 0 || name.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, modal.id, "", {
author: author,
name: name.substr(0, 49),
description: description.substr(0, 128),
icon: icon,
public: isPublic ? 1 : 0,
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "name", "description", "icon", "public");
}}
/>
</Section>
);
};
const createStoryModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const {
photo,
channels,
channel_idx = -1,
} = data;
// Additional data
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
let availableChannels = channels.slice()
.sort((a, b) => {
if (channel_idx < 0) {
return 0;
}
const selected = channels[channel_idx - 1];
if (selected.uid === a.uid) {
return -1;
} else if (selected.uid === b.uid) {
return 1;
}
})
.filter(c => isAdmin || (!c.frozen && (c.author === scannedUser || !!c.public)));
// Temp data
const [author, setAuthor] = useLocalState(context, "author", scannedUser || "Unknown");
const [channel, setChannel] = useLocalState(context, "channel", availableChannels.length > 0 ? availableChannels[0].name : "");
const [title, setTitle] = useLocalState(context, "title", "");
const [body, setBody] = useLocalState(context, "body", "");
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", false);
return (
<Section
level={2}
m="-1rem"
pb="1rem"
title="Create New Story">
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Author">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Channel" verticalAlign="top">
<Dropdown
selected={channel}
options={availableChannels.map(c => c.name)}
mb="0"
width="100%"
onSelected={c => setChannel(c)}
/>
</LabeledList.Item>
<LabeledList.Divider />
<LabeledList.Item label="Title">
<Input
width="100%"
placeholder="128 characters max."
maxLength="128"
value={title}
onInput={(_e, v) => setTitle(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Story Text" verticalAlign="top">
<Input
fluid
multiline
placeholder="1024 characters max."
maxLength="1024"
rows="8"
width="100%"
value={body}
onInput={(_e, v) => setBody(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
<Button
icon="image"
selected={photo}
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
tooltip={!photo && "Attach a photo to this story by holding the photograph in your hand."}
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
/>
</LabeledList.Item>
<LabeledList.Item label="Preview" verticalAlign="top">
<Section
noTopPadding
title={title}
maxHeight="13.5rem"
overflow="auto">
<Box mt="0.5rem">
{!!photo && (
<PhotoThumbnail
name={"inserted_photo_" + photo.uid + ".png"}
float="right"
/>
)}
{body.split("\n").map(p => (
<Box key={p}>
{p || <br />}
</Box>
))}
<Box clear="right" />
</Box>
</Section>
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this story will make it censorable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={author.trim().length === 0
|| channel.trim().length === 0
|| title.trim().length === 0
|| body.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, "create_story", "", {
author: author,
channel: channel,
title: title.substr(0, 127),
body: body.substr(0, 1023),
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "channel", "title", "body");
}}
/>
</Section>
);
};
const wantedNoticeModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const {
photo,
wanted,
} = data;
// Additional data
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
// Temp data
const [author, setAuthor] = useLocalState(context, "author", wanted?.author || scannedUser || "Unknown");
const [name, setName] = useLocalState(context, "name", wanted?.title.substr(8) || "");
const [description, setDescription] = useLocalState(context, "description", wanted?.body || "");
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (wanted?.admin_locked === 1) || false);
return (
<Section
level="2"
m="-1rem"
pb="1rem"
title="Manage Wanted Notice">
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Authority">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Name">
<Input
width="100%"
value={name}
maxLength="128"
onInput={(_e, v) => setName(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Description" verticalAlign="top">
<Input
multiline
width="100%"
value={description}
maxLength="512"
rows="4"
onInput={(_e, v) => setDescription(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
<Button
icon="image"
selected={photo}
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
tooltip={!photo && "Attach a photo to this wanted notice by holding the photograph in your hand."}
tooltipPosition="top"
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
/>
{!!photo && (
<PhotoThumbnail
name={"inserted_photo_" + photo.uid + ".png"}
float="right"
/>
)}
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this wanted notice will make it editable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={!wanted}
icon="eraser"
color="danger"
content="Clear"
position="absolute"
right="7.25rem"
bottom="-0.75rem"
onClick={() => {
act("clear_wanted_notice");
modalClose(context);
// Clean up
deleteLocalState(context, "author", "name", "description", "admin_locked");
}}
/>
<Button.Confirm
disabled={author.trim().length === 0 || name.trim().length === 0 || description.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, modal.id, "", {
author: author,
name: name.substr(0, 127),
description: description.substr(0, 511),
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "name", "description", "admin_locked");
}}
/>
</Section>
);
};
// TODO: make this common
const timeAgo = (ref_time, now_time) => {
if (ref_time > now_time) {
return "in the future";
}
// deciseconds -> seconds
ref_time = ref_time / 10;
now_time = now_time / 10;
const diff = now_time - ref_time;
if (diff > 3600) {
const hours = Math.round(diff / 3600);
return hours + " hour" + (hours === 1 ? "" : "s") + " ago";
} else if (diff > 60) {
const mins = Math.round(diff / 60);
return mins + " minute" + (mins === 1 ? "" : "s") + " ago";
} else {
const secs = Math.round(diff);
return secs + " second" + (secs === 1 ? "" : "s") + " ago";
}
return "just now";
};
export const Newscaster = (properties, context) => {
const { act, data } = useBackend(context);
const {
@@ -892,6 +493,381 @@ const PhotoZoom = (properties, context) => {
);
};
// This handles both creation and editing
const manageChannelModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
// Additional data
const channel = !!modal.args.uid && data.channels.filter(c => c.uid === modal.args.uid).pop();
if (modal.id === "manage_channel" && !channel) {
modalClose(context); // ?
return;
}
const isEditing = modal.id === "manage_channel";
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
// Temp data
const [author, setAuthor] = useLocalState(context, "author", channel?.author || scannedUser || "Unknown");
const [name, setName] = useLocalState(context, "name", channel?.name || "");
const [description, setDescription] = useLocalState(context, "description", channel?.description || "");
const [icon, setIcon] = useLocalState(context, "icon", channel?.icon || "newspaper");
const [isPublic, setIsPublic] = useLocalState(context, "isPublic", isEditing ? !!(channel?.public) : false);
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (channel?.admin === 1) || false);
return (
<Section
level="2"
m="-1rem"
pb="1rem"
title={isEditing ? ("Manage " + channel.name) : "Create New Channel"}>
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Owner">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Name">
<Input
width="100%"
placeholder="50 characters max."
maxLength="50"
value={name}
onInput={(_e, v) => setName(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Description (optional)" verticalAlign="top">
<Input
multiline
width="100%"
placeholder="128 characters max."
maxLength="128"
value={description}
onInput={(_e, v) => setDescription(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Icon">
<Input
disabled={!isAdmin}
value={icon}
width="35%"
mr="0.5rem"
onInput={(_e, v) => setIcon(v)}
/>
<Icon name={icon} size="2" verticalAlign="middle" mr="0.5rem" />
</LabeledList.Item>
<LabeledList.Item label="Accept Public Stories?">
<Button
selected={isPublic}
icon={isPublic ? "toggle-on" : "toggle-off"}
content={isPublic ? "Yes" : "No"}
onClick={() => setIsPublic(!isPublic)}
/>
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this channel will make it editable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={author.trim().length === 0 || name.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, modal.id, "", {
author: author,
name: name.substr(0, 49),
description: description.substr(0, 128),
icon: icon,
public: isPublic ? 1 : 0,
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "name", "description", "icon", "public");
}}
/>
</Section>
);
};
const createStoryModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const {
photo,
channels,
channel_idx = -1,
} = data;
// Additional data
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
let availableChannels = channels.slice()
.sort((a, b) => {
if (channel_idx < 0) {
return 0;
}
const selected = channels[channel_idx - 1];
if (selected.uid === a.uid) {
return -1;
} else if (selected.uid === b.uid) {
return 1;
}
})
.filter(c => isAdmin || (!c.frozen && (c.author === scannedUser || !!c.public)));
// Temp data
const [author, setAuthor] = useLocalState(context, "author", scannedUser || "Unknown");
const [channel, setChannel] = useLocalState(context, "channel", availableChannels.length > 0 ? availableChannels[0].name : "");
const [title, setTitle] = useLocalState(context, "title", "");
const [body, setBody] = useLocalState(context, "body", "");
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", false);
return (
<Section
level={2}
m="-1rem"
pb="1rem"
title="Create New Story">
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Author">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Channel" verticalAlign="top">
<Dropdown
selected={channel}
options={availableChannels.map(c => c.name)}
mb="0"
width="100%"
onSelected={c => setChannel(c)}
/>
</LabeledList.Item>
<LabeledList.Divider />
<LabeledList.Item label="Title">
<Input
width="100%"
placeholder="128 characters max."
maxLength="128"
value={title}
onInput={(_e, v) => setTitle(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Story Text" verticalAlign="top">
<Input
fluid
multiline
placeholder="1024 characters max."
maxLength="1024"
rows="8"
width="100%"
value={body}
onInput={(_e, v) => setBody(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
<Button
icon="image"
selected={photo}
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
tooltip={!photo && "Attach a photo to this story by holding the photograph in your hand."}
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
/>
</LabeledList.Item>
<LabeledList.Item label="Preview" verticalAlign="top">
<Section
noTopPadding
title={title}
maxHeight="13.5rem"
overflow="auto">
<Box mt="0.5rem">
{!!photo && (
<PhotoThumbnail
name={"inserted_photo_" + photo.uid + ".png"}
float="right"
/>
)}
{body.split("\n").map(p => (
<Box key={p}>
{p || <br />}
</Box>
))}
<Box clear="right" />
</Box>
</Section>
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this story will make it censorable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={author.trim().length === 0
|| channel.trim().length === 0
|| title.trim().length === 0
|| body.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, "create_story", "", {
author: author,
channel: channel,
title: title.substr(0, 127),
body: body.substr(0, 1023),
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "channel", "title", "body");
}}
/>
</Section>
);
};
const wantedNoticeModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const {
photo,
wanted,
} = data;
// Additional data
const isAdmin = !!modal.args.is_admin;
const scannedUser = modal.args.scanned_user;
// Temp data
const [author, setAuthor] = useLocalState(context, "author", wanted?.author || scannedUser || "Unknown");
const [name, setName] = useLocalState(context, "name", wanted?.title.substr(8) || "");
const [description, setDescription] = useLocalState(context, "description", wanted?.body || "");
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (wanted?.admin_locked === 1) || false);
return (
<Section
level="2"
m="-1rem"
pb="1rem"
title="Manage Wanted Notice">
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Authority">
<Input
disabled={!isAdmin}
width="100%"
value={author}
onInput={(_e, v) => setAuthor(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Name">
<Input
width="100%"
value={name}
maxLength="128"
onInput={(_e, v) => setName(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Description" verticalAlign="top">
<Input
multiline
width="100%"
value={description}
maxLength="512"
rows="4"
onInput={(_e, v) => setDescription(v)}
/>
</LabeledList.Item>
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
<Button
icon="image"
selected={photo}
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
tooltip={!photo && "Attach a photo to this wanted notice by holding the photograph in your hand."}
tooltipPosition="top"
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
/>
{!!photo && (
<PhotoThumbnail
name={"inserted_photo_" + photo.uid + ".png"}
float="right"
/>
)}
</LabeledList.Item>
{isAdmin && (
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
<Button
selected={adminLocked}
icon={adminLocked ? "lock" : "lock-open"}
content={adminLocked ? "On" : "Off"}
tooltip="Locking this wanted notice will make it editable by nobody but CentComm officers."
tooltipPosition="top"
onClick={() => setAdminLocked(!adminLocked)}
/>
</LabeledList.Item>
)}
</LabeledList>
</Box>
<Button.Confirm
disabled={!wanted}
icon="eraser"
color="danger"
content="Clear"
position="absolute"
right="7.25rem"
bottom="-0.75rem"
onClick={() => {
act("clear_wanted_notice");
modalClose(context);
// Clean up
deleteLocalState(context, "author", "name", "description", "admin_locked");
}}
/>
<Button.Confirm
disabled={author.trim().length === 0 || name.trim().length === 0 || description.trim().length === 0}
icon="check"
color="good"
content="Submit"
position="absolute"
right="1rem"
bottom="-0.75rem"
onClick={() => {
modalAnswer(context, modal.id, "", {
author: author,
name: name.substr(0, 127),
description: description.substr(0, 511),
admin_locked: adminLocked ? 1 : 0,
});
// Clean up
deleteLocalState(context, "author", "name", "description", "admin_locked");
}}
/>
</Section>
);
};
modalRegisterBodyOverride("create_channel", manageChannelModalBodyOverride);
modalRegisterBodyOverride("manage_channel", manageChannelModalBodyOverride);
modalRegisterBodyOverride("create_story", createStoryModalBodyOverride);
File diff suppressed because one or more lines are too long