Skip to content

Commit

Permalink
Give displays the ability to link through to a dev environment
Browse files Browse the repository at this point in the history
  • Loading branch information
beverloo committed Mar 15, 2024
1 parent cf659ea commit 5dc5d7f
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 12 deletions.
9 changes: 9 additions & 0 deletions app/admin/system/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ export default async function IntegrationsPage() {
description:
'Every how many seconds should displays "phone home" for updated information?',
},
{
setting: 'display-dev-environment-link',
type: 'string',
defaultValue: '',

label: 'Dev environment link',
description:
'When set, a menu item will be included linking through to this URL.',
},
{
setting: 'display-max-time-since-check-in-days',
type: 'number',
Expand Down
2 changes: 1 addition & 1 deletion app/api/admin/updateSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const kUpdateSettingsDefinition = z.object({
/**
* Value that the setting should be set to.
*/
value: z.number().or(z.string().min(1)),
value: z.number().or(z.string()),

})).min(1),
}),
Expand Down
37 changes: 27 additions & 10 deletions app/api/display/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { z } from 'zod';

import type { ApiDefinition, ApiRequest, ApiResponse } from '../Types';
import { executeAction, noAccess, type ActionProps } from '../Action';
import { readSetting } from '@lib/Settings';
import { readSettings } from '@lib/Settings';
import { getDisplayIdFromHeaders, writeDisplayIdToHeaders } from '@lib/auth/DisplaySession';
import db, { tActivitiesLocations, tDisplays, tEvents } from '@lib/database';

Expand All @@ -26,6 +26,21 @@ const kDisplayDefinition = z.object({
*/
label: z.string(),

/**
* Optional link to the development environment.
*/
devEnvironment: z.string().optional(),

/**
* Whether the device should be locked.
*/
locked: z.boolean(),

/**
* Whether the device has been fully provisioned and is ready for use.
*/
provisioned: z.boolean(),

/**
* Timezone in which the display operates. Will affect the local time.
*/
Expand Down Expand Up @@ -66,7 +81,12 @@ async function display(request: Request, props: ActionProps): Promise<Response>
if (!props.ip)
noAccess();

const updateFrequencySeconds = await readSetting('display-check-in-rate-seconds') ?? 300;
const settings = await readSettings([
'display-check-in-rate-seconds',
'display-dev-environment-link',
]);

const updateFrequencySeconds = settings['display-check-in-rate-seconds'] ?? 300;
const updateFrequencyMs = Math.max(10, updateFrequencySeconds) * 1000;

// ---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -119,26 +139,23 @@ async function display(request: Request, props: ActionProps): Promise<Response>
.select({
identifier: tDisplays.displayIdentifier,
label: tDisplays.displayLabel,
locked: tDisplays.displayLocked.equals(/* true= */ 1),
timezone: eventsJoin.eventTimezone,

// Event information:
eventId: eventsJoin.eventId,
timezone: eventsJoin.eventTimezone,

// Location information:
locationId: activitiesLocationsJoin.locationId,
})
.executeSelectOne();

// ---------------------------------------------------------------------------------------------
// Step 3: Read dynamic information about the volunteers at the location
// ---------------------------------------------------------------------------------------------

// TODO: Include the list of volunteers
// TODO: Include the device locked status

return {
identifier: configuration.identifier,
label: configuration.label ?? 'AnimeCon Volunteering Teams',
devEnvironment: settings['display-dev-environment-link'],
locked: configuration.locked,
provisioned: !!configuration.eventId && !!configuration.locationId,
timezone: configuration.timezone ?? 'Europe/Amsterdam',
updateFrequencyMs,
};
Expand Down
2 changes: 1 addition & 1 deletion app/display/DisplayController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function DisplayController(props: React.PropsWithChildren) {

return () => clearInterval(timer);

}, [ updateFrequencyMs ])
}, [ updateFrequencyMs ]);

// Effect that actually updates the context. This is done by making a network call to the
// Display API, which identifies the displays based on an identifier assigned by the server,
Expand Down
34 changes: 34 additions & 0 deletions app/display/DisplayHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
'use client';

import { useCallback, useContext, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';

import type { SvgIconProps } from '@mui/material/SvgIcon';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import Paper from '@mui/material/Paper';
Expand Down Expand Up @@ -44,6 +46,34 @@ function CurrentTime(props: { timezone?: string }) {
*/
export function DisplayHeader() {
const context = useContext(DisplayContext);
const router = useRouter();

const [ showDevEnvironment, setShowDevEnvironment ] = useState<boolean>(false);

const handleDevEnvironment = useCallback(() => {
if (!!context?.devEnvironment)
router.push(context.devEnvironment);

}, [ context?.devEnvironment, router ]);

useEffect(() => {
if (!!context && !!context.devEnvironment) {
try {
const currentUrl = new URL(window.location.href);
const devUrl = new URL(context.devEnvironment);

if (currentUrl.host !== devUrl.host) {
setShowDevEnvironment(true);
return;
}
} catch (error: any) {
console.error(`Invalid devEnvironment setting: ${context.devEnvironment}`);
}
}

setShowDevEnvironment(false);

}, [ context ]);

const [ refreshActive, setRefreshActive ] = useState<boolean>(false);
const [ refreshResult, setRefreshResult ] = useState<SvgIconProps['color']>('inherit');
Expand Down Expand Up @@ -75,6 +105,10 @@ export function DisplayHeader() {
<IconButton onClick={handleRefresh}>
<RefreshIcon color={refreshResult} />
</IconButton> }
{ !!showDevEnvironment &&
<IconButton onClick={handleDevEnvironment}>
<DeveloperBoardIcon color="inherit" />
</IconButton> }
<IconButton>
<SettingsIcon />
</IconButton>
Expand Down
1 change: 1 addition & 0 deletions app/lib/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type SettingsMap = {
// ---------------------------------------------------------------------------------------------

'display-check-in-rate-seconds': number;
'display-dev-environment-link': string;
'display-max-time-since-check-in-days': number;

// ---------------------------------------------------------------------------------------------
Expand Down

0 comments on commit 5dc5d7f

Please sign in to comment.