diff --git a/backend/src/matchService.ts b/backend/src/matchService.ts index 5e6e6d7..1d37269 100644 --- a/backend/src/matchService.ts +++ b/backend/src/matchService.ts @@ -163,6 +163,16 @@ export const remove = async (id: string) => { } }; +export const removeStopped = async (id: string) => { + const matchFromStorage = await getFromStorage(id); + if (!matchFromStorage) { + return false; + } + matchFromStorage.isStopped = true; + await save(matchFromStorage); + return true; +}; + export const revive = async (id: string) => { const match = matches.get(id); if (match) { diff --git a/backend/src/matchesController.ts b/backend/src/matchesController.ts index 1694d26..26968b9 100644 --- a/backend/src/matchesController.ts +++ b/backend/src/matchesController.ts @@ -175,6 +175,13 @@ export class MatchesController extends Controller { const match = MatchService.get(id); if (match) { await Match.update(match, requestBody); + } else if (requestBody.gameServer) { + // for offline matches only allow to update game server to get match running again + const offlineMatch = await MatchService.getFromStorage(id); + if (offlineMatch) { + offlineMatch.gameServer = requestBody.gameServer; + await MatchService.save(offlineMatch); + } } else { this.setStatus(404); } @@ -209,7 +216,9 @@ export class MatchesController extends Controller { @Delete('{id}') async deleteMatch(id: string, @Request() req: ExpressRequest): Promise { if (!(await MatchService.remove(id))) { - this.setStatus(404); + if (!(await MatchService.removeStopped(id))) { + this.setStatus(404); + } } } diff --git a/backend/swagger.json b/backend/swagger.json index b568a15..edc64cc 100644 --- a/backend/swagger.json +++ b/backend/swagger.json @@ -1167,7 +1167,8 @@ "description": "Match mode (single: stops when match is finished, loop: starts again after match is finished)" }, "isLive": { - "type": "boolean" + "type": "boolean", + "description": "Match is currently supervised." } }, "required": [ diff --git a/common/types/match.ts b/common/types/match.ts index a0db5bb..5d8c327 100644 --- a/common/types/match.ts +++ b/common/types/match.ts @@ -115,6 +115,7 @@ export interface IMatch { } export interface IMatchResponse extends IMatch { + /** Match is currently supervised. */ isLive: boolean; } diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index e0bbc28..c5bd254 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -1,15 +1,17 @@ -import { Component } from 'solid-js'; +import { Component, createSignal } from 'solid-js'; import { ChatEvent } from '../../../common'; import { t } from '../utils/locale'; import { onEnter } from '../utils/onEnter'; import { Card } from './Card'; -import { ScrollArea } from './ScrollArea'; +import { ErrorComponent } from './ErrorComponent'; import { TextInput } from './Inputs'; +import { ScrollArea } from './ScrollArea'; export const Chat: Component<{ messages: ChatEvent[]; - sendMessage: (msg: string) => void; + sendMessage: (msg: string) => Promise; }> = (props) => { + const [errorMessage, setErrorMessage] = createSignal(''); return (

{t('Chat')}

@@ -18,14 +20,21 @@ export const Chat: Component<{ { - const msg = e.currentTarget.value.trim(); + const input = e.currentTarget; + const msg = input.value.trim(); if (msg) { - props.sendMessage(msg); - e.currentTarget.value = ''; + props + .sendMessage(msg) + .then(() => { + input.value = ''; + setErrorMessage(''); + }) + .catch((err) => setErrorMessage(err + '')); } })} placeholder={t('Send chat message...')} /> +
); }; diff --git a/frontend/src/components/CreateUpdateMatch.tsx b/frontend/src/components/CreateUpdateMatch.tsx index 4cce545..a36500f 100644 --- a/frontend/src/components/CreateUpdateMatch.tsx +++ b/frontend/src/components/CreateUpdateMatch.tsx @@ -221,11 +221,11 @@ export const CreateUpdateMatch: Component< ( | { mode: 'CREATE'; - match: IMatchCreateDto; + match: IMatchCreateDto & { isStopped?: boolean }; } | { mode: 'UPDATE'; - match: IMatchResponse; + match: IMatchResponse & { isStopped?: boolean }; } ) & { callback: (data: IMatchUpdateDto & IMatchCreateDto) => Promise; @@ -278,40 +278,43 @@ export const CreateUpdateMatch: Component< return ( <> - - { - setDto(copyObject(preset)); - if (!dto.tmtLogAddress) { - setTmtLogAddress(); - } - }} + + + { + setDto(copyObject(preset)); + if (!dto.tmtLogAddress) { + setTmtLogAddress(); + } + }} + /> + +
+

{t('Teams')}

+
+ setDto('teamA', 'name', e.currentTarget.value)} + /> + setDto('teamB', 'name', e.currentTarget.value)} />
-
-

{t('Teams')}

-
- setDto('teamA', 'name', e.currentTarget.value)} - /> - setDto('teamB', 'name', e.currentTarget.value)} - /> +

{t('Game Server')}

@@ -364,220 +367,239 @@ export const CreateUpdateMatch: Component< )} onInput={(e) => setDto('gameServer', 'rconPassword', e.currentTarget.value)} /> -
-

{t('Map Pool')}

-
-