mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-16 21:23:20 +00:00
488 lines
15 KiB
JavaScript
488 lines
15 KiB
JavaScript
import { decodeHtmlEntities } from 'common/string';
|
|
import { Fragment } from 'inferno';
|
|
import { useBackend, useSharedState } from '../backend';
|
|
import { Box, Button, LabeledList, Input, Section } from '../components';
|
|
import { Window } from '../layouts';
|
|
import { TemporaryNotice } from './common/TemporaryNotice';
|
|
|
|
const NEWSCASTER_SCREEN_MAIN = 'Main Menu';
|
|
const NEWSCASTER_SCREEN_NEWCHANNEL = 'New Channel';
|
|
const NEWSCASTER_SCREEN_VIEWLIST = 'View List';
|
|
const NEWSCASTER_SCREEN_NEWSTORY = 'New Story';
|
|
const NEWSCASTER_SCREEN_PRINT = 'Print';
|
|
const NEWSCASTER_SCREEN_NEWWANTED = 'New Wanted';
|
|
const NEWSCASTER_SCREEN_VIEWWANTED = 'View Wanted';
|
|
const NEWSCASTER_SCREEN_SELECTEDCHANNEL = 'View Selected Channel';
|
|
|
|
export const Newscaster = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { screen, user } = data;
|
|
|
|
return (
|
|
<Window width={600} height={600} resizable>
|
|
<Window.Content scrollable>
|
|
<TemporaryNotice decode />
|
|
<NewscasterContent />
|
|
</Window.Content>
|
|
</Window>
|
|
);
|
|
};
|
|
|
|
const NewscasterContent = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { user } = data;
|
|
|
|
const [screen, setScreen] = useSharedState(context, 'screen', NEWSCASTER_SCREEN_MAIN);
|
|
let Template = screenToTemplate[screen];
|
|
|
|
return (
|
|
<Box>
|
|
<Template setScreen={setScreen} />
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const NewscasterMainMenu = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { securityCaster, wanted_issue } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
return (
|
|
<Fragment>
|
|
<Section title="Main Menu">
|
|
{wanted_issue && (
|
|
<Button fluid icon="eye" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWWANTED)} color="bad">
|
|
Read WANTED Issue
|
|
</Button>
|
|
)}
|
|
<Button fluid icon="eye" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
|
|
View Feed Channels
|
|
</Button>
|
|
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWCHANNEL)}>
|
|
Create Feed Channel
|
|
</Button>
|
|
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWSTORY)}>
|
|
Create Feed Message
|
|
</Button>
|
|
<Button fluid icon="print" onClick={() => setScreen(NEWSCASTER_SCREEN_PRINT)}>
|
|
Print Newspaper
|
|
</Button>
|
|
</Section>
|
|
{!!securityCaster && (
|
|
<Section title="Feed Security Functions">
|
|
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWWANTED)}>
|
|
Manage "Wanted" Issue
|
|
</Button>
|
|
</Section>
|
|
)}
|
|
</Fragment>
|
|
);
|
|
};
|
|
|
|
const NewscasterNewChannel = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { channel_name, c_locked, user } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
return (
|
|
<Section
|
|
title="Creating new Feed Channel"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Channel Name">
|
|
<Input
|
|
fluid
|
|
value={decodeHtmlEntities(channel_name)}
|
|
onInput={(e, val) => act('set_channel_name', { val: val })}
|
|
/>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Channel Author" color="good">
|
|
{user}
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Accept Public Feeds">
|
|
<Button icon={c_locked ? 'lock' : 'lock-open'} selected={!c_locked} onClick={() => act('set_channel_lock')}>
|
|
{c_locked ? 'No' : 'Yes'}
|
|
</Button>
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Button fluid color="good" icon="plus" onClick={() => act('submit_new_channel')}>
|
|
Submit Channel
|
|
</Button>
|
|
<Button fluid color="bad" icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Cancel
|
|
</Button>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterViewList = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { channels } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
return (
|
|
<Section
|
|
title="Station Feed Channels"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
{channels.map((channel) => (
|
|
<Button
|
|
fluid
|
|
key={channel.name}
|
|
icon="eye"
|
|
color={channel.admin ? 'good' : channel.censored ? 'bad' : ''}
|
|
onClick={() => {
|
|
act('show_channel', { show_channel: channel.ref });
|
|
setScreen(NEWSCASTER_SCREEN_SELECTEDCHANNEL);
|
|
}}>
|
|
{decodeHtmlEntities(channel.name)}
|
|
</Button>
|
|
))}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterNewStory = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { channel_name, user, title, msg, photo_data } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
const label_style = { 'white-space': 'nowrap' };
|
|
|
|
const break_style = { width: '100%', 'word-break': 'break-all', 'word-wrap': 'break-word' };
|
|
|
|
return (
|
|
<Section
|
|
title="Creating new Feed Message..."
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
<table
|
|
style={{
|
|
width: 'calc(100% + 0.5em)',
|
|
margin: '-0.25em -0.25em 0 -0.25em',
|
|
padding: 0,
|
|
}}>
|
|
<tr>
|
|
<td style={label_style}>Receiving Channel:</td>
|
|
<td colspan={2}>
|
|
<Button fluid onClick={() => act('set_channel_receiving')}>
|
|
{channel_name || 'Unset'}
|
|
</Button>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style={label_style}>Message Author:</td>
|
|
<td className="color-good" colspan={2}>
|
|
{user}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style={label_style}>Message Title:</td>
|
|
<td style={break_style}>{title || '(no title yet)'}</td>
|
|
<td>
|
|
<Button
|
|
verticalAlign="top"
|
|
onClick={() => act('set_new_title')}
|
|
icon="pen"
|
|
tooltip="Edit Title"
|
|
tooltipPosition="left"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style={label_style}>Message Body:</td>
|
|
<td style={break_style}>{msg || '(no message yet)'}</td>
|
|
<td>
|
|
<Button
|
|
verticalAlign="top"
|
|
onClick={() => act('set_new_message')}
|
|
icon="pen"
|
|
tooltip="Edit Message"
|
|
tooltipPosition="left"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style={label_style}>Attach Photo:</td>
|
|
<td colspan={2}>
|
|
<Button fluid icon="image" onClick={() => act('set_attachment')}>
|
|
{photo_data ? 'Photo Attached' : 'No Photo'}
|
|
</Button>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<Button fluid color="good" icon="plus" onClick={() => act('submit_new_message')}>
|
|
Submit Message
|
|
</Button>
|
|
<Button fluid color="bad" icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Cancel
|
|
</Button>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterPrint = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { total_num, active_num, message_num, paper_remaining } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
return (
|
|
<Section
|
|
title="Printing"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
<Box color="label" mb={1}>
|
|
Newscaster currently serves a total of {total_num} Feed channels, {active_num} of which are active, and a total
|
|
of {message_num} Feed stories.
|
|
</Box>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Liquid Paper remaining">{paper_remaining * 100} cm³</LabeledList.Item>
|
|
</LabeledList>
|
|
<Button mt={1} fluid color="good" icon="plus" onClick={() => act('print_paper')}>
|
|
Print Paper
|
|
</Button>
|
|
<Button fluid color="bad" icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Cancel
|
|
</Button>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterNewWanted = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { channel_name, msg, photo_data, user, wanted_issue } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
return (
|
|
<Section
|
|
title="Wanted Issue Handler"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
<LabeledList>
|
|
{!!wanted_issue && (
|
|
<LabeledList.Item label="Already In Circulation">
|
|
A wanted issue is already in circulation. You can edit or cancel it below.
|
|
</LabeledList.Item>
|
|
)}
|
|
<LabeledList.Item label="Criminal Name">
|
|
<Input
|
|
fluid
|
|
value={decodeHtmlEntities(channel_name)}
|
|
onInput={(e, val) => act('set_channel_name', { val: val })}
|
|
/>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Description">
|
|
<Input fluid value={decodeHtmlEntities(msg)} onInput={(e, val) => act('set_wanted_desc', { val: val })} />
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Attach Photo">
|
|
<Button fluid icon="image" onClick={() => act('set_attachment')}>
|
|
{photo_data ? 'Photo Attached' : 'No Photo'}
|
|
</Button>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Prosecutor" color="good">
|
|
{user}
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Button mt={1} fluid color="good" icon="plus" onClick={() => act('submit_wanted')}>
|
|
Submit Wanted Issue
|
|
</Button>
|
|
{!!wanted_issue && (
|
|
<Button fluid color="average" icon="minus" onClick={() => act('cancel_wanted')}>
|
|
Take Down Issue
|
|
</Button>
|
|
)}
|
|
<Button fluid color="bad" icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Cancel
|
|
</Button>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterViewWanted = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { wanted_issue } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
if (!wanted_issue) {
|
|
return (
|
|
<Section
|
|
title="No Outstanding Wanted Issues"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
There are no wanted issues currently outstanding.
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Section
|
|
title="--STATIONWIDE WANTED ISSUE--"
|
|
color="bad"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
<Box color="white">
|
|
<LabeledList>
|
|
<LabeledList.Item label="Submitted by" color="good">
|
|
{decodeHtmlEntities(wanted_issue.author)}
|
|
</LabeledList.Item>
|
|
<LabeledList.Divider />
|
|
<LabeledList.Item label="Criminal">{decodeHtmlEntities(wanted_issue.criminal)}</LabeledList.Item>
|
|
<LabeledList.Item label="Description">{decodeHtmlEntities(wanted_issue.desc)}</LabeledList.Item>
|
|
<LabeledList.Item label="Photo">
|
|
{(wanted_issue.img && <img src={wanted_issue.img} />) || 'None'}
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
</Box>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const NewscasterViewSelected = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { viewing_channel, securityCaster, company } = data;
|
|
|
|
const { setScreen } = props;
|
|
|
|
if (!viewing_channel) {
|
|
return (
|
|
<Section
|
|
title="Channel Not Found"
|
|
buttons={
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
|
|
Back
|
|
</Button>
|
|
}>
|
|
The channel you were looking for no longer exists.
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Section
|
|
title={decodeHtmlEntities(viewing_channel.name)}
|
|
buttons={
|
|
<Fragment>
|
|
{!!securityCaster && (
|
|
<Button.Confirm
|
|
color="bad"
|
|
icon="ban"
|
|
confirmIcon="ban"
|
|
content="Issue D-Notice"
|
|
onClick={() => act('toggle_d_notice', { ref: viewing_channel.ref })}
|
|
/>
|
|
)}
|
|
<Button icon="undo" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
|
|
Back
|
|
</Button>
|
|
</Fragment>
|
|
}>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Channel Created By">
|
|
{(securityCaster && (
|
|
<Button.Confirm
|
|
color="bad"
|
|
icon="strikethrough"
|
|
confirmIcon="strikethrough"
|
|
content={decodeHtmlEntities(viewing_channel.author)}
|
|
tooltip="Censor?"
|
|
confirmContent="Censor Author"
|
|
onClick={() => act('censor_channel_author', { ref: viewing_channel.ref })}
|
|
/>
|
|
)) || <Box>{decodeHtmlEntities(viewing_channel.author)}</Box>}
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
{!!viewing_channel.censored && (
|
|
<Box color="bad">
|
|
ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a{' '}
|
|
{company} D-Notice. No further feed story additions are allowed while the D-Notice is in effect.
|
|
</Box>
|
|
)}
|
|
{(!!viewing_channel.messages.length &&
|
|
viewing_channel.messages.map((message) => (
|
|
<Section key={message.ref}>
|
|
- {decodeHtmlEntities(message.body)}
|
|
{!!message.img && (
|
|
<Box>
|
|
<img src={'data:image/png;base64,' + message.img} />
|
|
{decodeHtmlEntities(message.caption) || null}
|
|
</Box>
|
|
)}
|
|
<Box color="grey">
|
|
[Story by {decodeHtmlEntities(message.author)} - {message.timestamp}]
|
|
</Box>
|
|
{!!securityCaster && (
|
|
<Fragment>
|
|
<Button.Confirm
|
|
mt={1}
|
|
color="bad"
|
|
icon="strikethrough"
|
|
confirmIcon="strikethrough"
|
|
content="Censor Story"
|
|
onClick={() => act('censor_channel_story_body', { ref: message.ref })}
|
|
/>
|
|
<Button.Confirm
|
|
color="bad"
|
|
icon="strikethrough"
|
|
confirmIcon="strikethrough"
|
|
content="Censor Author"
|
|
onClick={() => act('censor_channel_story_author', { ref: message.ref })}
|
|
/>
|
|
</Fragment>
|
|
)}
|
|
</Section>
|
|
))) ||
|
|
(!viewing_channel.censored && <Box color="average">No feed messages found in channel.</Box>)}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
/* Must be at the bottom because of how the const lifting rules work */
|
|
let screenToTemplate = {};
|
|
screenToTemplate[NEWSCASTER_SCREEN_MAIN] = NewscasterMainMenu;
|
|
screenToTemplate[NEWSCASTER_SCREEN_NEWCHANNEL] = NewscasterNewChannel;
|
|
screenToTemplate[NEWSCASTER_SCREEN_VIEWLIST] = NewscasterViewList;
|
|
screenToTemplate[NEWSCASTER_SCREEN_NEWSTORY] = NewscasterNewStory;
|
|
screenToTemplate[NEWSCASTER_SCREEN_PRINT] = NewscasterPrint;
|
|
screenToTemplate[NEWSCASTER_SCREEN_NEWWANTED] = NewscasterNewWanted;
|
|
screenToTemplate[NEWSCASTER_SCREEN_VIEWWANTED] = NewscasterViewWanted;
|
|
screenToTemplate[NEWSCASTER_SCREEN_SELECTEDCHANNEL] = NewscasterViewSelected;
|