feat: optional welcome modal

welcome modal is persisted in app state

created endpoints to set the visibility

added UI to support feature

Co-authored-by: Carlos Valente
<34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
arc-alex
2025-01-12 15:30:22 +01:00
committed by Carlos Valente
parent 4a6fbc2a92
commit ce8d534953
9 changed files with 66 additions and 16 deletions
+5 -4
View File
@@ -34,7 +34,7 @@ export class SocketServer implements IAdapter {
private wss: WebSocketServer | null;
private readonly clients: Map<string, Client>;
private lastConnection: Date | null = null;
private isFirstEditor = true;
private shouldShowWelcome = true;
constructor() {
if (instance) {
@@ -47,7 +47,8 @@ export class SocketServer implements IAdapter {
this.wss = null;
}
init(server: Server, prefix?: string) {
init(server: Server, showWelcome: boolean, prefix?: string) {
this.shouldShowWelcome = showWelcome;
this.wss = new WebSocketServer({ path: `${prefix}/ws`, server, maxPayload: this.MAX_PAYLOAD });
this.wss.on('connection', (ws) => {
@@ -136,8 +137,8 @@ export class SocketServer implements IAdapter {
previousData.path = payload;
this.clients.set(clientId, previousData);
if (payload.includes('editor') && this.isFirstEditor) {
this.isFirstEditor = false;
if (payload.includes('editor') && this.shouldShowWelcome) {
this.shouldShowWelcome = false;
ws.send(
JSON.stringify({
type: 'dialog',
@@ -6,6 +6,7 @@ import type { Request, Response } from 'express';
import { isDocker } from '../../externals.js';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as appState from '../../services/app-state-service/AppStateService.js';
import { extractPin } from './settings.utils.js';
@@ -65,3 +66,8 @@ export async function postSettings(req: Request, res: Response<Settings | ErrorR
res.status(400).send({ message });
}
}
export async function postWelcomeDialog(req: Request, res: Response) {
const show = await appState.setShowWelcomeDialog(req.body.show);
res.status(200).send({ show });
}
@@ -1,8 +1,10 @@
import express from 'express';
import { getSettings, postSettings } from './settings.controller.js';
import { validateSettings } from './settings.validation.js';
import { getSettings, postSettings, postWelcomeDialog } from './settings.controller.js';
import { validateSettings, validateWelcomeDialog } from './settings.validation.js';
export const router = express.Router();
router.post('/welcomedialog', validateWelcomeDialog, postWelcomeDialog);
router.get('/', getSettings);
router.post('/', validateSettings, postSettings);
@@ -1,6 +1,18 @@
import { body, validationResult } from 'express-validator';
import { Request, Response, NextFunction } from 'express';
/**
* @description Validates object for POST /ontime/settings/welcomedialog
*/
export const validateWelcomeDialog = [
body('show').exists().isBoolean(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
/**
* @description Validates object for POST /ontime/settings
*/
+3 -1
View File
@@ -44,6 +44,7 @@ import { clearUploadfolder } from './utils/upload.js';
import { generateCrashReport } from './utils/generateCrashReport.js';
import { timerConfig } from './config/config.js';
import { serverTryDesiredPort, getNetworkInterfaces } from './utils/network.js';
import { getShowWelcomeDialog } from './services/app-state-service/AppStateService.js';
console.log('\n');
consoleHighlight(`Starting Ontime version ${ONTIME_VERSION}`);
@@ -166,8 +167,9 @@ export const startServer = async (
// the express server must be started before the socket otherwise the on error event listener will not attach properly
const resultPort = await serverTryDesiredPort(expressServer, desiredPort);
await getDataProvider().setSettings({ ...settings, serverPort: resultPort });
const showWelcome = await getShowWelcomeDialog();
socket.init(expressServer, prefix);
socket.init(expressServer, showWelcome, prefix);
/**
* Module initialises the services and provides initial payload for the store
@@ -8,6 +8,7 @@ import { shouldCrashDev } from '../../utils/development.js';
interface AppState {
lastLoadedProject?: string;
showWelcomeDialog?: boolean;
}
const adapter = new JSONFile<AppState>(publicFiles.appState);
@@ -34,3 +35,19 @@ export async function setLastLoadedProject(filename: string): Promise<void> {
config.data.lastLoadedProject = filename;
await config.write();
}
export async function getShowWelcomeDialog(): Promise<boolean> {
// in test environment, we do not want the dialog
if (isTest) return false;
await config.read();
return config.data.showWelcomeDialog ?? true; // default to true
}
export async function setShowWelcomeDialog(show: boolean): Promise<boolean> {
if (isTest) return;
config.data.showWelcomeDialog = show;
await config.write();
return show;
}