diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fac5fce..f118009 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,8 +44,8 @@ jobs: with: tag_name: ${{ github.ref }} release_name: ${{ github.ref }} - draft: false - prerelease: true + draft: true + prerelease: false - name: Upload Release Asset id: upload-release-asset diff --git a/core/game/bot.ts b/core/game/bot.ts index ed5a4bb..12c9f91 100644 --- a/core/game/bot.ts +++ b/core/game/bot.ts @@ -39,6 +39,7 @@ window.gameRoom = { ,antiTrollingChatFloodCount: [] ,antiInsufficientStartAbusingCount: [] ,antiPlayerKickAbusingCount: [] + ,notice: '' ,onEmergency: EmergencyTools } diff --git a/core/game/controller/Parser.ts b/core/game/controller/Parser.ts index 16b25ef..f35be98 100644 --- a/core/game/controller/Parser.ts +++ b/core/game/controller/Parser.ts @@ -14,6 +14,7 @@ import { cmdMute } from "./commands/mute"; import { cmdVote } from "./commands/vote"; import { cmdSuper } from "./commands/super"; import { cmdTier } from "./commands/tier"; +import { cmdNotice } from "./commands/notice"; // if given string is command chat, this function returns true, nor false. export function isCommandString(message: string): boolean { @@ -115,6 +116,10 @@ export function parseCommand(byPlayer: PlayerObject, message: string): void { cmdTier(byPlayer); break; } + case window.gameRoom.config.commands.notice: { + cmdNotice(byPlayer); + break; + } case window.gameRoom.config.commands.super: { if(msgChunk[1] !== undefined) { if(msgChunk[2] !== undefined) { diff --git a/core/game/controller/commands/help.ts b/core/game/controller/commands/help.ts index 2344c95..1f0d897 100644 --- a/core/game/controller/commands/help.ts +++ b/core/game/controller/commands/help.ts @@ -60,6 +60,10 @@ export function cmdHelp(byPlayer: PlayerObject, message?: string): void { window.gameRoom._room.sendAnnouncement(LangRes.command.helpman.tier, byPlayer.id, 0x479947, "normal", 1); break; } + case window.gameRoom.config.commands._helpMannotice: { + window.gameRoom._room.sendAnnouncement(LangRes.command.helpman.notice, byPlayer.id, 0x479947, "normal", 1); + break; + } default: { window.gameRoom._room.sendAnnouncement(LangRes.command.helpman._ErrorWrongMan, byPlayer.id, 0xFF7777, "normal", 2); break; diff --git a/core/game/controller/commands/notice.ts b/core/game/controller/commands/notice.ts new file mode 100644 index 0000000..e19cdfe --- /dev/null +++ b/core/game/controller/commands/notice.ts @@ -0,0 +1,10 @@ +import * as LangRes from "../../resource/strings"; +import { PlayerObject } from "../../model/GameObject/PlayerObject"; + +export function cmdNotice(byPlayer: PlayerObject): void { + if(window.gameRoom.notice) { + window.gameRoom._room.sendAnnouncement(window.gameRoom.notice, byPlayer.id, 0x479947, "normal", 1); + } else { + window.gameRoom._room.sendAnnouncement(LangRes.command.notice._ErrorNoMessage, byPlayer.id, 0xFF7777, "normal", 1); + } +} diff --git a/core/game/controller/events/onPlayerJoin.ts b/core/game/controller/events/onPlayerJoin.ts index 20d5312..56e03e3 100644 --- a/core/game/controller/events/onPlayerJoin.ts +++ b/core/game/controller/events/onPlayerJoin.ts @@ -194,6 +194,11 @@ export async function onPlayerJoinListener(player: PlayerObject): Promise // send welcome message to new player. other players cannot read this message. window.gameRoom._room.sendAnnouncement(Tst.maketext(LangRes.onJoin.welcome, placeholderJoin), player.id, 0x00FF00, "normal", 0); + // send notice + if(window.gameRoom.notice !== '') { + window.gameRoom._room.sendAnnouncement(window.gameRoom.notice, player.id, 0x55AADD, "bold", 0); + } + // check number of players joined and change game mode let activePlayersNumber: number = roomActivePlayersNumberCheck(); if (window.gameRoom.config.rules.statsRecord === true && activePlayersNumber >= window.gameRoom.config.rules.requisite.minimumPlayers) { diff --git a/core/game/model/Configuration/GameCommands.ts b/core/game/model/Configuration/GameCommands.ts index a22f892..a091004 100644 --- a/core/game/model/Configuration/GameCommands.ts +++ b/core/game/model/Configuration/GameCommands.ts @@ -16,6 +16,7 @@ export interface GameCommands { _helpMantier: string _helpMansuper: string _helpManadmin: string + _helpMannotice: string _listSubafk: string _listSubmute: string @@ -46,4 +47,5 @@ export interface GameCommands { super: string vote: string tier: string + notice: string } \ No newline at end of file diff --git a/core/game/resource/strings.sample.en.ts b/core/game/resource/strings.sample.en.ts index 68c47e8..211eb6e 100644 --- a/core/game/resource/strings.sample.en.ts +++ b/core/game/resource/strings.sample.en.ts @@ -54,7 +54,7 @@ export const command = { _ErrorWrongCommand : '❌ You did wrong command. πŸ“‘ !help or !help COMMAND for detail.' ,_ErrorNoPermission: '❌ You are not admin. You can\'t use this command.' ,_ErrorDisabled: '❌ This command is disabled. You can\'t use this command.' - ,help: 'πŸ“„ !about, stats, statsreset, tier, afk, vote, poss, streak, scout, list\nπŸ“‘ !help COMMAND for detail. (eg. !help stats)\nπŸ“‘ !help admin shows you commands list for administrator.' + ,help: 'πŸ“„ !about, notice, stats, statsreset, tier, afk, vote, poss, streak, scout, list\nπŸ“‘ !help COMMAND for detail. (eg. !help stats)\nπŸ“‘ !help admin shows you commands list for administrator.' ,helpadmin: 'πŸ“„ !freeze, mute\nπŸ“‘ !help COMMAND for detail.' ,helpman: { // detailed description for a command _ErrorWrongMan : '❌ Failed to read manual about that command.' @@ -71,6 +71,7 @@ export const command = { ,scout: 'πŸ“‘ !scout shows you expectation of each teams by customed Pythagorean Expectation.' ,vote: 'πŸ“‘ !vote shows you progress of the vote.\nπŸ“‘ !vote #ID : vote for kick that player. (eg: !vote #12)' ,tier: 'πŸ“‘ !tier shows you information of tier and rating system.' + ,notice: 'πŸ“‘ !notice shows you notice message.' } ,about: 'πŸ“„ {RoomName} ({_LaunchTime})\nπŸ’¬ This room is powered by HaxbotronπŸ€– bot. https://github.com/dapucita/haxbotron\nπŸ’¬ Discord https://discord.gg/qfg45B2 Donate https://www.patreon.com/dapucita' ,stats: { @@ -91,7 +92,7 @@ export const command = { ,mute: { _ErrorNoPermission: '❌ You are not admin. You can\'t do this command.' ,_ErrorNoPlayer: '❌ Wrong player ID. You can only target numeric ID.(eg: !mute #12)\nπŸ“‘ You can check IDs by command !list red,blue,spec,mute' - ,successMute: 'πŸ”‡ {targetName}#{ticketTarget} player is muted.(30mins) You can command it againt for release.' + ,successMute: 'πŸ”‡ {targetName}#{ticketTarget} player is muted.(3mins) You can command it againt for release.' ,successUnmute: 'πŸ”Š Player {targetName}#{ticketTarget} is unmuted.' ,muteAbusingWarn: '❌ You can\'t mute again this player in too short time. (3mins)' } @@ -153,6 +154,9 @@ export const command = { ,voteAutoNotify: 'πŸ—³οΈ Voting to ban is in progress: {voteList}' } ,tier: 'πŸ“„ Tier is determined by the rating score. (!stats shows your rating)\nπŸ“‘ {tierAvatar9}{tierCutoff9} {tierAvatar8}{tierCutoff8} {tierAvatar7}{tierCutoff7} {tierAvatar6}{tierCutoff6} {tierAvatar5}{tierCutoff5} {tierAvatar4}{tierCutoff4} {tierAvatar3}{tierCutoff3} {tierAvatar2}{tierCutoff2} {tierAvatar1}{tierCutoff1}' + ,notice: { + _ErrorNoMessage: '❌ No notice message.' + } } export const funcUpdateAdmins = { diff --git a/core/game/resource/strings.ts b/core/game/resource/strings.ts index d140117..2cde3e5 100644 --- a/core/game/resource/strings.ts +++ b/core/game/resource/strings.ts @@ -54,7 +54,7 @@ export const command = { _ErrorWrongCommand : '❌ 잘λͺ»λœ λͺ…λ Ήμ–΄μž…λ‹ˆλ‹€. πŸ“‘ !help λ˜λŠ” !help COMMAND둜 μžμ„Ένžˆ μ•Œμ•„λ³΄μ„Έμš”.' ,_ErrorNoPermission: '❌ κ΄€λ¦¬μžλ§Œ 이 λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.' ,_ErrorDisabled: '❌ ν˜„μž¬ λ°©μ—μ„œλŠ” μ‚¬μš©ν•  수 μ—†λŠ” λͺ…λ Ήμ–΄μž…λ‹ˆλ‹€.' - ,help: 'πŸ“„ !about, stats, statsreset, tier, afk, vote, poss, streak, scout, list\nπŸ“‘ !help COMMAND둜 μžμ„Ένžˆ 보기 (예: !help stats)\nπŸ“‘ !help admin 을 μž…λ ₯ν•˜μ—¬ κ΄€λ¦¬μžμš© λͺ…λ Ήμ–΄λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.' + ,help: 'πŸ“„ !about, notice, stats, statsreset, tier, afk, vote, poss, streak, scout, list\nπŸ“‘ !help COMMAND둜 μžμ„Ένžˆ 보기 (예: !help stats)\nπŸ“‘ !help admin 을 μž…λ ₯ν•˜μ—¬ κ΄€λ¦¬μžμš© λͺ…λ Ήμ–΄λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.' ,helpadmin: 'πŸ“„ !freeze, mute\nπŸ“‘ !help COMMAND둜 μžμ„Ένžˆ 보기' ,helpman: { // detailed description for a command _ErrorWrongMan : '❌ μš”μ²­ν•˜μ‹  λͺ…령어에 λŒ€ν•œ μ„€λͺ…이 μ—†μŠ΅λ‹ˆλ‹€.' @@ -71,6 +71,7 @@ export const command = { ,scout: 'πŸ“‘ !scout : 각 νŒ€μ˜ κΈ°λŒ€μŠΉλ₯ μΉ˜λ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€. νŒ€ κ°„μ˜ λΉ„κ΅λŠ” μ•„λ‹ˆλ©°, ν”Όνƒ€κ³ λ¦¬μ•ˆ 승λ₯  κ³΅μ‹μ˜ λ³€ν˜•μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.' ,vote: 'πŸ“‘ !vote : ν˜„μž¬ μΆ”λ°© νˆ¬ν‘œ ν˜„ν™©κ³Ό 본인의 νˆ¬ν‘œ μƒνƒœλ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€.\nπŸ“‘ !vote #ID : ν•΄λ‹Ή ID의 ν”Œλ ˆμ΄μ–΄μ— λŒ€ν•΄ μΆ”λ°© νˆ¬ν‘œλ₯Ό ν•˜κ±°λ‚˜ μ·¨μ†Œν•©λ‹ˆλ‹€. IDλŠ” μˆ«μžμ΄μ–΄μ•Ό ν•©λ‹ˆλ‹€. (예: !vote #12)' ,tier: 'πŸ“‘ !tier : 티어와 λ ˆμ΄νŒ… μ‹œμŠ€ν…œμ— λŒ€ν•œ 정보λ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€.' + ,notice: 'πŸ“‘ !notice : 곡지사항을 λ³΄μ—¬μ€λ‹ˆλ‹€.' } ,about: 'πŸ“„ λ°© 이름 : {RoomName} ({_LaunchTime})\nπŸ’¬ 이 방은 HaxbotronπŸ€– 봇에 μ˜ν•΄ μš΄μ˜λ©λ‹ˆλ‹€. https://github.com/dapucita/haxbotron\nπŸ’¬ [λ””μŠ€μ½”λ“œ] https://discord.gg/qfg45B2 [ν›„μ›ν•˜κΈ°] https://www.patreon.com/dapucita' ,stats: { @@ -91,7 +92,7 @@ export const command = { ,mute: { _ErrorNoPermission: '❌ κ΄€λ¦¬μžλ§Œ 이 λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.' ,_ErrorNoPlayer: '❌ 접속쀑이지 μ•ŠμŠ΅λ‹ˆλ‹€. #μˆ«μžμ•„μ΄λ”” 의 ν˜•μ‹μœΌλ‘œ 지정해야 ν•©λ‹ˆλ‹€. (예: !mute #12)\nπŸ“‘ !list red,blue,spec,mute λͺ…λ Ήμ–΄λ‘œ μˆ«μžμ•„μ΄λ””λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.' - ,successMute: 'πŸ”‡ {targetName}#{ticketTarget}λ‹˜μ„ μŒμ†Œκ±°ν–ˆμŠ΅λ‹ˆλ‹€.(30λΆ„) ν•΄μ œν•˜λ €λ©΄ mute λͺ…λ Ήμ–΄λ₯Ό λ‹€μ‹œ μ‚¬μš©ν•˜μ„Έμš”.' + ,successMute: 'πŸ”‡ {targetName}#{ticketTarget}λ‹˜μ„ μŒμ†Œκ±°ν–ˆμŠ΅λ‹ˆλ‹€.(3λΆ„) ν•΄μ œν•˜λ €λ©΄ mute λͺ…λ Ήμ–΄λ₯Ό λ‹€μ‹œ μ‚¬μš©ν•˜μ„Έμš”.' ,successUnmute: 'πŸ”Š {targetName}#{ticketTarget}λ‹˜μ˜ μŒμ†Œκ±°λ₯Ό ν•΄μ œν–ˆμŠ΅λ‹ˆλ‹€.' ,muteAbusingWarn: '❌ ν•΄λ‹Ή ν”Œλ ˆμ΄μ–΄μ— λŒ€ν•΄ κ³§λ°”λ‘œ μŒμ†Œκ±°ν•  수 μ—†μŠ΅λ‹ˆλ‹€.(3λΆ„)' } @@ -153,6 +154,9 @@ export const command = { ,voteAutoNotify: 'πŸ—³οΈ ν˜„μž¬ μΆ”λ°© νˆ¬ν‘œκ°€ μ§„ν–‰μ€‘μž…λ‹ˆλ‹€ : {voteList}' } ,tier: 'πŸ“„ ν‹°μ–΄λŠ” λ ˆμ΄νŒ… μ μˆ˜μ— 따라 κ²°μ •λ©λ‹ˆλ‹€. λ ˆμ΄νŒ… μ μˆ˜λŠ” !stats λͺ…λ Ήμ–΄λ‘œ λ΄…λ‹ˆλ‹€.\nπŸ“‘ {tierAvatar9}{tierCutoff9} {tierAvatar8}{tierCutoff8} {tierAvatar7}{tierCutoff7} {tierAvatar6}{tierCutoff6} {tierAvatar5}{tierCutoff5} {tierAvatar4}{tierCutoff4} {tierAvatar3}{tierCutoff3} {tierAvatar2}{tierCutoff2} {tierAvatar1}{tierCutoff1}' + ,notice: { + _ErrorNoMessage: '❌ ν˜„μž¬ 곡지사항이 μ—†μŠ΅λ‹ˆλ‹€.' + } } export const funcUpdateAdmins = { diff --git a/core/lib/browser.hostconfig.ts b/core/lib/browser.hostconfig.ts index fb42467..852ee05 100644 --- a/core/lib/browser.hostconfig.ts +++ b/core/lib/browser.hostconfig.ts @@ -163,6 +163,7 @@ export interface BrowserHostRoomCommands { _helpMantier: string _helpMansuper: string _helpManadmin: string + _helpMannotice: string _listSubafk: string _listSubmute: string @@ -193,4 +194,5 @@ export interface BrowserHostRoomCommands { super: string vote: string tier: string + notice: string } diff --git a/core/lib/browser.ts b/core/lib/browser.ts index 0a2a596..6da7008 100644 --- a/core/lib/browser.ts +++ b/core/lib/browser.ts @@ -397,4 +397,29 @@ export class HeadlessBrowser { window.gameRoom.logger.i('system', `[Broadcast] ${message}`); }, message); } + + /** + * Get notice message. + * @param ruid Game room's UID + */ + public async getNotice(ruid: string): Promise { + return await this._PageContainer.get(ruid)!.evaluate(() => { + if(window.gameRoom.notice) { + return window.gameRoom.notice; + } else { + return undefined; + } + }) + } + + /** + * Set notice message. + * @param ruid Game room's UID + * @param message Notice Content + */ + public async setNotice(ruid: string, message: string): Promise { + await this._PageContainer.get(ruid)!.evaluate((message: string) => { + window.gameRoom.notice = message; + },message) + } } diff --git a/core/package-lock.json b/core/package-lock.json index 43fd14c..d6c9f80 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "haxbotron-core", - "version": "0.4.3", + "version": "0.4.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/core/package.json b/core/package.json index 3fcd182..10ee03c 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "haxbotron-core", - "version": "0.4.3", + "version": "0.4.4", "description": "Haxbotron is a headless host server application for Haxball.", "main": "out/app.js", "scripts": { diff --git a/core/react/component/Admin/Dashboard.tsx b/core/react/component/Admin/Dashboard.tsx index d241433..bbf9509 100644 --- a/core/react/component/Admin/Dashboard.tsx +++ b/core/react/component/Admin/Dashboard.tsx @@ -32,6 +32,7 @@ import RoomSuperAdmin from './RoomSuperAdmin'; import RoomInfo from './RoomInfo'; import RoomBanList from './RoomBanList'; import RoomPlayerList from './RoomPlayerList'; +import RoomSocial from './RoomSocial'; const drawerWidth = 240; @@ -230,6 +231,7 @@ function Dashboard({ match }: RouteComponentProps) { + diff --git a/core/react/component/Admin/RoomBanList.tsx b/core/react/component/Admin/RoomBanList.tsx index 4dfdd8d..c6bc9cc 100644 --- a/core/react/component/Admin/RoomBanList.tsx +++ b/core/react/component/Admin/RoomBanList.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import clsx from 'clsx'; import Box from '@material-ui/core/Box'; import Container from '@material-ui/core/Container'; diff --git a/core/react/component/Admin/RoomList.tsx b/core/react/component/Admin/RoomList.tsx index 15bd173..31e4c30 100644 --- a/core/react/component/Admin/RoomList.tsx +++ b/core/react/component/Admin/RoomList.tsx @@ -5,11 +5,7 @@ import Container from '@material-ui/core/Container'; import Grid from '@material-ui/core/Grid'; import Paper from '@material-ui/core/Paper'; import Copyright from '../common/Footer.Copyright'; -import Table from '@material-ui/core/Table'; -import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; -import TableHead from '@material-ui/core/TableHead'; -import TableRow from '@material-ui/core/TableRow'; +import { Table, TableBody, TableCell, TableHead, TableRow, TextField } from '@material-ui/core'; import Title from './common/Widget.Title'; import client from '../../lib/client'; import { WSocketContext } from '../../context/ws'; diff --git a/core/react/component/Admin/RoomLog.tsx b/core/react/component/Admin/RoomLog.tsx index e695126..3cef2f5 100644 --- a/core/react/component/Admin/RoomLog.tsx +++ b/core/react/component/Admin/RoomLog.tsx @@ -8,8 +8,9 @@ import Copyright from '../common/Footer.Copyright'; import Title from './common/Widget.Title'; import { WSocketContext } from '../../context/ws'; import { useParams } from 'react-router-dom'; -import { Button, TextField, Typography } from '@material-ui/core'; +import { Button, TextField } from '@material-ui/core'; import client from '../../lib/client'; +import Alert, { AlertColor } from '../common/Alert'; interface styleClass { styleClass: any @@ -40,6 +41,8 @@ export default function RoomLog({ styleClass }: styleClass) { const [recentLogMessage, setRecentLogMessage] = useState({} as LogMessage); const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); + const [broadcastMessage, setBroadcastMessage] = useState(''); const handleBroadcast = async (event: React.FormEvent) => { @@ -48,12 +51,14 @@ export default function RoomLog({ styleClass }: styleClass) { const result = await client.post(`/api/v1/room/${matchParams.ruid}/chat`, { message: broadcastMessage }); if (result.status === 201) { setFlashMessage('Successfully sent.'); + setAlertStatus('success'); setBroadcastMessage(''); setTimeout(() => { setFlashMessage(''); }, 3000); } } catch (error) { + setAlertStatus('error'); switch (error.response.status) { case 400: { setFlashMessage('No message.'); @@ -101,7 +106,7 @@ export default function RoomLog({ styleClass }: styleClass) { - {flashMessage} + {flashMessage && {flashMessage}} Broadcast
{ + if (timestamp === -1) return 'Permanent'; return new Date(timestamp).toLocaleString(); } -function OnlinePlayerRow(props: { row: Player }) { - const { row } = props; +function OnlinePlayerRow(props: { ruid: string, row: Player }) { + const { ruid, row } = props; const classes = useRowStyles(); const [open, setOpen] = useState(false); + const [newBan, setNewBan] = useState({ reason: '', seconds: 0 } as newBanFields); + + const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); + const convertTeamID = (teamID: number): string => { if (teamID === 1) return 'Red'; if (teamID === 2) return 'Blue'; @@ -76,11 +88,54 @@ function OnlinePlayerRow(props: { row: Player }) { const makePermissionsText = (admin: boolean, superAdmin: boolean): string => { let text: string[] = []; - if(admin) text.push('Admin'); - if(superAdmin) text.push('SuperAdmin'); + if (admin) text.push('Admin'); + if (superAdmin) text.push('SuperAdmin'); return text.join(','); } + const onChangeNewBan = (e: React.ChangeEvent) => { + const { name, value } = e.target; + if (name === "newbanseconds" && isNumber(parseInt(value))) { + setNewBan({ + ...newBan, + seconds: parseInt(value) + }); + } else { + setNewBan({ + ...newBan, + [name]: value + }); + } + } + + const handleAdd = async (event: React.FormEvent) => { + event.preventDefault(); + try { + const result = await client.delete(`/api/v1/room/${ruid}/player/${row.id}`, { + data: { + ban: false, + seconds: newBan.seconds, + message: newBan.reason + } + }); + if (result.status === 204) { + setFlashMessage('Successfully kicked.'); + setAlertStatus('success'); + setNewBan({ reason: '', seconds: 0 }); + setTimeout(() => { + setFlashMessage(''); + }, 3000); + } + } catch (error) { + //error.response.status + setFlashMessage('Failed to kick.'); + setAlertStatus('error'); + setTimeout(() => { + setFlashMessage(''); + }, 3000); + } + } + return ( @@ -98,6 +153,22 @@ function OnlinePlayerRow(props: { row: Player }) { + + {flashMessage && {flashMessage}} + + + + + + + + Information @@ -106,6 +177,8 @@ function OnlinePlayerRow(props: { row: Player }) { Permissions AFK + Mute + Mute Expiration Voted Join Date @@ -113,7 +186,9 @@ function OnlinePlayerRow(props: { row: Player }) { {makePermissionsText(row.admin, row.permissions.superadmin)} - {row.permissions.afkmode? 'Yes': 'No'} + {row.permissions.afkmode ? 'Yes' : 'No'} + {row.permissions.mute ? 'Yes' : 'No'} + {row.permissions.muteExpire === 0 ? '-' : convertDate(row.permissions.muteExpire)} {row.voteGet} {convertDate(row.entrytime.joinDate)} @@ -349,7 +424,7 @@ export default function RoomPlayerList({ styleClass }: styleClass) { {onlinePlayerList && onlinePlayerList.map((item, idx) => ( - + ))} diff --git a/core/react/component/Admin/RoomPower.tsx b/core/react/component/Admin/RoomPower.tsx index 5825406..42eace1 100644 --- a/core/react/component/Admin/RoomPower.tsx +++ b/core/react/component/Admin/RoomPower.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useState } from 'react'; import clsx from 'clsx'; import Box from '@material-ui/core/Box'; import Container from '@material-ui/core/Container'; @@ -7,8 +7,9 @@ import Paper from '@material-ui/core/Paper'; import Copyright from '../common/Footer.Copyright'; import Title from './common/Widget.Title'; import { useHistory, useParams } from 'react-router-dom'; -import { Button, Typography } from '@material-ui/core'; +import { Button } from '@material-ui/core'; import client from '../../lib/client'; +import Alert, { AlertColor } from '../common/Alert'; interface styleClass { styleClass: any @@ -29,6 +30,7 @@ export default function RoomPower({ styleClass }: styleClass) { const history = useHistory(); const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); const handleShutdownClick = async (e: React.MouseEvent) => { e.preventDefault(); @@ -37,9 +39,11 @@ export default function RoomPower({ styleClass }: styleClass) { const result = await client.delete('/api/v1/room/' + matchParams.ruid); if (result.status === 204) { setFlashMessage('Shutdown succeeded.'); + setAlertStatus('success'); history.push('/admin/roomlist'); } } catch (e) { + setAlertStatus('error'); switch (e.response.status) { case 401: { setFlashMessage('No permission.'); @@ -63,7 +67,7 @@ export default function RoomPower({ styleClass }: styleClass) { - {flashMessage} + {flashMessage && {flashMessage}} {matchParams.ruid} + + + + + Notice Message + + + + + + {noticeMessage} + + {noticeMessage && + + + + } + + + +
+
+
+
+
+ + + + + ); +} diff --git a/core/react/component/Admin/RoomSuperAdmin.tsx b/core/react/component/Admin/RoomSuperAdmin.tsx index 3dc6ae9..a1ec0e0 100644 --- a/core/react/component/Admin/RoomSuperAdmin.tsx +++ b/core/react/component/Admin/RoomSuperAdmin.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import clsx from 'clsx'; import Box from '@material-ui/core/Box'; import Container from '@material-ui/core/Container'; @@ -15,6 +15,7 @@ import client from '../../lib/client'; import { useParams } from 'react-router-dom'; import { Button, IconButton, TextField, Typography } from '@material-ui/core'; import BackspaceIcon from '@material-ui/icons/Backspace'; +import Alert, { AlertColor } from '../common/Alert'; interface styleClass { styleClass: any @@ -49,6 +50,8 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { const [superAdminKeyList, setSuperAdminKeyList] = useState([] as superAdminItem[]); const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); + const [newAdminKey, setNewAdminKey] = useState(''); const getSuperAdminKeyList = async () => { @@ -61,9 +64,11 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { } catch (error) { if (error.response.status === 404) { setFlashMessage('Failed to load list.'); + setAlertStatus('error'); setSuperAdminKeyList([]); } else { setFlashMessage('Unexpected error is caused. Please try again.'); + setAlertStatus('error'); } } } @@ -77,6 +82,7 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { const result = await client.delete(`/api/v1/superadmin/${matchParams.ruid}/${key}`); if (result.status === 204) { setFlashMessage('Successfully deleted.'); + setAlertStatus('success'); setTimeout(() => { setFlashMessage(''); }, 3000); @@ -84,6 +90,7 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { } catch (error) { //error.response.status setFlashMessage('Failed to delete the key.'); + setAlertStatus('error'); setTimeout(() => { setFlashMessage(''); }, 3000); @@ -105,6 +112,7 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { } as superAdminItem); if (result.status === 204) { setFlashMessage('Successfully added.'); + setAlertStatus('success'); setNewAdminKey(''); setTimeout(() => { setFlashMessage(''); @@ -113,6 +121,7 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { } catch (error) { //error.response.status setFlashMessage('Failed to register the key.'); + setAlertStatus('error'); setTimeout(() => { setFlashMessage(''); }, 3000); @@ -130,7 +139,7 @@ export default function RoomSuperAdmin({ styleClass }: styleClass) { - {flashMessage} + {flashMessage && {flashMessage}} Super Admin Keys Super admin is an ingame player who has adminstrative permissions.
diff --git a/core/react/component/Admin/SideMenu/RoomInfo.SideMenu.tsx b/core/react/component/Admin/SideMenu/RoomInfo.SideMenu.tsx index 77a6e8f..863dd8b 100644 --- a/core/react/component/Admin/SideMenu/RoomInfo.SideMenu.tsx +++ b/core/react/component/Admin/SideMenu/RoomInfo.SideMenu.tsx @@ -14,6 +14,7 @@ import CancelPresentationIcon from '@material-ui/icons/CancelPresentation'; import VpnKeyIcon from '@material-ui/icons/VpnKey'; import ListAltIcon from '@material-ui/icons/ListAlt'; import DnsIcon from '@material-ui/icons/Dns'; +import SendIcon from '@material-ui/icons/Send'; interface matchParams { ruid: string @@ -58,6 +59,12 @@ export default function RoomInfoSideMenu() { + + + + + + diff --git a/core/react/component/Install/Install.SignUp.tsx b/core/react/component/Install/Install.SignUp.tsx index 413dca7..9f74712 100644 --- a/core/react/component/Install/Install.SignUp.tsx +++ b/core/react/component/Install/Install.SignUp.tsx @@ -13,6 +13,7 @@ import Container from '@material-ui/core/Container'; import { Link as RouterLink, useHistory } from 'react-router-dom'; import client from '../../lib/client'; import Copyright from '../common/Footer.Copyright'; +import Alert, { AlertColor } from '../common/Alert'; interface checkProps { installed: boolean @@ -43,6 +44,7 @@ export default function SignUp({ installed }: checkProps) { const history = useHistory(); const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); const [adminAccount, setAdminAccount] = useState({ username: '', password: '' @@ -70,11 +72,13 @@ export default function SignUp({ installed }: checkProps) { const result = await client.post('/api/v1/init', { username, password }); if(result.status === 201) { setFlashMessage('Configuration succeeded.'); + setAlertStatus('success'); setTimeout(()=>{ history.push('/admin'); }, 5000); } } catch (e) { + setAlertStatus('error'); switch(e.response.status) { case 400: { setFlashMessage('Form is unfulfilled.'); @@ -92,6 +96,7 @@ export default function SignUp({ installed }: checkProps) { } } else { setFlashMessage('Form is unfulfilled.'); + setAlertStatus('error'); } } @@ -126,7 +131,7 @@ export default function SignUp({ installed }: checkProps) { Initial Configuration Sign up new admin account. - {flashMessage} + {flashMessage && {flashMessage}} diff --git a/core/react/component/SignIn.tsx b/core/react/component/SignIn.tsx index 11fc94d..c313d97 100644 --- a/core/react/component/SignIn.tsx +++ b/core/react/component/SignIn.tsx @@ -11,6 +11,7 @@ import Container from '@material-ui/core/Container'; import Copyright from './common/Footer.Copyright'; import client from '../lib/client'; import { useHistory } from 'react-router-dom'; +import Alert, { AlertColor } from './common/Alert'; const useStyles = makeStyles((theme) => ({ paper: { @@ -37,6 +38,7 @@ export default function SignIn() { const history = useHistory(); const [flashMessage, setFlashMessage] = useState(''); + const [alertStatus, setAlertStatus] = useState("success" as AlertColor); const [adminAccount, setAdminAccount] = useState({ username: '', password: '' @@ -64,12 +66,14 @@ export default function SignIn() { const result = await client.post('/api/v1/auth', { username, password }); if(result.status === 201) { setFlashMessage('Configuration succeeded.'); + setAlertStatus('success'); /*setTimeout(()=>{ history.push('/admin'); }, 1000);*/ history.push('/admin'); } } catch (e) { + setAlertStatus('error'); switch(e.response.status) { case 401: { setFlashMessage('Login failed.'); @@ -82,6 +86,7 @@ export default function SignIn() { } } } else { + setAlertStatus('error'); setFlashMessage('Form is unfulfilled.'); } } @@ -97,7 +102,7 @@ export default function SignIn() { Admin Account Login and start managing the server. - {flashMessage} + {flashMessage && {flashMessage}}