Skip to content

Commit 93727fb

Browse files
committed
Added more abilities for banners
1 parent 0f784eb commit 93727fb

File tree

12 files changed

+212
-20
lines changed

12 files changed

+212
-20
lines changed

public/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default (): JSX.Element => {
2828
dispatch(loadBanners());
2929

3030
useEffect(() => {
31-
const seenBanners = (localStorage.getItem('SeenBanners') ?? []) as number[];
31+
const seenBanners = JSON.parse(localStorage.getItem('SeenBanners') ?? '[]') as number[];
3232
for (let banner of banners) {
3333
if (banner.startTime > Date.now() || banner.endTime < Date.now() || seenBanners.some(id => id === banner.id)) {
3434
continue;

public/src/actions/bannerActions.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { HTTP_SERVER_URL } from '../configuration';
55

66
export const ActionTypes = Object.freeze({
77
AddBanner: 'ADD_BANNER',
8+
UpdateBanner: 'UPDATE_BANNER',
89
DeleteBanner: 'DELETE_BANNER',
910
GetBanners: new AsyncFunction('GET_BANNERS'),
1011
HideBanner: 'HIDE_BANNER'
@@ -15,9 +16,14 @@ export const addBanner = (message: string, startTime: number, endTime: number):
1516
payload: { message, startTime, endTime }
1617
});
1718

18-
export const deleteBanner = (id: number): FluxStandardAction => ({
19+
export const updateBanner = (id: number, message: string, startTime: number, endTime: number): FluxStandardAction => ({
20+
type: ActionTypes.UpdateBanner,
21+
payload: { id, message, startTime, endTime }
22+
});
23+
24+
export const deleteBanner = (id: number, message: string, startTime: number, endTime: number): FluxStandardAction => ({
1925
type: ActionTypes.DeleteBanner,
20-
payload: { id }
26+
payload: { id, message, startTime, endTime }
2127
});
2228

2329
export const loadBanners = (): AsyncAction => ({

public/src/actions/modalActions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const ActionTypes = Object.freeze({
1515
OpenSetQueueInformationModal: 'OPEN_SET_QUEUE_INFORMATION_MODAL',
1616
OpenSetServerMessageModal: 'OPEN_SET_SERVER_MESSAGE_MODAL',
1717
OpenAddBannerModal: 'OPEN_ADD_BANNER_MODAL',
18+
OpenUpdateBannerModal: 'OPEN_UPDATE_BANNER_MODAL',
1819
OpenShowMotdModal: 'OPEN_SHOW_MOTD_MODAL',
1920
OpenShowQueueModal: 'OPEN_SHOW_QUEUE_MODAL'
2021
});
@@ -60,6 +61,11 @@ export const openAddBannerModal = (): FluxStandardAction => ({
6061
type: ActionTypes.OpenAddBannerModal
6162
});
6263

64+
export const openUpdateBannerModal = (id: number): FluxStandardAction => ({
65+
type: ActionTypes.OpenUpdateBannerModal,
66+
payload: { id }
67+
});
68+
6369
export const openShowQueueModal = (queueName: string): FluxStandardAction => ({
6470
type: ActionTypes.OpenShowQueueModal,
6571
payload: { queueName }

public/src/middlewares/SocketMiddleware.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,22 @@ const middleware = () => {
215215
break;
216216
}
217217

218+
case BannerActions.UpdateBanner: {
219+
sendMessage(new RequestMessage(`updateBanner`, {
220+
id: action.payload.id,
221+
message: action.payload.message,
222+
startTime: action.payload.startTime,
223+
endTime: action.payload.endTime
224+
}));
225+
break;
226+
}
227+
218228
case BannerActions.DeleteBanner: {
219-
sendMessage(new RequestMessage(`deleteBanner`, {
220-
id: action.payload.id
229+
sendMessage(new RequestMessage(`updateBanner`, {
230+
id: action.payload.id,
231+
message: action.payload.message,
232+
startTime: action.payload.startTime,
233+
endTime: action.payload.endTime
221234
}));
222235
break;
223236
}

public/src/pages/Administration/Administrators/AdministrationInformation.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React from 'react';
22
import { useSelector, useDispatch } from 'react-redux'
33
import { GlobalStore } from '../../../store';
4+
import { Link } from 'react-router-dom';
45
import { openSetServerMessageModal, openAddBannerModal } from '../../../actions/modalActions';
5-
import { deleteBanner } from '../../../actions/bannerActions';
66
import User from '../../../models/User';
77
import Banner from '../../../models/Banner';
8-
import { Cross } from '../../../viewcomponents/FontAwesome';
8+
import BannerList from './BannerList';
99

1010
export default function AdministrationInformationViewComponent() {
1111

@@ -51,14 +51,11 @@ export default function AdministrationInformationViewComponent() {
5151
{
5252
banners.length === 0
5353
? null
54-
: <div className="mb-5">
55-
<h4>Active information banners</h4>
56-
{
57-
banners.map((banner: Banner) =>
58-
<p key={banner.id}>
59-
<Cross color="red" title="Remove banner" onClick={() => dispatch(deleteBanner(banner.id))} /> { banner.message }
60-
</p>)
61-
}
54+
: <div className="row mb-5">
55+
<div className="col-12 mb-3">
56+
<h2>Currently active banners <Link to="/help#banner">?</Link></h2>
57+
</div>
58+
<BannerList />
6259
</div>
6360
}
6461
</>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import { useSelector, useDispatch } from 'react-redux'
3+
import { GlobalStore } from '../../../store';
4+
import { openUpdateBannerModal } from '../../../actions/modalActions';
5+
import Banner from '../../../models/Banner';
6+
import { Pen } from '../../../viewcomponents/FontAwesome';
7+
8+
export default (): JSX.Element | null => {
9+
10+
const banners = useSelector<GlobalStore, Banner[]>(store => store.banners);
11+
12+
const dispatch = useDispatch();
13+
14+
function getTimestamp(ms: number) {
15+
const date = new Date(ms);
16+
const pad = (n: any, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s);
17+
return `${pad(date.getFullYear(),4)}-${pad(date.getMonth()+1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
18+
}
19+
20+
return (
21+
banners.length
22+
? <div>
23+
<table className="table table-striped scrollable">
24+
<thead>
25+
<tr>
26+
<th>Message</th>
27+
<th>Shown between</th>
28+
<th></th>
29+
</tr>
30+
</thead>
31+
<tbody>
32+
{
33+
banners.map((banner: Banner) =>
34+
<tr key={ banner.id }>
35+
<td>{ banner.message }</td>
36+
<td>{ getTimestamp(banner.startTime) } and { getTimestamp(banner.endTime) }</td>
37+
<td>
38+
<Pen
39+
color="green"
40+
title="Update banner"
41+
onClick={() => dispatch(openUpdateBannerModal(banner.id))} />
42+
</td>
43+
</tr>)
44+
}
45+
</tbody>
46+
</table>
47+
</div>
48+
: null
49+
);
50+
};

public/src/pages/Help/Users/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ export default (): JSX.Element | null => {
114114

115115
To search for a queue, type the queue name (or parts of the queue name) in the search box at the top of the page.
116116
</p>
117+
118+
<h3 id="banner">Banners</h3>
119+
<p>
120+
Although <i>Stay A While</i> is a flawless system, once in a blue moon, an administrator may want to inform the users of upcoming or current issues and changes.
121+
If that were to ever happen, an alert box will be displayed at the bottom of the user's screen.
122+
Feel free to discard the message after reading it thoroughly several times over.
123+
</p>
117124
</div>
118125
: null
119126
);

public/src/reducers/bannerReducer.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ const initialState = [] as Banner[];
77
export default (state = initialState, action: FluxStandardAction) => {
88
switch (action.type) {
99

10-
case BannerActionTypes.DeleteBanner: {
11-
return state;
12-
}
13-
1410
case BannerActionTypes.GetBanners.Rejected: {
1511
return state;
1612
}
@@ -25,7 +21,7 @@ export default (state = initialState, action: FluxStandardAction) => {
2521

2622
case BannerActionTypes.HideBanner: {
2723
const token = 'SeenBanners';
28-
const seenBanners = (localStorage.getItem(token) ?? []) as number[];
24+
const seenBanners = JSON.parse(localStorage.getItem('SeenBanners') ?? '[]') as number[];
2925
seenBanners.push(action.payload.id);
3026
localStorage.setItem(token, JSON.stringify(seenBanners));
3127
return state;

public/src/reducers/modalReducer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ModalType as SetMotdModal } from '../viewcomponents/Modals/SetMotdModal
1414
import { ModalType as SetQueueInformationModal } from '../viewcomponents/Modals/SetQueueInformationModal';
1515
import { ModalType as SetServerMessageModal } from '../viewcomponents/Modals/ServerMessageModal';
1616
import { ModalType as AddBannerModal } from '../viewcomponents/Modals/AddBannerModal';
17+
import { ModalType as UpdateBannerModal } from '../viewcomponents/Modals/UpdateBannerModal';
1718
import { ModalType as ShowMessageModal } from '../viewcomponents/Modals/ShowMessageModal';
1819
import { ModalType as ShowMotdModal } from '../viewcomponents/Modals/ShowMotdModal';
1920
import { ModalType as ShowQueueModal } from '../viewcomponents/Modals/ShowQueueModal';
@@ -78,6 +79,10 @@ export default (state = initialState, action: FluxStandardAction) => {
7879
return { ...state, modalList: [...state.modalList, new Modal(AddBannerModal)] }
7980
}
8081

82+
case ModalActionTypes.OpenUpdateBannerModal: {
83+
return { ...state, modalList: [...state.modalList, new Modal(UpdateBannerModal, action.payload)] }
84+
}
85+
8186
case ModalActionTypes.OpenShowQueueModal: {
8287
return { ...state, modalList: [...state.modalList, new Modal(ShowQueueModal, action.payload)] }
8388
}

public/src/viewcomponents/FontAwesome.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const Invisible = (props: any): JSX.Element => Markup(props, 'eye-slash')
1111
export const Lock = (props: any): JSX.Element => Markup(props, 'lock');
1212
export const Megaphone = (props: any): JSX.Element => Markup(props, 'bullhorn');
1313
export const Muted = (props: any): JSX.Element => Markup(props, 'volume-mute');
14+
export const Pen = (props: any): JSX.Element => Markup(props, 'pen');
1415
export const Plus = (props: any): JSX.Element => Markup(props, 'plus');
1516
export const QuestionMark = (props: any): JSX.Element => Markup(props, 'question');
1617
export const Sign = (props: any): JSX.Element => Markup(props, 'sign');

0 commit comments

Comments
 (0)