diff --git a/src/components/mod-tools/views/chatlog/ChatlogView.tsx b/src/components/mod-tools/views/chatlog/ChatlogView.tsx index 63e5201..118d9d8 100644 --- a/src/components/mod-tools/views/chatlog/ChatlogView.tsx +++ b/src/components/mod-tools/views/chatlog/ChatlogView.tsx @@ -1,7 +1,8 @@ import { ChatRecordData, CreateLinkEvent } from '@nitrots/nitro-renderer'; import { FC, useMemo } from 'react'; +import { FaCommentDots, FaDoorOpen, FaSignInAlt, FaTools } from 'react-icons/fa'; import { TryVisitRoom } from '../../../../api'; -import { Button, Column, Flex, Grid, InfiniteScroll, Text } from '../../../../common'; +import { Column, InfiniteScroll } from '../../../../common'; import { useModTools } from '../../../../hooks'; import { ChatlogRecord } from './ChatlogRecord'; @@ -43,46 +44,61 @@ export const ChatlogView: FC = props => return results; }, [ records ]); - const RoomInfo = (props: { roomId: number, roomName: string }) => - { - return ( - - { props.roomName } -
- - -
-
- ); - }; + const totalMessages = useMemo( + () => allRecords.filter(r => !r.isRoomInfo).length, + [ allRecords ] + ); + + const RoomInfo = (props: { roomId: number, roomName: string }) => ( +
+ +
{ props.roomName }
+
+ + +
+
+ ); + + const isEmpty = !records || records.length === 0 || totalMessages === 0; return ( - <> - - - -
Time
-
User
-
Message
-
-
- { (records && (records.length > 0)) && - - { - return ( - <> - { row.isRoomInfo && - } - { !row.isRoomInfo && - - { row.timestamp } - CreateLinkEvent(`mod-tools/open-user-info/${ row.habboId }`) }>{ row.username } - { row.message } - } - - ); - } } rows={ allRecords } /> } -
- + + {/* Column headers */} +
+
Time
+
User
+
Message
+
+ { isEmpty + ?
+ + No messages +
+ : + { + if(row.isRoomInfo) return ; + + return ( +
+ { row.timestamp } + + { row.message } +
+ ); + } } rows={ allRecords } /> } +
); }; diff --git a/src/components/mod-tools/views/room/ModToolsChatlogView.tsx b/src/components/mod-tools/views/room/ModToolsChatlogView.tsx index 65061be..f668301 100644 --- a/src/components/mod-tools/views/room/ModToolsChatlogView.tsx +++ b/src/components/mod-tools/views/room/ModToolsChatlogView.tsx @@ -26,11 +26,10 @@ export const ModToolsChatlogView: FC = props => if(!roomChatlog) return null; return ( - + - - { roomChatlog && - } + + ); diff --git a/src/components/mod-tools/views/room/ModToolsRoomView.tsx b/src/components/mod-tools/views/room/ModToolsRoomView.tsx index 37d9fc5..b178141 100644 --- a/src/components/mod-tools/views/room/ModToolsRoomView.tsx +++ b/src/components/mod-tools/views/room/ModToolsRoomView.tsx @@ -1,7 +1,8 @@ import { CreateLinkEvent, GetModeratorRoomInfoMessageComposer, ModerateRoomMessageComposer, ModeratorActionMessageComposer, ModeratorRoomInfoEvent } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; +import { FaBullhorn, FaCommentDots, FaDoorOpen, FaExclamationTriangle, FaSignInAlt, FaSync, FaUserShield, FaUsers } from 'react-icons/fa'; import { SendMessageComposer, TryVisitRoom } from '../../../../api'; -import { Button, Column, DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; +import { Button, DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; import { useMessageEvent } from '../../../../hooks'; interface ModToolsRoomViewProps @@ -25,7 +26,9 @@ export const ModToolsRoomView: FC = props => const [ changeRoomName, setChangeRoomName ] = useState(false); const [ message, setMessage ] = useState(''); - const handleClick = (action: string, value?: string) => + const refresh = () => SendMessageComposer(new GetModeratorRoomInfoMessageComposer(roomId)); + + const handleClick = (action: string) => { if(!action) return; @@ -66,55 +69,102 @@ export const ModToolsRoomView: FC = props => SendMessageComposer(new GetModeratorRoomInfoMessageComposer(roomId)); setInfoRequested(true); - }, [ roomId, infoRequested, setInfoRequested ]); + }, [ roomId, infoRequested ]); + + const isLoaded = loadedRoomId !== null; + const hasMessage = message.trim().length > 0; + const ownerPillClass = ownerInRoom + ? 'bg-emerald-100 text-emerald-700 border-emerald-200' + : 'bg-zinc-100 text-zinc-600 border-zinc-200'; + const ownerDotClass = ownerInRoom ? 'bg-emerald-500' : 'bg-zinc-400'; return ( - - onCloseClick() } /> + + onCloseClick() } /> - { name && -
- { name } + {/* Identity header */} +
+ +
+ { name || 'Loading…' } + Room #{ roomId }
- } -
- -
- Owner: - CreateLinkEvent(`mod-tools/open-user-info/${ ownerId }`) }>{ ownerName } + + + { ownerInRoom ? 'Owner here' : 'Owner away' } + + +
+ + {/* Stat strip */} +
+
+
+ Users
-
- Users in room: - { usersInRoom } +
{ usersInRoom }
+
+
+
+ Owner
-
- Owner here: - { ownerInRoom ? 'Yes' : 'No' } +
ownerId && CreateLinkEvent(`mod-tools/open-user-info/${ ownerId }`) } + title={ ownerName ? `Open ${ ownerName }'s info` : '' }> + { ownerName || '—' }
- -
- -
- -
+ + {/* Quick actions */} +
+ + +
+ + {/* Moderate panel */} +
+
+ Moderate room +
+
-
+ Kick everyone out + +
-
+ Enable the doorbell + + + -
- -
diff --git a/src/components/mod-tools/views/tickets/CfhChatlogView.tsx b/src/components/mod-tools/views/tickets/CfhChatlogView.tsx index 33cc52d..d691c0f 100644 --- a/src/components/mod-tools/views/tickets/CfhChatlogView.tsx +++ b/src/components/mod-tools/views/tickets/CfhChatlogView.tsx @@ -1,7 +1,8 @@ import { CfhChatlogData, CfhChatlogEvent, GetCfhChatlogMessageComposer } from '@nitrots/nitro-renderer'; import { FC } from 'react'; +import { FaSpinner } from 'react-icons/fa'; import { useNitroQuery } from '../../../../api/nitro-query'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; +import { DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { ChatlogView } from '../chatlog/ChatlogView'; interface CfhChatlogViewProps @@ -24,10 +25,15 @@ export const CfhChatlogView: FC = props => }); return ( - - - - { chatlogData && } + + + + { chatlogData + ? + :
+ + Loading chatlog… +
}
); diff --git a/src/components/mod-tools/views/tickets/ModToolsIssueInfoView.tsx b/src/components/mod-tools/views/tickets/ModToolsIssueInfoView.tsx index 7444a73..f5fb954 100644 --- a/src/components/mod-tools/views/tickets/ModToolsIssueInfoView.tsx +++ b/src/components/mod-tools/views/tickets/ModToolsIssueInfoView.tsx @@ -1,7 +1,8 @@ import { CloseIssuesMessageComposer, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useState } from 'react'; +import { FaBan, FaCheck, FaCommentDots, FaExternalLinkAlt, FaSignOutAlt, FaTrashAlt } from 'react-icons/fa'; import { GetIssueCategoryName, LocalizeText, SendMessageComposer } from '../../../../api'; -import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; +import { Button, DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { useModTools } from '../../../../hooks'; import { CfhChatlogView } from './CfhChatlogView'; @@ -11,76 +12,102 @@ interface IssueInfoViewProps onIssueInfoClosed(issueId: number): void; } +const Field: FC<{ label: string; children: React.ReactNode }> = ({ label, children }) => ( + <> +
{ label }
+
{ children || }
+ +); + export const ModToolsIssueInfoView: FC = props => { const { issueId = null, onIssueInfoClosed = null } = props; - const [ cfhChatlogOpen, setcfhChatlogOpen ] = useState(false); + const [ cfhChatlogOpen, setCfhChatlogOpen ] = useState(false); const { tickets = [], openUserInfo = null } = useModTools(); const ticket = tickets.find(issue => (issue.issueId === issueId)); - const releaseIssue = (issueId: number) => + const releaseIssue = () => { SendMessageComposer(new ReleaseIssuesMessageComposer([ issueId ])); - onIssueInfoClosed(issueId); }; const closeIssue = (resolutionType: number) => { SendMessageComposer(new CloseIssuesMessageComposer([ issueId ], resolutionType)); - onIssueInfoClosed(issueId); }; + if(!ticket) return null; + return ( <> - - onIssueInfoClosed(issueId) } /> - - Issue Information - - - - - - - - - - - - - - - - - - - - - - - - - -
Source{ GetIssueCategoryName(ticket.categoryId) }
Category{ LocalizeText('help.cfh.topic.' + ticket.reportedCategoryId) }
Description{ ticket.message }
Caller - openUserInfo(ticket.reporterUserId) }>{ ticket.reporterUserName } -
Reported User - openUserInfo(ticket.reportedUserId) }>{ ticket.reportedUserName } -
-
- - - - - - - -
+ + onIssueInfoClosed(issueId) } /> + + {/* Issue header */} +
+ +
+
Issue #{ issueId }
+
{ GetIssueCategoryName(ticket.categoryId) }
+
+ + { LocalizeText('help.cfh.topic.' + ticket.reportedCategoryId) } + +
+ + {/* Details */} +
+
Details
+
+ { GetIssueCategoryName(ticket.categoryId) } + { LocalizeText('help.cfh.topic.' + ticket.reportedCategoryId) } + { ticket.message } + + + + + + +
+
+ + {/* Tools */} + + + {/* Resolution buttons */} +
+
Resolve as
+
+ + + +
+ +
{ cfhChatlogOpen && - setcfhChatlogOpen(false) }/> } + setCfhChatlogOpen(false) } /> } ); }; diff --git a/src/components/mod-tools/views/tickets/ModToolsMyIssuesTabView.tsx b/src/components/mod-tools/views/tickets/ModToolsMyIssuesTabView.tsx index 9aaa441..3aa9025 100644 --- a/src/components/mod-tools/views/tickets/ModToolsMyIssuesTabView.tsx +++ b/src/components/mod-tools/views/tickets/ModToolsMyIssuesTabView.tsx @@ -1,7 +1,7 @@ import { IssueMessageData, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useRef } from 'react'; -import { SendMessageComposer } from '../../../../api'; -import { Button, Column, Grid } from '../../../../common'; +import { FaClock, FaInbox, FaSignOutAlt, FaTools, FaUser } from 'react-icons/fa'; +import { GetIssueCategoryName, SendMessageComposer } from '../../../../api'; interface ModToolsMyIssuesTabViewProps { @@ -24,35 +24,45 @@ export const ModToolsMyIssuesTabView: FC = props = setTimeout(() => pendingReleasesRef.current.delete(issueId), 2000); }; + const isEmpty = !myIssues || myIssues.length === 0; + return ( - - - -
Type
-
Room/Player
-
Opened
-
-
-
-
- - { myIssues && (myIssues.length > 0) && myIssues.map(issue => - { - return ( - -
{ issue.categoryId }
-
{ issue.reportedUserName }
-
{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }
-
- -
-
- -
-
- ); - }) } -
-
+
+
+
Type
+
Reported
+
Opened
+
+
+
+ { isEmpty + ?
+ + No issues picked by you +
+ :
+ { myIssues.map(issue => ( +
+ + { GetIssueCategoryName(issue.categoryId) } + + { issue.reportedUserName } + + { new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() } + + + +
+ )) } +
} +
); }; diff --git a/src/components/mod-tools/views/tickets/ModToolsOpenIssuesTabView.tsx b/src/components/mod-tools/views/tickets/ModToolsOpenIssuesTabView.tsx index 387580b..5fb4596 100644 --- a/src/components/mod-tools/views/tickets/ModToolsOpenIssuesTabView.tsx +++ b/src/components/mod-tools/views/tickets/ModToolsOpenIssuesTabView.tsx @@ -1,7 +1,7 @@ import { IssueMessageData, PickIssuesMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useRef } from 'react'; -import { SendMessageComposer } from '../../../../api'; -import { Button, Column, Grid } from '../../../../common'; +import { FaClock, FaHandPointer, FaInbox, FaUser } from 'react-icons/fa'; +import { GetIssueCategoryName, SendMessageComposer } from '../../../../api'; interface ModToolsOpenIssuesTabViewProps { @@ -23,31 +23,39 @@ export const ModToolsOpenIssuesTabView: FC = pro setTimeout(() => pendingPicksRef.current.delete(issueId), 2000); }; + const isEmpty = !openIssues || openIssues.length === 0; + return ( - - - -
Type
-
Room/Player
-
Opened
-
-
-
- - { openIssues && (openIssues.length > 0) && openIssues.map(issue => - { - return ( - -
{ issue.categoryId }
-
{ issue.reportedUserName }
-
{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }
-
- -
-
- ); - }) } -
-
+
+
+
Type
+
Reported
+
Opened
+
+
+ { isEmpty + ?
+ + No open issues +
+ :
+ { openIssues.map(issue => ( +
+ + { GetIssueCategoryName(issue.categoryId) } + + { issue.reportedUserName } + + { new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() } + + +
+ )) } +
} +
); }; diff --git a/src/components/mod-tools/views/tickets/ModToolsPickedIssuesTabView.tsx b/src/components/mod-tools/views/tickets/ModToolsPickedIssuesTabView.tsx index ca6003e..8c22b10 100644 --- a/src/components/mod-tools/views/tickets/ModToolsPickedIssuesTabView.tsx +++ b/src/components/mod-tools/views/tickets/ModToolsPickedIssuesTabView.tsx @@ -1,6 +1,7 @@ import { IssueMessageData } from '@nitrots/nitro-renderer'; import { FC } from 'react'; -import { Column, Grid } from '../../../../common'; +import { FaClock, FaInbox, FaUser, FaUserShield } from 'react-icons/fa'; +import { GetIssueCategoryName } from '../../../../api'; interface ModToolsPickedIssuesTabViewProps { @@ -10,30 +11,35 @@ interface ModToolsPickedIssuesTabViewProps export const ModToolsPickedIssuesTabView: FC = props => { const { pickedIssues = null } = props; + const isEmpty = !pickedIssues || pickedIssues.length === 0; return ( - - - -
Type
-
Room/Player
-
Opened
-
Picker
-
-
- - { pickedIssues && (pickedIssues.length > 0) && pickedIssues.map(issue => - { - return ( - -
{ issue.categoryId }
-
{ issue.reportedUserName }
-
{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }
-
{ issue.pickerUserName }
-
- ); - }) } -
-
+
+
+
Type
+
Reported
+
Opened
+
Picker
+
+ { isEmpty + ?
+ + No picked issues +
+ :
+ { pickedIssues.map(issue => ( +
+ + { GetIssueCategoryName(issue.categoryId) } + + { issue.reportedUserName } + + { new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() } + + { issue.pickerUserName } +
+ )) } +
} +
); }; diff --git a/src/components/mod-tools/views/tickets/ModToolsTicketsView.tsx b/src/components/mod-tools/views/tickets/ModToolsTicketsView.tsx index ab7ac35..5bb9c45 100644 --- a/src/components/mod-tools/views/tickets/ModToolsTicketsView.tsx +++ b/src/components/mod-tools/views/tickets/ModToolsTicketsView.tsx @@ -1,5 +1,6 @@ import { GetSessionDataManager, IssueMessageData } from '@nitrots/nitro-renderer'; -import { FC, useState } from 'react'; +import { FC, useMemo, useState } from 'react'; +import { FaCheckSquare, FaListUl, FaUserCheck } from 'react-icons/fa'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../common'; import { useModTools } from '../../../../hooks'; import { ModToolsIssueInfoView } from './ModToolsIssueInfoView'; @@ -12,11 +13,30 @@ interface ModToolsTicketsViewProps onCloseClick: () => void; } -const TABS: string[] = [ - 'Open Issues', - 'My Issues', - 'Picked Issues' -]; +interface TabBadgeProps +{ + label: string; + count: number; + icon: React.ReactNode; + tone: 'amber' | 'sky' | 'zinc'; +} + +const TONE_MAP: Record = { + amber: 'bg-amber-500 text-white', + sky: 'bg-sky-500 text-white', + zinc: 'bg-zinc-400 text-white' +}; + +const TabLabel: FC = ({ label, count, icon, tone }) => ( + + { icon } + { label } + { count > 0 && + + { count > 99 ? '99+' : count } + } + +); export const ModToolsTicketsView: FC = props => { @@ -25,9 +45,15 @@ export const ModToolsTicketsView: FC = props => const [ issueInfoWindows, setIssueInfoWindows ] = useState([]); const { tickets = [] } = useModTools(); - const openIssues = tickets.filter(issue => issue.state === IssueMessageData.STATE_OPEN); - const myIssues = tickets.filter(issue => (issue.state === IssueMessageData.STATE_PICKED) && (issue.pickerUserId === GetSessionDataManager().userId)); - const pickedIssues = tickets.filter(issue => issue.state === IssueMessageData.STATE_PICKED); + const { openIssues, myIssues, pickedIssues } = useMemo(() => + { + const ownId = GetSessionDataManager()?.userId; + return { + openIssues: tickets.filter(issue => issue.state === IssueMessageData.STATE_OPEN), + myIssues: tickets.filter(issue => (issue.state === IssueMessageData.STATE_PICKED) && (issue.pickerUserId === ownId)), + pickedIssues: tickets.filter(issue => issue.state === IssueMessageData.STATE_PICKED) + }; + }, [ tickets ]); const closeIssue = (issueId: number) => { @@ -56,32 +82,34 @@ export const ModToolsTicketsView: FC = props => }); }; - const CurrentTabComponent = () => + const renderTab = () => { switch(currentTab) { - case 0: return ; - case 1: return ; - case 2: return ; + case 0: return ; + case 1: return ; + case 2: return ; } - return null; }; return ( <> - + - { TABS.map((tab, index) => - { - return ( setCurrentTab(index) }> - { tab } - ); - }) } + setCurrentTab(0) }> + } tone="amber" /> + + setCurrentTab(1) }> + } tone="sky" /> + + setCurrentTab(2) }> + } tone="zinc" /> + - + { renderTab() } { issueInfoWindows && (issueInfoWindows.length > 0) && issueInfoWindows.map(issueId => ) } diff --git a/src/components/mod-tools/views/user/ModToolsUserChatlogView.tsx b/src/components/mod-tools/views/user/ModToolsUserChatlogView.tsx index acae308..a18107e 100644 --- a/src/components/mod-tools/views/user/ModToolsUserChatlogView.tsx +++ b/src/components/mod-tools/views/user/ModToolsUserChatlogView.tsx @@ -1,5 +1,6 @@ import { ChatRecordData, GetUserChatlogMessageComposer, UserChatlogEvent } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; +import { FaSpinner } from 'react-icons/fa'; import { SendMessageComposer } from '../../../../api'; import { DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { useMessageEvent } from '../../../../hooks'; @@ -33,11 +34,15 @@ export const ModToolsUserChatlogView: FC = props = }, [ userId ]); return ( - - - - { userChatlog && - } + + + + { userChatlog + ? + :
+ + Loading chatlog… +
}
); diff --git a/src/components/mod-tools/views/user/ModToolsUserModActionView.tsx b/src/components/mod-tools/views/user/ModToolsUserModActionView.tsx index 1bea10a..97b969f 100644 --- a/src/components/mod-tools/views/user/ModToolsUserModActionView.tsx +++ b/src/components/mod-tools/views/user/ModToolsUserModActionView.tsx @@ -1,7 +1,8 @@ import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useMemo, useRef, useState } from 'react'; +import { FaBan, FaBolt, FaEnvelope, FaExclamationTriangle, FaGavel, FaUserSlash, FaVolumeMute } from 'react-icons/fa'; import { ISelectedUser, LocalizeText, ModActionDefinition, NotificationAlertType, SendMessageComposer } from '../../../../api'; -import { Button, DraggableWindowPosition, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common'; +import { Button, DraggableWindowPosition, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { useModTools, useNotification } from '../../../../hooks'; interface ModToolsUserModActionViewProps @@ -25,6 +26,24 @@ const MOD_ACTION_DEFINITIONS = [ new ModActionDefinition(105, 'Message', ModActionDefinition.MESSAGE, 0, 0), ]; +const ACTION_ICONS: Record = { + [ModActionDefinition.ALERT]: , + [ModActionDefinition.MUTE]: , + [ModActionDefinition.BAN]: , + [ModActionDefinition.KICK]: , + [ModActionDefinition.TRADE_LOCK]: , + [ModActionDefinition.MESSAGE]: , +}; + +const ACTION_TONE: Record = { + [ModActionDefinition.ALERT]: 'bg-amber-100 text-amber-800 border-amber-200', + [ModActionDefinition.MUTE]: 'bg-sky-100 text-sky-800 border-sky-200', + [ModActionDefinition.BAN]: 'bg-rose-100 text-rose-800 border-rose-200', + [ModActionDefinition.KICK]: 'bg-orange-100 text-orange-800 border-orange-200', + [ModActionDefinition.TRADE_LOCK]: 'bg-fuchsia-100 text-fuchsia-800 border-fuchsia-200', + [ModActionDefinition.MESSAGE]: 'bg-zinc-100 text-zinc-800 border-zinc-200', +}; + export const ModToolsUserModActionView: FC = props => { const { user = null, onCloseClick = null } = props; @@ -50,26 +69,20 @@ export const ModToolsUserModActionView: FC = pro return values; }, [ cfhCategories ]); - const sendAlert = (message: string) => simpleAlert(message, NotificationAlertType.DEFAULT, null, null, 'Error'); + const sendAlert = (m: string) => simpleAlert(m, NotificationAlertType.DEFAULT, null, null, 'Error'); const sendDefaultSanction = () => { if(isSendingRef.current) return; - let errorMessage: string = null; - const category = topics[selectedTopic]; - if(selectedTopic === -1) errorMessage = 'You must select a CFH topic'; - - if(errorMessage) return sendAlert(errorMessage); + if(selectedTopic === -1) return sendAlert('You must select a CFH topic'); const messageOrDefault = (message.trim().length === 0) ? LocalizeText(`help.cfh.topic.${ category.id }`) : message; isSendingRef.current = true; - SendMessageComposer(new DefaultSanctionMessageComposer(user.userId, selectedTopic, messageOrDefault)); - onCloseClick(); }; @@ -78,7 +91,6 @@ export const ModToolsUserModActionView: FC = pro if(isSendingRef.current) return; let errorMessage: string = null; - const category = topics[selectedTopic]; const sanction = MOD_ACTION_DEFINITIONS[selectedAction]; @@ -87,25 +99,14 @@ export const ModToolsUserModActionView: FC = pro else if(!category) errorMessage = 'You must select a CFH topic'; else if(!sanction) errorMessage = 'You must select a sanction'; - if(errorMessage) - { - sendAlert(errorMessage); - - return; - } + if(errorMessage) return sendAlert(errorMessage); const messageOrDefault = (message.trim().length === 0) ? LocalizeText(`help.cfh.topic.${ category.id }`) : message; switch(sanction.actionType) { case ModActionDefinition.ALERT: { - if(!settings.alertPermission) - { - sendAlert('You have insufficient permissions'); - - return; - } - + if(!settings.alertPermission) return sendAlert('You have insufficient permissions'); SendMessageComposer(new ModAlertMessageComposer(user.userId, messageOrDefault, category.id)); break; } @@ -113,72 +114,106 @@ export const ModToolsUserModActionView: FC = pro SendMessageComposer(new ModMuteMessageComposer(user.userId, messageOrDefault, category.id)); break; case ModActionDefinition.BAN: { - if(!settings.banPermission) - { - sendAlert('You have insufficient permissions'); - - return; - } - + if(!settings.banPermission) return sendAlert('You have insufficient permissions'); SendMessageComposer(new ModBanMessageComposer(user.userId, messageOrDefault, category.id, selectedAction, (sanction.actionId === 106))); break; } case ModActionDefinition.KICK: { - if(!settings.kickPermission) - { - sendAlert('You have insufficient permissions'); - return; - } - + if(!settings.kickPermission) return sendAlert('You have insufficient permissions'); SendMessageComposer(new ModKickMessageComposer(user.userId, messageOrDefault, category.id)); break; } case ModActionDefinition.TRADE_LOCK: { const numSeconds = (sanction.actionLengthHours * 60); - SendMessageComposer(new ModTradingLockMessageComposer(user.userId, messageOrDefault, numSeconds, category.id)); break; } case ModActionDefinition.MESSAGE: { - if(message.trim().length === 0) - { - sendAlert('Please write a message to user'); - - return; - } - + if(message.trim().length === 0) return sendAlert('Please write a message to user'); SendMessageComposer(new ModMessageMessageComposer(user.userId, message, category.id)); break; } } isSendingRef.current = true; - onCloseClick(); }; if(!user) return null; + const selectedSanction = selectedAction >= 0 ? MOD_ACTION_DEFINITIONS[selectedAction] : null; + const selectedTopicName = selectedTopic >= 0 && topics[selectedTopic] ? LocalizeText('help.cfh.topic.' + topics[selectedTopic].id) : null; + const sanctionTone = selectedSanction ? ACTION_TONE[selectedSanction.actionType] : ''; + const sanctionIcon = selectedSanction ? ACTION_ICONS[selectedSanction.actionType] : null; + const canSubmit = (selectedTopic !== -1); + return ( - - onCloseClick() } /> - - - -
- Optional message type, overrides default - - + + {/* Recipient header */} +
+ +
+
Message to
+
+ + { user.username } +
+
+
+ + {/* Body */} +
+ +