mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Update mod tools layout
This commit is contained in:
parent
0c01bb2e37
commit
8aa7279e7c
@ -69,6 +69,8 @@ $camera-checkout-width: 350px;
|
||||
|
||||
$room-info-width: 325px;
|
||||
|
||||
$nitro-mod-tools-width: 175px;
|
||||
|
||||
.nitro-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -732,7 +732,7 @@ $table-cell-padding-x-sm: .25rem !default;
|
||||
|
||||
$table-cell-vertical-align: top !default;
|
||||
|
||||
$table-color: $body-color !default;
|
||||
$table-color: $black !default;
|
||||
$table-bg: transparent !default;
|
||||
$table-accent-bg: transparent !default;
|
||||
|
||||
|
@ -90,3 +90,10 @@ ul {
|
||||
.flex-basis-max-content {
|
||||
flex-basis: max-content;
|
||||
}
|
||||
|
||||
.striped-children {
|
||||
|
||||
> :nth-child(1) {
|
||||
background-color: $table-striped-bg;
|
||||
}
|
||||
}
|
||||
|
20
src/components/mod-tools/ModToolsContext.tsx
Normal file
20
src/components/mod-tools/ModToolsContext.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { createContext, Dispatch, FC, ProviderProps, useContext } from 'react';
|
||||
import { IModToolsAction, IModToolsState } from './reducers/ModToolsReducer';
|
||||
|
||||
export interface IModToolsContext
|
||||
{
|
||||
modToolsState: IModToolsState;
|
||||
dispatchModToolsState: Dispatch<IModToolsAction>;
|
||||
}
|
||||
|
||||
const ModToolsContext = createContext<IModToolsContext>({
|
||||
modToolsState: null,
|
||||
dispatchModToolsState: null
|
||||
});
|
||||
|
||||
export const ModToolsContextProvider: FC<ProviderProps<IModToolsContext>> = props =>
|
||||
{
|
||||
return <ModToolsContext.Provider value={ props.value }>{ props.children }</ModToolsContext.Provider>
|
||||
}
|
||||
|
||||
export const useModToolsContext = () => useContext(ModToolsContext);
|
@ -10,7 +10,7 @@ import { CreateMessageHook, useRoomEngineEvent, useUiEvent } from '../../hooks';
|
||||
import { NotificationAlertType } from '../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../views/notification-center/common/NotificationUtilities';
|
||||
import { SetCfhCategories } from './common/GetCFHCategories';
|
||||
import { useModToolsContext } from './context/ModToolsContext';
|
||||
import { useModToolsContext } from './ModToolsContext';
|
||||
import { ModToolsActions } from './reducers/ModToolsReducer';
|
||||
|
||||
export const ModToolsMessageHandler: FC<{}> = props =>
|
||||
|
@ -1,9 +1,93 @@
|
||||
.nitro-mod-tools {
|
||||
width: 200px;
|
||||
width: $nitro-mod-tools-width;
|
||||
}
|
||||
|
||||
@import './views/room/room-tools/ModToolsRoomView';
|
||||
@import './views/chatlog/ChatlogView';
|
||||
@import './views/user/user-info/ModToolsUserView';
|
||||
@import './views/user/user-room-visits/ModToolsUserRoomVisitsView';
|
||||
@import './views/tickets/ModToolsTicketView';
|
||||
.nitro-mod-tools-room {
|
||||
width: 240px;
|
||||
|
||||
.username {
|
||||
color: #1E7295;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-mod-tools-user {
|
||||
width: 350px;
|
||||
height: 370px;
|
||||
|
||||
.username {
|
||||
color: #1E7295;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: $black;
|
||||
|
||||
> :not(caption) > * > * {
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
&.table-striped > tbody > tr:nth-of-type(odd) {
|
||||
color: $black;
|
||||
background: rgba(0, 0, 0, .05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-mod-tools-user-visits {
|
||||
min-width: 300px;
|
||||
|
||||
.user-visits {
|
||||
min-height: 200px;
|
||||
|
||||
.roomvisits-container {
|
||||
div.room-visit {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-mod-tools-chatlog {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.nitro-mod-tools-user-visits {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.nitro-mod-tools-tickets {
|
||||
width: 400px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.nitro-mod-tools-handle-issue {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.nitro-mod-tools-chatlog,
|
||||
.nitro-mod-tools-user-visits {
|
||||
|
||||
.log-container {
|
||||
min-height: 200px;
|
||||
|
||||
.log-entry-container {
|
||||
|
||||
.log-entry {
|
||||
|
||||
&.highlighted {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { RoomEngineObjectEvent, RoomObjectCategory } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useReducer, useState } from 'react';
|
||||
import { GetRoomSession } from '../../api';
|
||||
import { Button } from '../../common';
|
||||
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
||||
import { ModToolsOpenRoomChatlogEvent } from '../../events/mod-tools/ModToolsOpenRoomChatlogEvent';
|
||||
import { ModToolsOpenRoomInfoEvent } from '../../events/mod-tools/ModToolsOpenRoomInfoEvent';
|
||||
@ -8,23 +10,23 @@ import { ModToolsOpenUserInfoEvent } from '../../events/mod-tools/ModToolsOpenUs
|
||||
import { useRoomEngineEvent } from '../../hooks/events';
|
||||
import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
|
||||
import { ModToolsContextProvider } from './context/ModToolsContext';
|
||||
import { ModToolsContextProvider } from './ModToolsContext';
|
||||
import { ModToolsMessageHandler } from './ModToolsMessageHandler';
|
||||
import { initialModTools, ModToolsActions, ModToolsReducer } from './reducers/ModToolsReducer';
|
||||
import { ISelectedUser } from './utils/ISelectedUser';
|
||||
import { ModToolsChatlogView } from './views/room/room-chatlog/ModToolsChatlogView';
|
||||
import { ModToolsRoomView } from './views/room/room-tools/ModToolsRoomView';
|
||||
import { ModToolsChatlogView } from './views/room/ModToolsChatlogView';
|
||||
import { ModToolsRoomView } from './views/room/ModToolsRoomView';
|
||||
import { ModToolsTicketsView } from './views/tickets/ModToolsTicketsView';
|
||||
import { ModToolsUserChatlogView } from './views/user/user-chatlog/ModToolsUserChatlogView';
|
||||
import { ModToolsUserView } from './views/user/user-info/ModToolsUserView';
|
||||
import { ModToolsUserChatlogView } from './views/user/ModToolsUserChatlogView';
|
||||
import { ModToolsUserView } from './views/user/ModToolsUserView';
|
||||
|
||||
export const ModToolsView: FC<{}> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ modToolsState, dispatchModToolsState ] = useReducer(ModToolsReducer, initialModTools);
|
||||
const { currentRoomId = null, openRooms = null, openRoomChatlogs = null, openUserChatlogs = null, openUserInfo = null } = modToolsState;
|
||||
const [ selectedUser, setSelectedUser] = useState<ISelectedUser>(null);
|
||||
const [ isTicketsVisible, setIsTicketsVisible ] = useState(false);
|
||||
const [ modToolsState, dispatchModToolsState ] = useReducer(ModToolsReducer, initialModTools);
|
||||
const { currentRoomId = null, openRooms = null, openRoomChatlogs = null, openUserChatlogs = null, openUserInfo = null } = modToolsState;
|
||||
|
||||
const onModToolsEvent = useCallback((event: ModToolsEvent) =>
|
||||
{
|
||||
@ -190,11 +192,19 @@ export const ModToolsView: FC<{}> = props =>
|
||||
{ isVisible &&
|
||||
<NitroCardView uniqueKey="mod-tools" className="nitro-mod-tools" simple={ false }>
|
||||
<NitroCardHeaderView headerText={ 'Mod Tools' } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<button className="btn btn-primary btn-sm w-100 mb-2" onClick={ () => handleClick('toggle_room') } disabled={ !currentRoomId }><i className="fas fa-home"></i> Room Tool</button>
|
||||
<button className="btn btn-primary btn-sm w-100 mb-2" onClick={ () => handleClick('toggle_room_chatlog') } disabled={ !currentRoomId }><i className="fas fa-comments"></i> Chatlog Tool</button>
|
||||
<button className="btn btn-primary btn-sm w-100 mb-2" onClick={ () => handleClick('toggle_user_info') } disabled={ !selectedUser }><i className="fas fa-user"></i> User: { selectedUser ? selectedUser.username : '' }</button>
|
||||
<button className="btn btn-primary btn-sm w-100" onClick={ () => setIsTicketsVisible(value => !value) }><i className="fas fa-exclamation-circle"></i> Report Tool</button>
|
||||
<NitroCardContentView className="text-black" gap={ 1 }>
|
||||
<Button gap={ 1 } onClick={ event => handleClick('toggle_room') } disabled={ !currentRoomId }>
|
||||
<FontAwesomeIcon icon="home" /> Room Tool
|
||||
</Button>
|
||||
<Button gap={ 1 } onClick={ event => handleClick('toggle_room_chatlog') } disabled={ !currentRoomId }>
|
||||
<FontAwesomeIcon icon="comments" /> Chatlog Tool
|
||||
</Button>
|
||||
<Button gap={ 1 } onClick={ () => handleClick('toggle_user_info') } disabled={ !selectedUser }>
|
||||
<FontAwesomeIcon icon="user" /> User: { selectedUser ? selectedUser.username : '' }
|
||||
</Button>
|
||||
<Button gap={ 1 } onClick={ () => setIsTicketsVisible(value => !value) }>
|
||||
<FontAwesomeIcon icon="exclamation-circle" /> Report Tool
|
||||
</Button>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
{ openRooms && openRooms.map(roomId =>
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { createContext, FC, useContext } from 'react';
|
||||
import { IModToolsContext, ModToolsContextProps } from './ModToolsContext.types';
|
||||
|
||||
const ModToolsContext = createContext<IModToolsContext>({
|
||||
modToolsState: null,
|
||||
dispatchModToolsState: null
|
||||
});
|
||||
|
||||
export const ModToolsContextProvider: FC<ModToolsContextProps> = props =>
|
||||
{
|
||||
return <ModToolsContext.Provider value={ props.value }>{ props.children }</ModToolsContext.Provider>
|
||||
}
|
||||
|
||||
export const useModToolsContext = () => useContext(ModToolsContext);
|
@ -1,13 +0,0 @@
|
||||
import { Dispatch, ProviderProps } from 'react';
|
||||
import { IModToolsAction, IModToolsState } from '../reducers/ModToolsReducer';
|
||||
|
||||
export interface IModToolsContext
|
||||
{
|
||||
modToolsState: IModToolsState;
|
||||
dispatchModToolsState: Dispatch<IModToolsAction>;
|
||||
}
|
||||
|
||||
export interface ModToolsContextProps extends ProviderProps<IModToolsContext>
|
||||
{
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
.chatlog-messages {
|
||||
color: $black;
|
||||
min-width: 400px;
|
||||
|
||||
$username-col-width: 100px;
|
||||
|
||||
.username-label {
|
||||
width: $username-col-width;
|
||||
}
|
||||
|
||||
.chatlog {
|
||||
min-height: 200px;
|
||||
|
||||
.chatlog-container {
|
||||
color: $black;
|
||||
|
||||
div.chatlog-entry {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
.username {
|
||||
color: #1E7295;
|
||||
text-decoration: underline;
|
||||
width: $username-col-width;
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
border: 1px solid $red;
|
||||
}
|
||||
|
||||
.message {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.room-info {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
background: rgba(0, 0, 0, .05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +1,87 @@
|
||||
import { ChatlineData, ChatRecordData, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List, ListRowProps, ListRowRenderer } from 'react-virtualized';
|
||||
import { ChatRecordData, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { CSSProperties, FC, Key, useCallback } from 'react';
|
||||
import { AutoSizer, CellMeasurer, CellMeasurerCache, List, ListRowProps } from 'react-virtualized';
|
||||
import { TryVisitRoom } from '../../../../api';
|
||||
import { Base, Button, Column, Flex, Grid, Text } from '../../../../common';
|
||||
import { ModToolsOpenRoomInfoEvent } from '../../../../events/mod-tools/ModToolsOpenRoomInfoEvent';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../../hooks';
|
||||
import { ChatlogViewProps } from './ChatlogView.types';
|
||||
|
||||
interface ChatlogViewProps
|
||||
{
|
||||
records: ChatRecordData[];
|
||||
}
|
||||
|
||||
export const ChatlogView: FC<ChatlogViewProps> = props =>
|
||||
{
|
||||
const { records = null } = props;
|
||||
|
||||
const simpleRowRenderer: ListRowRenderer = (props: ListRowProps) =>
|
||||
const rowRenderer = (props: ListRowProps) =>
|
||||
{
|
||||
const item = records[0].chatlog[props.index];
|
||||
let chatlogEntry = records[0].chatlog[props.index];
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
cache={cache}
|
||||
columnIndex={0}
|
||||
key={props.key}
|
||||
parent={props.parent}
|
||||
rowIndex={props.index}
|
||||
cache={ cache }
|
||||
columnIndex={ 0 }
|
||||
key={ props.key }
|
||||
parent={ props.parent }
|
||||
rowIndex={ props.index }
|
||||
>
|
||||
<div key={props.key} style={props.style} className={'row chatlog-entry justify-content-start ' + (item.hasHighlighting ? 'highlighted' : '')}>
|
||||
<div className="col-auto text-center">{item.timestamp}</div>
|
||||
<div className="col-sm-2 justify-content-start username"><span className="fw-bold cursor-pointer" onClick={() => SendMessageHook(new UserProfileComposer(item.userId))}>{item.userName}</span></div>
|
||||
<div className="col justify-content-start h-100"><span className="text-break text-wrap h-100">{item.message}</span></div>
|
||||
</div>
|
||||
<Grid key={ props.key } fullHeight={ false } style={ props.style } gap={ 1 } alignItems="center" className="log-entry py-1 border-bottom">
|
||||
<Text className="g-col-2">{ chatlogEntry.timestamp }</Text>
|
||||
<Text className="g-col-3" bold underline pointer onClick={ event => SendMessageHook(new UserProfileComposer(chatlogEntry.userId)) }>{ chatlogEntry.userName }</Text>
|
||||
<Text textBreak wrap className="g-col-7">{ chatlogEntry.message }</Text>
|
||||
</Grid>
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
const advancedRowRenderer: ListRowRenderer = (props: ListRowProps) =>
|
||||
const advancedRowRenderer = (props: ListRowProps) =>
|
||||
{
|
||||
let chatlogEntry: ChatlineData;
|
||||
let currentRecord: ChatRecordData;
|
||||
let chatlogEntry = null;
|
||||
let currentRecord: ChatRecordData = null;
|
||||
let isRoomInfo = false;
|
||||
|
||||
let totalIndex = 0;
|
||||
|
||||
for(let i = 0; i < records.length; i++)
|
||||
{
|
||||
currentRecord = records[i];
|
||||
|
||||
totalIndex++; // row for room info
|
||||
totalIndex = totalIndex + currentRecord.chatlog.length;
|
||||
totalIndex = (totalIndex + currentRecord.chatlog.length);
|
||||
|
||||
if(props.index > (totalIndex - 1))
|
||||
{
|
||||
continue; // it is not in current one
|
||||
}
|
||||
if(props.index > (totalIndex - 1)) continue;
|
||||
|
||||
if( (props.index + 1) === (totalIndex - currentRecord.chatlog.length))
|
||||
if((props.index + 1) === (totalIndex - currentRecord.chatlog.length))
|
||||
{
|
||||
isRoomInfo = true;
|
||||
|
||||
break;
|
||||
}
|
||||
const index = props.index - (totalIndex - currentRecord.chatlog.length);
|
||||
|
||||
const index = (props.index - (totalIndex - currentRecord.chatlog.length));
|
||||
|
||||
chatlogEntry = currentRecord.chatlog[index];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
cache={cache}
|
||||
columnIndex={0}
|
||||
key={props.key}
|
||||
parent={props.parent}
|
||||
rowIndex={props.index}
|
||||
cache={ cache }
|
||||
columnIndex={ 0 }
|
||||
key={ props.key }
|
||||
parent={ props.parent }
|
||||
rowIndex={ props.index }
|
||||
>
|
||||
{isRoomInfo && <RoomInfo roomId={currentRecord.roomId} roomName={currentRecord.roomName} uniqueKey={props.key} style={props.style}/>}
|
||||
{!isRoomInfo &&
|
||||
<div key={props.key} style={props.style} className="row chatlog-entry justify-content-start">
|
||||
<div className="col-auto text-center">{chatlogEntry.timestamp}</div>
|
||||
<div className="col-sm-2 justify-content-start username"><span className="fw-bold cursor-pointer" onClick={() => SendMessageHook(new UserProfileComposer(chatlogEntry.userId))}>{chatlogEntry.userName}</span></div>
|
||||
<div className="col justify-content-start h-100"><span className="text-break text-wrap h-100">{chatlogEntry.message}</span></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
{ (isRoomInfo && currentRecord) &&
|
||||
<RoomInfo roomId={ currentRecord.roomId } roomName={ currentRecord.roomName } uniqueKey={ props.key } style={ props.style } /> }
|
||||
{ !isRoomInfo &&
|
||||
<Grid key={ props.key } style={ props.style } gap={ 1 } alignItems="center" className="log-entry py-1 border-bottom">
|
||||
<Text className="g-col-2">{ chatlogEntry.timestamp }</Text>
|
||||
<Text className="g-col-3" bold underline pointer onClick={ event => SendMessageHook(new UserProfileComposer(chatlogEntry.userId)) }>{ chatlogEntry.userName }</Text>
|
||||
<Text textBreak wrap className="g-col-7">{ chatlogEntry.message }</Text>
|
||||
</Grid> }
|
||||
</CellMeasurer>
|
||||
);
|
||||
}
|
||||
@ -94,57 +99,60 @@ export const ChatlogView: FC<ChatlogViewProps> = props =>
|
||||
return count;
|
||||
}, [records]);
|
||||
|
||||
const RoomInfo = (props: { roomId: number, roomName: string, uniqueKey: Key, style: CSSProperties }) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={ props.uniqueKey } gap={ 2 } alignItems="center" justifyContent="between" className="room-info bg-muted rounded p-1" style={ props.style }>
|
||||
<Flex gap={ 1 }>
|
||||
<Text bold>Room name:</Text>
|
||||
<Text>{ props.roomName }</Text>
|
||||
</Flex>
|
||||
<Flex gap={ 1 }>
|
||||
<Button onClick={ event => TryVisitRoom(props.roomId) }>Visit Room</Button>
|
||||
<Button onClick={ event => dispatchUiEvent(new ModToolsOpenRoomInfoEvent(props.roomId)) }>Room Tools</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
defaultHeight: 25,
|
||||
fixedWidth: true
|
||||
});
|
||||
|
||||
const RoomInfo = useCallback(({ roomId, roomName, uniqueKey, style }) =>
|
||||
{
|
||||
return (
|
||||
<div key={uniqueKey} style={style} className="row justify-content-start gap-2 room-info">
|
||||
<div className="col-7"><span className="fw-bold">Room: </span>{roomName}</div>
|
||||
<button className="btn btn-sm btn-primary col-auto" onClick={() => TryVisitRoom(roomId)}>Visit Room</button>
|
||||
<button className="btn btn-sm btn-primary col-auto" onClick={() => dispatchUiEvent(new ModToolsOpenRoomInfoEvent(roomId))}>Room Tools</button>
|
||||
</div>
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
(records && records.length) &&
|
||||
<>
|
||||
{(records.length === 1) && <RoomInfo roomId={records[0].roomId} roomName={records[0].roomName} uniqueKey={records[0].roomId} style={{}} />}
|
||||
<div className="chatlog-messages w-100 h-100 overflow-hidden">
|
||||
<div className="row align-items-start w-100">
|
||||
<div className="col-auto text-center fw-bold">Time</div>
|
||||
<div className="col-sm-2 username-label fw-bold">User</div>
|
||||
<div className="col fw-bold">Message</div>
|
||||
</div>
|
||||
<div className="row w-100 h-100 chatlog">
|
||||
<AutoSizer defaultWidth={400} defaultHeight={200}>
|
||||
{({ height, width }) =>
|
||||
{ (records && (records.length === 1)) &&
|
||||
<RoomInfo roomId={records[0].roomId} roomName={records[0].roomName} uniqueKey={ null } style={ {} } /> }
|
||||
<Column fit gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid gap={ 1 } className="text-black fw-bold border-bottom pb-1">
|
||||
<Base className="g-col-2">Time</Base>
|
||||
<Base className="g-col-3">User</Base>
|
||||
<Base className="g-col-7">Message</Base>
|
||||
</Grid>
|
||||
</Column>
|
||||
{ (records && (records.length > 0)) &&
|
||||
<Column className="log-container striped-children" overflow="auto" gap={ 0 }>
|
||||
<AutoSizer defaultWidth={ 400 } defaultHeight={ 200 }>
|
||||
{ ({ height, width }) =>
|
||||
{
|
||||
cache.clearAll();
|
||||
|
||||
return (
|
||||
<List
|
||||
width={width}
|
||||
height={height}
|
||||
rowCount={records.length > 1 ? getNumRowsForAdvanced() : records[0].chatlog.length}
|
||||
rowHeight={cache.rowHeight}
|
||||
className={'chatlog-container'}
|
||||
rowRenderer={records.length > 1 ? advancedRowRenderer : simpleRowRenderer}
|
||||
deferredMeasurementCache={cache} />
|
||||
)
|
||||
}
|
||||
}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
width={ width }
|
||||
height={ height }
|
||||
rowCount={ (records.length > 1) ? getNumRowsForAdvanced() : records[0].chatlog.length }
|
||||
rowHeight={ cache.rowHeight }
|
||||
className={ 'log-entry-container' }
|
||||
rowRenderer={ (records.length > 1) ? advancedRowRenderer : rowRenderer }
|
||||
deferredMeasurementCache={ cache } />
|
||||
);
|
||||
} }
|
||||
</AutoSizer>
|
||||
</Column> }
|
||||
</Column>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { ChatRecordData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface ChatlogViewProps
|
||||
{
|
||||
records: ChatRecordData[];
|
||||
}
|
@ -1,20 +1,19 @@
|
||||
import { ChatRecordData, GetRoomChatlogMessageComposer, RoomChatlogEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ChatlogView } from '../../chatlog/ChatlogView';
|
||||
import { ModToolsChatlogViewProps } from './ModToolsChatlogView.types';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../hooks/messages';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { ChatlogView } from '../chatlog/ChatlogView';
|
||||
|
||||
interface ModToolsChatlogViewProps
|
||||
{
|
||||
roomId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsChatlogView: FC<ModToolsChatlogViewProps> = props =>
|
||||
{
|
||||
const { roomId = null, onCloseClick = null } = props;
|
||||
|
||||
const [roomChatlog, setRoomChatlog] = useState<ChatRecordData>(null);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetRoomChatlogMessageComposer(roomId));
|
||||
}, [roomId]);
|
||||
const [ roomChatlog, setRoomChatlog ] = useState<ChatRecordData>(null);
|
||||
|
||||
const onModtoolRoomChatlogEvent = useCallback((event: RoomChatlogEvent) =>
|
||||
{
|
||||
@ -23,17 +22,23 @@ export const ModToolsChatlogView: FC<ModToolsChatlogViewProps> = props =>
|
||||
if(!parser || parser.data.roomId !== roomId) return;
|
||||
|
||||
setRoomChatlog(parser.data);
|
||||
}, [roomId, setRoomChatlog]);
|
||||
}, [ roomId ]);
|
||||
|
||||
CreateMessageHook(RoomChatlogEvent, onModtoolRoomChatlogEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetRoomChatlogMessageComposer(roomId));
|
||||
}, [ roomId ]);
|
||||
|
||||
if(!roomChatlog) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-room-chatlog" simple={true}>
|
||||
<NitroCardHeaderView headerText={'Room Chatlog' + (roomChatlog ? ': ' + roomChatlog.roomName : '')} onCloseClick={() => onCloseClick()} />
|
||||
<NitroCardView className="nitro-mod-tools-chatlog" simple={true}>
|
||||
<NitroCardHeaderView headerText={ `Room Chatlog ${ roomChatlog.roomName }` } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black h-100">
|
||||
{roomChatlog &&
|
||||
<ChatlogView records={[roomChatlog]} />
|
||||
}
|
||||
{ roomChatlog &&
|
||||
<ChatlogView records={ [ roomChatlog ] } /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
128
src/components/mod-tools/views/room/ModToolsRoomView.tsx
Normal file
128
src/components/mod-tools/views/room/ModToolsRoomView.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
import { GetModeratorRoomInfoMessageComposer, ModerateRoomMessageComposer, ModeratorActionMessageComposer, ModeratorRoomInfoEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { TryVisitRoom } from '../../../../api';
|
||||
import { Button, Column, Flex, Text } from '../../../../common';
|
||||
import { ModToolsOpenRoomChatlogEvent } from '../../../../events/mod-tools/ModToolsOpenRoomChatlogEvent';
|
||||
import { BatchUpdates, dispatchUiEvent } from '../../../../hooks';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../hooks/messages';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
|
||||
interface ModToolsRoomViewProps
|
||||
{
|
||||
roomId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsRoomView: FC<ModToolsRoomViewProps> = props =>
|
||||
{
|
||||
const { roomId = null, onCloseClick = null } = props;
|
||||
|
||||
const [ infoRequested, setInfoRequested ] = useState(false);
|
||||
const [ loadedRoomId, setLoadedRoomId ] = useState(null);
|
||||
|
||||
const [ name, setName ] = useState(null);
|
||||
const [ ownerId, setOwnerId ] = useState(null);
|
||||
const [ ownerName, setOwnerName ] = useState(null);
|
||||
const [ ownerInRoom, setOwnerInRoom ] = useState(false);
|
||||
const [ usersInRoom, setUsersInRoom ] = useState(0);
|
||||
|
||||
//form data
|
||||
const [ kickUsers, setKickUsers ] = useState(false);
|
||||
const [ lockRoom, setLockRoom ] = useState(false);
|
||||
const [ changeRoomName, setChangeRoomName ] = useState(false);
|
||||
const [ message, setMessage ] = useState('');
|
||||
|
||||
const onModtoolRoomInfoEvent = useCallback((event: ModeratorRoomInfoEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser || parser.data.flatId !== roomId) return;
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setLoadedRoomId(parser.data.flatId);
|
||||
setName(parser.data.room.name);
|
||||
setOwnerId(parser.data.ownerId);
|
||||
setOwnerName(parser.data.ownerName);
|
||||
setOwnerInRoom(parser.data.ownerInRoom);
|
||||
setUsersInRoom(parser.data.userCount);
|
||||
});
|
||||
}, [ roomId ]);
|
||||
|
||||
CreateMessageHook(ModeratorRoomInfoEvent, onModtoolRoomInfoEvent);
|
||||
|
||||
const handleClick = useCallback((action: string, value?: string) =>
|
||||
{
|
||||
if(!action) return;
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case 'alert_only':
|
||||
if(message.trim().length === 0) return;
|
||||
|
||||
SendMessageHook(new ModeratorActionMessageComposer(ModeratorActionMessageComposer.ACTION_ALERT, message, ''));
|
||||
return;
|
||||
case 'send_message':
|
||||
if(message.trim().length === 0) return;
|
||||
|
||||
SendMessageHook(new ModeratorActionMessageComposer(ModeratorActionMessageComposer.ACTION_MESSAGE, message, ''));
|
||||
SendMessageHook(new ModerateRoomMessageComposer(roomId, lockRoom ? 1 : 0, changeRoomName ? 1 : 0, kickUsers ? 1 : 0))
|
||||
return;
|
||||
}
|
||||
}, [ changeRoomName, kickUsers, lockRoom, message, roomId ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(infoRequested) return;
|
||||
|
||||
SendMessageHook(new GetModeratorRoomInfoMessageComposer(roomId));
|
||||
setInfoRequested(true);
|
||||
}, [ roomId, infoRequested, setInfoRequested ]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-room" simple>
|
||||
<NitroCardHeaderView headerText={ 'Room Info' + (name ? ': ' + name : '') } onCloseClick={ event => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<Flex gap={ 2 }>
|
||||
<Column justifyContent="center" grow gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 2 }>
|
||||
<Text bold align="end" className="col-7">Room Owner:</Text>
|
||||
<Text underline pointer truncate>{ ownerName }</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 2 }>
|
||||
<Text bold align="end" className="col-7">Users in room:</Text>
|
||||
<Text>{ usersInRoom }</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 2 }>
|
||||
<Text bold align="end" className="col-7">Owner in room:</Text>
|
||||
<Text>{ ownerInRoom ? 'Yes' : 'No' }</Text>
|
||||
</Flex>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Button onClick={ event => TryVisitRoom(roomId) }>Visit Room</Button>
|
||||
<Button onClick={ event => dispatchUiEvent(new ModToolsOpenRoomChatlogEvent(roomId)) }>Chatlog</Button>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Column className="bg-muted rounded p-2" gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ kickUsers } onChange={ event => setKickUsers(event.target.checked) } />
|
||||
<Text small>Kick everyone out</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ lockRoom } onChange={ event => setLockRoom(event.target.checked) } />
|
||||
<Text small>Enable the doorbell</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<input className="form-check-input" type="checkbox" checked={ changeRoomName } onChange={ event => setChangeRoomName(event.target.checked) }/>
|
||||
<Text small>Change room name</Text>
|
||||
</Flex>
|
||||
</Column>
|
||||
<textarea className="form-control" placeholder="Type a mandatory message to the users in this text box..." value={ message } onChange={ event => setMessage(event.target.value) }></textarea>
|
||||
<Flex justifyContent="between">
|
||||
<Button variant="danger" onClick={ event => handleClick('send_message') }>Send Caution</Button>
|
||||
<Button onClick={ event => handleClick('alert_only') }>Send Alert only</Button>
|
||||
</Flex>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface ModToolsChatlogViewProps
|
||||
{
|
||||
roomId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
.nitro-mod-tools-room {
|
||||
width: 240px;
|
||||
|
||||
.username {
|
||||
color: #1E7295;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
import { GetModeratorRoomInfoMessageComposer, ModerateRoomMessageComposer, ModeratorActionMessageComposer, ModeratorRoomInfoEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { TryVisitRoom } from '../../../../../api';
|
||||
import { ModToolsOpenRoomChatlogEvent } from '../../../../../events/mod-tools/ModToolsOpenRoomChatlogEvent';
|
||||
import { dispatchUiEvent } from '../../../../../hooks';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ModToolsRoomViewProps } from './ModToolsRoomView.types';
|
||||
|
||||
export const ModToolsRoomView: FC<ModToolsRoomViewProps> = props =>
|
||||
{
|
||||
const { roomId = null, onCloseClick = null } = props;
|
||||
|
||||
const [ infoRequested, setInfoRequested ] = useState(false);
|
||||
const [ loadedRoomId, setLoadedRoomId ] = useState(null);
|
||||
|
||||
const [ name, setName ] = useState(null);
|
||||
const [ ownerId, setOwnerId ] = useState(null);
|
||||
const [ ownerName, setOwnerName ] = useState(null);
|
||||
const [ ownerInRoom, setOwnerInRoom ] = useState(false);
|
||||
const [ usersInRoom, setUsersInRoom ] = useState(0);
|
||||
|
||||
//form data
|
||||
const [kickUsers, setKickUsers] = useState(false);
|
||||
const [lockRoom, setLockRoom] = useState(false);
|
||||
const [changeRoomName, setChangeRoomName] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(infoRequested) return;
|
||||
|
||||
SendMessageHook(new GetModeratorRoomInfoMessageComposer(roomId));
|
||||
setInfoRequested(true);
|
||||
}, [ roomId, infoRequested, setInfoRequested ]);
|
||||
|
||||
const onModtoolRoomInfoEvent = useCallback((event: ModeratorRoomInfoEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser || parser.data.flatId !== roomId) return;
|
||||
|
||||
setLoadedRoomId(parser.data.flatId);
|
||||
setName(parser.data.room.name);
|
||||
setOwnerId(parser.data.ownerId);
|
||||
setOwnerName(parser.data.ownerName);
|
||||
setOwnerInRoom(parser.data.ownerInRoom);
|
||||
setUsersInRoom(parser.data.userCount);
|
||||
}, [ setLoadedRoomId, setName, setOwnerId, setOwnerName, setOwnerInRoom, setUsersInRoom, roomId ]);
|
||||
|
||||
CreateMessageHook(ModeratorRoomInfoEvent, onModtoolRoomInfoEvent);
|
||||
|
||||
const handleClick = useCallback((action: string, value?: string) =>
|
||||
{
|
||||
if(!action) return;
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case 'alert_only':
|
||||
if(message.trim().length === 0) return;
|
||||
SendMessageHook(new ModeratorActionMessageComposer(ModeratorActionMessageComposer.ACTION_ALERT, message, ''));
|
||||
return;
|
||||
case 'send_message':
|
||||
if(message.trim().length === 0) return;
|
||||
SendMessageHook(new ModeratorActionMessageComposer(ModeratorActionMessageComposer.ACTION_MESSAGE, message, ''));
|
||||
SendMessageHook(new ModerateRoomMessageComposer(roomId, lockRoom ? 1 : 0, changeRoomName ? 1 : 0, kickUsers ? 1 : 0))
|
||||
return;
|
||||
}
|
||||
}, [changeRoomName, kickUsers, lockRoom, message, roomId]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-room" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ 'Room Info' + (name ? ': ' + name : '') } onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div>
|
||||
<b>Room Owner:</b> <span className="username fw-bold cursor-pointer">{ ownerName }</span>
|
||||
</div>
|
||||
<button className="btn btn-sm btn-primary" onClick={() => TryVisitRoom(roomId)}>Visit Room</button>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div>
|
||||
<b>Users in room:</b> { usersInRoom }
|
||||
</div>
|
||||
<button className="btn btn-sm btn-primary" onClick={() => dispatchUiEvent(new ModToolsOpenRoomChatlogEvent(roomId))}>Chatlog</button>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<b>Owner in room:</b> { ownerInRoom ? 'Yes' : 'No' }
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted rounded py-1 px-2 mb-2">
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" id="kickUsers" checked={ kickUsers } onChange={e => setKickUsers(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="kickUsers">
|
||||
Kick users out of the room
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" id="lockRoom" checked={ lockRoom } onChange={e => setLockRoom(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="lockRoom">
|
||||
Change room lock to doorbell
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" id="roomName" checked={ changeRoomName } onChange={e => setChangeRoomName(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="roomName">
|
||||
Change room name to "Inappro- priate to Hotel Management"
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<textarea className="form-control mb-2" placeholder="Type a mandatory message to the users in this text box..." value={message} onChange={e => setMessage(e.target.value)}></textarea>
|
||||
<div className="d-flex justify-content-between">
|
||||
<button className="btn btn-danger w-100 me-2" onClick={() => handleClick('send_message')}>Send Caution</button>
|
||||
<button className="btn btn-success w-100" onClick={() => handleClick('alert_only')}>Send Alert only</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface ModToolsRoomViewProps
|
||||
{
|
||||
roomId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
import { CfhChatlogData, CfhChatlogEvent, GetCfhChatlogMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ChatlogView } from '../../chatlog/ChatlogView';
|
||||
import { CfhChatlogViewProps } from './CfhChatlogView.types';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { ChatlogView } from '../chatlog/ChatlogView';
|
||||
|
||||
interface CfhChatlogViewProps
|
||||
{
|
||||
issueId: number;
|
||||
onCloseClick(): void;
|
||||
}
|
||||
|
||||
export const CfhChatlogView: FC<CfhChatlogViewProps> = props =>
|
||||
{
|
@ -0,0 +1,99 @@
|
||||
import { CloseIssuesMessageComposer, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { Button, Column, Grid, Text } from '../../../../common';
|
||||
import { ModToolsOpenUserInfoEvent } from '../../../../events/mod-tools/ModToolsOpenUserInfoEvent';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { getSourceName } from '../../common/IssueCategoryNames';
|
||||
import { useModToolsContext } from '../../ModToolsContext';
|
||||
import { CfhChatlogView } from './CfhChatlogView';
|
||||
|
||||
interface IssueInfoViewProps
|
||||
{
|
||||
issueId: number;
|
||||
onIssueInfoClosed(issueId: number): void;
|
||||
}
|
||||
|
||||
export const ModToolsIssueInfoView: FC<IssueInfoViewProps> = props =>
|
||||
{
|
||||
const { issueId = null, onIssueInfoClosed = null } = props;
|
||||
const { modToolsState = null } = useModToolsContext();
|
||||
const { tickets = null } = modToolsState;
|
||||
const [ cfhChatlogOpen, setcfhChatlogOpen ] = useState(false);
|
||||
|
||||
const ticket = useMemo(() =>
|
||||
{
|
||||
if(!tickets || !tickets.length) return null;
|
||||
|
||||
return tickets.find(issue => issue.issueId === issueId);
|
||||
}, [ issueId, tickets ]);
|
||||
|
||||
const releaseIssue = (issueId: number) =>
|
||||
{
|
||||
SendMessageHook(new ReleaseIssuesMessageComposer([ issueId ]));
|
||||
|
||||
onIssueInfoClosed(issueId);
|
||||
}
|
||||
|
||||
const closeIssue = (resolutionType: number) =>
|
||||
{
|
||||
SendMessageHook(new CloseIssuesMessageComposer([ issueId ], resolutionType));
|
||||
|
||||
onIssueInfoClosed(issueId)
|
||||
}
|
||||
|
||||
const openUserInfo = (userId: number) => dispatchUiEvent(new ModToolsOpenUserInfoEvent(userId));
|
||||
|
||||
return (
|
||||
<>
|
||||
<NitroCardView className="nitro-mod-tools-handle-issue" simple>
|
||||
<NitroCardHeaderView headerText={'Resolving issue ' + issueId} onCloseClick={() => onIssueInfoClosed(issueId)} />
|
||||
<NitroCardContentView className="text-black">
|
||||
<Text fontSize={ 4 }>Issue Information</Text>
|
||||
<Grid>
|
||||
<Column size={ 8 }>
|
||||
<table className="table table-striped table-sm table-text-small text-black m-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Source</th>
|
||||
<td>{ getSourceName(ticket.categoryId) }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Category</th>
|
||||
<td>{ LocalizeText('help.cfh.topic.' + ticket.reportedCategoryId) }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{ ticket.message }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Caller</th>
|
||||
<td>
|
||||
<Text bold underline pointer onClick={ event => openUserInfo(ticket.reporterUserId) }>{ ticket.reporterUserName }</Text>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Reported User</th>
|
||||
<td>
|
||||
<Text bold underline pointer onClick={ event => openUserInfo(ticket.reportedUserId) }>{ ticket.reportedUserName }</Text>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Column>
|
||||
<Column size={ 4 } gap={ 1 }>
|
||||
<Button variant="secondary" onClick={ () => setcfhChatlogOpen(!cfhChatlogOpen) }>Chatlog</Button>
|
||||
<Button onClick={ event => closeIssue(CloseIssuesMessageComposer.RESOLUTION_USELESS) }>Close as useless</Button>
|
||||
<Button variant="danger" onClick={ event => closeIssue(CloseIssuesMessageComposer.RESOLUTION_ABUSIVE) }>Close as abusive</Button>
|
||||
<Button variant="success" onClick={ event => closeIssue(CloseIssuesMessageComposer.RESOLUTION_RESOLVED) }>Close as resolved</Button>
|
||||
<Button variant="secondary" onClick={ event => releaseIssue(issueId)} >Release</Button>
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{ cfhChatlogOpen &&
|
||||
<CfhChatlogView issueId={ issueId } onCloseClick={ () => setcfhChatlogOpen(false) }/> }
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import { IssueMessageData, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { Base, Button, Column, Grid } from '../../../../common';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
|
||||
interface ModToolsMyIssuesTabViewProps
|
||||
{
|
||||
myIssues: IssueMessageData[];
|
||||
onIssueHandleClick(issueId: number): void;
|
||||
}
|
||||
|
||||
export const ModToolsMyIssuesTabView: FC<ModToolsMyIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { myIssues = null, onIssueHandleClick = null } = props;
|
||||
|
||||
const onReleaseIssue = (issueId: number) => SendMessageHook(new ReleaseIssuesMessageComposer([issueId]));
|
||||
|
||||
return (
|
||||
<Column gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid gap={ 1 } className="text-black fw-bold border-bottom pb-1">
|
||||
<Base className="g-col-2">Type</Base>
|
||||
<Base className="g-col-3">Room/Player</Base>
|
||||
<Base className="g-col-3">Opened</Base>
|
||||
<Base className="g-col-2"></Base>
|
||||
<Base className="g-col-2"></Base>
|
||||
</Grid>
|
||||
</Column>
|
||||
<Column overflow="auto" className="striped-children" gap={ 0 }>
|
||||
{ myIssues && (myIssues.length > 0) && myIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<Grid key={ issue.issueId } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom">
|
||||
<Base className="g-col-2">{ issue.categoryId }</Base>
|
||||
<Base className="g-col-3">{ issue.reportedUserName }</Base>
|
||||
<Base className="g-col-3">{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }</Base>
|
||||
<Base className="g-col-2">
|
||||
<Button variant="primary" onClick={ event => onIssueHandleClick(issue.issueId) }>Handle</Button>
|
||||
</Base>
|
||||
<Base className="g-col-2">
|
||||
<Button variant="danger" onClick={ event => onReleaseIssue(issue.issueId) }>Release</Button>
|
||||
</Base>
|
||||
</Grid>
|
||||
);
|
||||
}) }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import { IssueMessageData, PickIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { Base, Button, Column, Grid } from '../../../../common';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
|
||||
interface ModToolsOpenIssuesTabViewProps
|
||||
{
|
||||
openIssues: IssueMessageData[];
|
||||
}
|
||||
|
||||
export const ModToolsOpenIssuesTabView: FC<ModToolsOpenIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { openIssues = null } = props;
|
||||
|
||||
const onPickIssue = (issueId: number) => SendMessageHook(new PickIssuesMessageComposer([issueId], false, 0, 'pick issue button'));
|
||||
|
||||
return (
|
||||
<Column gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid gap={ 1 } className="text-black fw-bold border-bottom pb-1">
|
||||
<Base className="g-col-2">Type</Base>
|
||||
<Base className="g-col-3">Room/Player</Base>
|
||||
<Base className="g-col-4">Opened</Base>
|
||||
<Base className="g-col-3"></Base>
|
||||
</Grid>
|
||||
</Column>
|
||||
<Column overflow="auto" className="striped-children" gap={ 0 }>
|
||||
{ openIssues && (openIssues.length > 0) && openIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<Grid key={ issue.issueId } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom">
|
||||
<Base className="g-col-2">{ issue.categoryId }</Base>
|
||||
<Base className="g-col-3">{ issue.reportedUserName }</Base>
|
||||
<Base className="g-col-4">{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }</Base>
|
||||
<Base className="g-col-3">
|
||||
<Button variant="success" onClick={ event => onPickIssue(issue.issueId) }>Pick Issue</Button>
|
||||
</Base>
|
||||
</Grid>
|
||||
);
|
||||
}) }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import { IssueMessageData } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { Base, Column, Grid } from '../../../../common';
|
||||
|
||||
interface ModToolsPickedIssuesTabViewProps
|
||||
{
|
||||
pickedIssues: IssueMessageData[];
|
||||
}
|
||||
|
||||
export const ModToolsPickedIssuesTabView: FC<ModToolsPickedIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { pickedIssues = null } = props;
|
||||
|
||||
return (
|
||||
<Column gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid gap={ 1 } className="text-black fw-bold border-bottom pb-1">
|
||||
<Base className="g-col-2">Type</Base>
|
||||
<Base className="g-col-3">Room/Player</Base>
|
||||
<Base className="g-col-4">Opened</Base>
|
||||
<Base className="g-col-3">Picker</Base>
|
||||
</Grid>
|
||||
</Column>
|
||||
<Column overflow="auto" className="striped-children" gap={ 0 }>
|
||||
{ pickedIssues && (pickedIssues.length > 0) && pickedIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<Grid key={ issue.issueId } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom">
|
||||
<Base className="g-col-2">{ issue.categoryId }</Base>
|
||||
<Base className="g-col-3">{ issue.reportedUserName }</Base>
|
||||
<Base className="g-col-4">{ new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString() }</Base>
|
||||
<Base className="g-col-3">{ issue.pickerUserName }</Base>
|
||||
</Grid>
|
||||
);
|
||||
}) }
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
.nitro-mod-tools-tickets
|
||||
{
|
||||
width: 400px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.nitro-mod-tools-handle-issue
|
||||
{
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
}
|
@ -2,12 +2,16 @@ import { IssueMessageData } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { GetSessionDataManager } from '../../../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
|
||||
import { useModToolsContext } from '../../context/ModToolsContext';
|
||||
import { IssueInfoView } from './issue-info/IssueInfoView';
|
||||
import { ModToolsTicketsViewProps } from './ModToolsTicketsView.types';
|
||||
import { ModToolsMyIssuesTabView } from './my-issues/ModToolsMyIssuesTabView';
|
||||
import { ModToolsOpenIssuesTabView } from './open-issues/ModToolsOpenIssuesTabView';
|
||||
import { ModToolsPickedIssuesTabView } from './picked-issues/ModToolsPickedIssuesTabView';
|
||||
import { useModToolsContext } from '../../ModToolsContext';
|
||||
import { ModToolsIssueInfoView } from './ModToolsIssueInfoView';
|
||||
import { ModToolsMyIssuesTabView } from './ModToolsMyIssuesTabView';
|
||||
import { ModToolsOpenIssuesTabView } from './ModToolsOpenIssuesTabView';
|
||||
import { ModToolsPickedIssuesTabView } from './ModToolsPickedIssuesTabView';
|
||||
|
||||
interface ModToolsTicketsViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
const TABS: string[] = [
|
||||
'Open Issues',
|
||||
@ -82,25 +86,21 @@ export const ModToolsTicketsView: FC<ModToolsTicketsViewProps> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<NitroCardView className="nitro-mod-tools-tickets" simple={ false }>
|
||||
<NitroCardHeaderView headerText={ 'Tickets' } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="p-0 text-black">
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map((tab, index) =>
|
||||
{
|
||||
return (<NitroCardTabsItemView key={ index } isActive={ currentTab === index } onClick={ () => setCurrentTab(index) }>
|
||||
{ tab }
|
||||
</NitroCardTabsItemView>);
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<div className="p-2">
|
||||
<NitroCardView className="nitro-mod-tools-tickets">
|
||||
<NitroCardHeaderView headerText={ 'Tickets' } onCloseClick={ onCloseClick } />
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map((tab, index) =>
|
||||
{
|
||||
return (<NitroCardTabsItemView key={ index } isActive={ (currentTab === index) } onClick={ event => setCurrentTab(index) }>
|
||||
{ tab }
|
||||
</NitroCardTabsItemView>);
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView gap={ 1 }>
|
||||
<CurrentTabComponent />
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{
|
||||
issueInfoWindows && issueInfoWindows.map(issueId => <IssueInfoView key={issueId} issueId={issueId} onIssueInfoClosed={onIssueInfoClosed}/>)
|
||||
}
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{ issueInfoWindows && (issueInfoWindows.length > 0) && issueInfoWindows.map(issueId => <ModToolsIssueInfoView key={ issueId } issueId={ issueId } onIssueInfoClosed={ onIssueInfoClosed } />) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
export interface ModToolsTicketsViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface CfhChatlogViewProps
|
||||
{
|
||||
issueId: number;
|
||||
onCloseClick(): void;
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import { CloseIssuesMessageComposer, ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { ModToolsOpenUserInfoEvent } from '../../../../../events/mod-tools/ModToolsOpenUserInfoEvent';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { getSourceName } from '../../../common/IssueCategoryNames';
|
||||
import { useModToolsContext } from '../../../context/ModToolsContext';
|
||||
import { CfhChatlogView } from './CfhChatlogView';
|
||||
import { IssueInfoViewProps } from './IssueInfoView.types';
|
||||
|
||||
export const IssueInfoView: FC<IssueInfoViewProps> = props =>
|
||||
{
|
||||
const { issueId = null, onIssueInfoClosed = null } = props;
|
||||
const { modToolsState = null } = useModToolsContext();
|
||||
const { tickets= null } = modToolsState;
|
||||
const [ cfhChatlogOpen, setcfhChatlogOpen ] = useState(false);
|
||||
|
||||
const ticket = useMemo(() =>
|
||||
{
|
||||
return tickets.find( issue => issue.issueId === issueId);
|
||||
}, [issueId, tickets]);
|
||||
|
||||
const onReleaseIssue = useCallback((issueId: number) =>
|
||||
{
|
||||
SendMessageHook(new ReleaseIssuesMessageComposer([issueId]));
|
||||
onIssueInfoClosed(issueId);
|
||||
}, [onIssueInfoClosed]);
|
||||
|
||||
const openUserInfo = useCallback((userId: number) =>
|
||||
{
|
||||
dispatchUiEvent(new ModToolsOpenUserInfoEvent(userId));
|
||||
}, []);
|
||||
|
||||
const closeIssue = useCallback((resolutionType: number) =>
|
||||
{
|
||||
SendMessageHook(new CloseIssuesMessageComposer([issueId], resolutionType));
|
||||
onIssueInfoClosed(issueId)
|
||||
}, [issueId, onIssueInfoClosed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NitroCardView className="nitro-mod-tools-handle-issue" simple={true}>
|
||||
<NitroCardHeaderView headerText={'Resolving issue ' + issueId} onCloseClick={() => onIssueInfoClosed(issueId)} />
|
||||
<NitroCardContentView className="text-black">
|
||||
<div className="row">
|
||||
<div className="col-8">
|
||||
<h3>Issue Information</h3>
|
||||
<div><span className="fw-bold">Source: </span>{getSourceName(ticket.categoryId)}</div>
|
||||
<div><span className="fw-bold">Category: </span>{LocalizeText('help.cfh.topic.' + ticket.reportedCategoryId)}</div>
|
||||
<div><span className="fw-bold">Description: </span>{ticket.message}</div>
|
||||
<div><span className="fw-bold">Caller: </span><button className="btn btn-link fw-bold text-primary" onClick={() => openUserInfo(ticket.reporterUserId)}>{ticket.reporterUserName}</button></div>
|
||||
<div><span className="fw-bold">Reported User: </span><button className="btn btn-link fw-bold text-danger" onClick={() => openUserInfo(ticket.reportedUserId)}>{ticket.reportedUserName}</button></div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<div className="d-grid gap-2 mb-4">
|
||||
<button className="btn btn-secondary" onClick={() => setcfhChatlogOpen(!cfhChatlogOpen)}>Chatlog</button>
|
||||
</div>
|
||||
<div className="d-grid gap-2">
|
||||
<button className="btn btn-primary" onClick={() => closeIssue(CloseIssuesMessageComposer.RESOLUTION_USELESS)}>Close as useless</button>
|
||||
<button className="btn btn-danger" onClick={() => closeIssue(CloseIssuesMessageComposer.RESOLUTION_ABUSIVE)}>Close as abusive</button>
|
||||
<button className="btn btn-success" onClick={() => closeIssue(CloseIssuesMessageComposer.RESOLUTION_RESOLVED)}>Close as resolved</button>
|
||||
<button className="btn btn-secondary" onClick={() => onReleaseIssue(issueId)}>Release</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{ cfhChatlogOpen && <CfhChatlogView issueId={issueId} onCloseClick={() => setcfhChatlogOpen(false) }/>}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface IssueInfoViewProps
|
||||
{
|
||||
issueId: number;
|
||||
onIssueInfoClosed(issueId: number): void;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { ReleaseIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { SendMessageHook } from '../../../../../hooks';
|
||||
import { ModToolsMyIssuesTabViewProps } from './ModToolsMyIssuesTabView.types';
|
||||
|
||||
export const ModToolsMyIssuesTabView: FC<ModToolsMyIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { myIssues = null, onIssueHandleClick = null } = props;
|
||||
|
||||
|
||||
const onReleaseIssue = useCallback((issueId: number) =>
|
||||
{
|
||||
SendMessageHook(new ReleaseIssuesMessageComposer([issueId]));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className="table text-black table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Room/Player</th>
|
||||
<th scope="col">Opened</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{myIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<tr className="text-black" key={issue.issueId}>
|
||||
<td>{issue.categoryId}</td>
|
||||
<td>{issue.reportedUserName}</td>
|
||||
<td>{new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString()}</td>
|
||||
<td><button className="btn btn-sm btn-primary" onClick={() => onIssueHandleClick(issue.issueId)}>Handle</button></td>
|
||||
<td><button className="btn btn-sm btn-danger" onClick={() => onReleaseIssue(issue.issueId)}>Release</button></td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { IssueMessageData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface ModToolsMyIssuesTabViewProps
|
||||
{
|
||||
myIssues: IssueMessageData[];
|
||||
onIssueHandleClick(issueId: number): void;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import { PickIssuesMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { SendMessageHook } from '../../../../../hooks';
|
||||
import { ModToolsOpenIssuesTabViewProps } from './ModToolsOpenIssuesTabView.types';
|
||||
|
||||
export const ModToolsOpenIssuesTabView: FC<ModToolsOpenIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { openIssues = null } = props;
|
||||
|
||||
const onPickIssue = useCallback((issueId: number) =>
|
||||
{
|
||||
SendMessageHook(new PickIssuesMessageComposer([issueId], false, 0, 'pick issue button'));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className="table text-black table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Room/Player</th>
|
||||
<th scope="col">Opened</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{openIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<tr className="text-black" key={issue.issueId}>
|
||||
<td>{issue.categoryId}</td>
|
||||
<td>{issue.reportedUserName}</td>
|
||||
<td>{new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString()}</td>
|
||||
<td><button className="btn btn-sm btn-success" onClick={() => onPickIssue(issue.issueId)}>Pick Issue</button></td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { IssueMessageData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface ModToolsOpenIssuesTabViewProps
|
||||
{
|
||||
openIssues: IssueMessageData[];
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
import { ModToolsPickedIssuesTabViewProps } from './ModToolsPickedIssuesTabView.types';
|
||||
|
||||
export const ModToolsPickedIssuesTabView: FC<ModToolsPickedIssuesTabViewProps> = props =>
|
||||
{
|
||||
const { pickedIssues = null } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className="table text-black table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Room/Player</th>
|
||||
<th scope="col">Opened</th>
|
||||
<th scope="col">Picker</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pickedIssues.map(issue =>
|
||||
{
|
||||
return (
|
||||
<tr className="text-black" key={issue.issueId}>
|
||||
<td>{issue.categoryId}</td>
|
||||
<td>{issue.reportedUserName}</td>
|
||||
<td>{new Date(Date.now() - issue.issueAgeInMilliseconds).toLocaleTimeString()}</td>
|
||||
<td>{issue.pickerUserName}</td>
|
||||
</tr>)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { IssueMessageData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface ModToolsPickedIssuesTabViewProps
|
||||
{
|
||||
pickedIssues: IssueMessageData[];
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
import { ChatRecordData, GetUserChatlogMessageComposer, UserChatlogEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ChatlogView } from '../../chatlog/ChatlogView';
|
||||
import { ModToolsUserChatlogViewProps } from './ModToolsUserChatlogView.types';
|
||||
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { ChatlogView } from '../chatlog/ChatlogView';
|
||||
|
||||
interface ModToolsUserChatlogViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsUserChatlogView: FC<ModToolsUserChatlogViewProps> = props =>
|
||||
{
|
||||
const { userId = null, onCloseClick = null } = props;
|
||||
const [userChatlog, setUserChatlog] = useState<ChatRecordData[]>(null);
|
||||
const [username, setUsername] = useState<string>(null);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetUserChatlogMessageComposer(userId));
|
||||
}, [userId]);
|
||||
const [ userChatlog, setUserChatlog ] = useState<ChatRecordData[]>(null);
|
||||
const [ username, setUsername ] = useState<string>(null);
|
||||
|
||||
const onModtoolUserChatlogEvent = useCallback((event: UserChatlogEvent) =>
|
||||
{
|
||||
@ -22,19 +22,26 @@ export const ModToolsUserChatlogView: FC<ModToolsUserChatlogViewProps> = props =
|
||||
|
||||
if(!parser || parser.data.userId !== userId) return;
|
||||
|
||||
setUsername(parser.data.username);
|
||||
setUserChatlog(parser.data.roomChatlogs);
|
||||
}, [setUsername, setUserChatlog, userId]);
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setUsername(parser.data.username);
|
||||
setUserChatlog(parser.data.roomChatlogs);
|
||||
});
|
||||
}, [ userId ]);
|
||||
|
||||
CreateMessageHook(UserChatlogEvent, onModtoolUserChatlogEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetUserChatlogMessageComposer(userId));
|
||||
}, [ userId ]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-chatlog" simple={true}>
|
||||
<NitroCardHeaderView headerText={'User Chatlog' + (username ? ': ' + username : '')} onCloseClick={() => onCloseClick()} />
|
||||
<NitroCardView className="nitro-mod-tools-chatlog" simple>
|
||||
<NitroCardHeaderView headerText={ `User Chatlog: ${ username || '' }` } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black h-100">
|
||||
{userChatlog &&
|
||||
<ChatlogView records={userChatlog} />
|
||||
}
|
||||
{ userChatlog &&
|
||||
<ChatlogView records={userChatlog} /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
@ -0,0 +1,182 @@
|
||||
import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { Button, Column, Flex, Text } from '../../../../common';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { NotificationAlertType } from '../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { useModToolsContext } from '../../ModToolsContext';
|
||||
import { ISelectedUser } from '../../utils/ISelectedUser';
|
||||
import { ModActionDefinition } from '../../utils/ModActionDefinition';
|
||||
|
||||
interface ModToolsUserModActionViewProps
|
||||
{
|
||||
user: ISelectedUser;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
const MOD_ACTION_DEFINITIONS = [
|
||||
new ModActionDefinition(1, 'Alert', ModActionDefinition.ALERT, 1, 0),
|
||||
new ModActionDefinition(2, 'Mute 1h', ModActionDefinition.MUTE, 2, 0),
|
||||
new ModActionDefinition(4, 'Ban 7 days', ModActionDefinition.BAN, 4, 0),
|
||||
new ModActionDefinition(3, 'Ban 18h', ModActionDefinition.BAN, 3, 0),
|
||||
new ModActionDefinition(5, 'Ban 30 days (step 1)', ModActionDefinition.BAN, 5, 0),
|
||||
new ModActionDefinition(7, 'Ban 30 days (step 2)', ModActionDefinition.BAN, 7, 0),
|
||||
new ModActionDefinition(6, 'Ban 100 years', ModActionDefinition.BAN, 6, 0),
|
||||
new ModActionDefinition(106, 'Ban avatar-only 100 years', ModActionDefinition.BAN, 6, 0),
|
||||
new ModActionDefinition(101, 'Kick', ModActionDefinition.KICK, 0, 0),
|
||||
new ModActionDefinition(102, 'Lock trade 1 week', ModActionDefinition.TRADE_LOCK, 0, 168),
|
||||
new ModActionDefinition(104, 'Lock trade permanent', ModActionDefinition.TRADE_LOCK, 0, 876000),
|
||||
new ModActionDefinition(105, 'Message', ModActionDefinition.MESSAGE, 0, 0),
|
||||
];
|
||||
|
||||
export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = props =>
|
||||
{
|
||||
const { user = null, onCloseClick = null } = props;
|
||||
const [ selectedTopic, setSelectedTopic ] = useState(-1);
|
||||
const [ selectedAction, setSelectedAction ] = useState(-1);
|
||||
const [ message, setMessage ] = useState<string>('');
|
||||
const { modToolsState = null } = useModToolsContext();
|
||||
const { cfhCategories = null, settings = null } = modToolsState;
|
||||
|
||||
const topics = useMemo(() =>
|
||||
{
|
||||
const values: CallForHelpTopicData[] = [];
|
||||
|
||||
if(cfhCategories && cfhCategories.length)
|
||||
{
|
||||
for(const category of cfhCategories)
|
||||
{
|
||||
for(const topic of category.topics) values.push(topic);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}, [ cfhCategories ]);
|
||||
|
||||
const sendAlert = (message: string) =>
|
||||
{
|
||||
NotificationUtilities.simpleAlert(message, NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
}
|
||||
|
||||
const sendDefaultSanction = () =>
|
||||
{
|
||||
SendMessageHook(new DefaultSanctionMessageComposer(user.userId, selectedTopic, message));
|
||||
|
||||
onCloseClick();
|
||||
}
|
||||
|
||||
const sendSanction = () =>
|
||||
{
|
||||
let errorMessage: string = null;
|
||||
|
||||
const category = topics[selectedTopic];
|
||||
const sanction = MOD_ACTION_DEFINITIONS[selectedAction];
|
||||
|
||||
if((selectedTopic === -1) || (selectedAction === -1)) errorMessage = 'You must select a CFH topic and Sanction';
|
||||
else if(!settings || !settings.cfhPermission) errorMessage = 'You do not have permission to do this';
|
||||
else if(!category) errorMessage = 'You must select a CFH topic';
|
||||
else if(!sanction) errorMessage = 'You must select a sanction';
|
||||
|
||||
if(errorMessage)
|
||||
{
|
||||
sendAlert('You must select a sanction');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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(message.trim().length === 0)
|
||||
{
|
||||
sendAlert('Please write a message to user');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModAlertMessageComposer(user.userId, message, category.id));
|
||||
break;
|
||||
}
|
||||
case ModActionDefinition.MUTE:
|
||||
SendMessageHook(new ModMuteMessageComposer(user.userId, messageOrDefault, category.id));
|
||||
break;
|
||||
case ModActionDefinition.BAN: {
|
||||
if(!settings.banPermission)
|
||||
{
|
||||
sendAlert('You have insufficient permissions');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModBanMessageComposer(user.userId, messageOrDefault, category.id, selectedAction, (sanction.actionId === 106)));
|
||||
break;
|
||||
}
|
||||
case ModActionDefinition.KICK: {
|
||||
if(!settings.kickPermission)
|
||||
{
|
||||
sendAlert('You have insufficient permissions');
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModKickMessageComposer(user.userId, messageOrDefault, category.id));
|
||||
break;
|
||||
}
|
||||
case ModActionDefinition.TRADE_LOCK: {
|
||||
const numSeconds = (sanction.actionLengthHours * 60);
|
||||
|
||||
SendMessageHook(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;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModMessageMessageComposer(user.userId, message, category.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onCloseClick();
|
||||
}
|
||||
|
||||
if(!user) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-action" simple={true}>
|
||||
<NitroCardHeaderView headerText={'Mod Action: ' + (user ? user.username : '')} onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<select className="form-select form-select-sm" value={ selectedTopic } onChange={ event => setSelectedTopic(parseInt(event.target.value)) }>
|
||||
<option value={ -1 } disabled>CFH Topic</option>
|
||||
{ topics.map((topic, index) => <option key={ index } value={ index }>{LocalizeText('help.cfh.topic.' + topic.id)}</option>) }
|
||||
</select>
|
||||
<select className="form-select form-select-sm" value={ selectedAction } onChange={ event => setSelectedAction(parseInt(event.target.value)) }>
|
||||
<option value={ -1 } disabled>Sanction Type</option>
|
||||
{ MOD_ACTION_DEFINITIONS.map((action, index) => <option key={ index } value={ index }>{ action.name }</option>) }
|
||||
</select>
|
||||
<Column gap={ 1 }>
|
||||
<Text small>Optional message type, overrides default</Text>
|
||||
<textarea className="form-control" value={ message } onChange={ event => setMessage(event.target.value) }/>
|
||||
</Column>
|
||||
<Flex justifyContent="between" gap={ 1 }>
|
||||
<Button variant="danger" onClick={ sendSanction }>Sanction</Button>
|
||||
<Button variant="success" onClick={ sendDefaultSanction }>Default Sanction</Button>
|
||||
</Flex>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
import { GetRoomVisitsMessageComposer, RoomVisitsData, RoomVisitsEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { AutoSizer, List, ListRowProps } from 'react-virtualized';
|
||||
import { TryVisitRoom } from '../../../../api';
|
||||
import { Base, Column, Grid, Text } from '../../../../common';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
|
||||
interface ModToolsUserRoomVisitsViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsUserRoomVisitsView: FC<ModToolsUserRoomVisitsViewProps> = props =>
|
||||
{
|
||||
const { userId = null, onCloseClick = null } = props;
|
||||
const [ roomVisitData, setRoomVisitData ] = useState<RoomVisitsData>(null);
|
||||
|
||||
const onModtoolReceivedRoomsUserEvent = useCallback((event: RoomVisitsEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser || (parser.data.userId !== userId)) return;
|
||||
|
||||
setRoomVisitData(parser.data);
|
||||
}, [ userId ]);
|
||||
|
||||
CreateMessageHook(RoomVisitsEvent, onModtoolReceivedRoomsUserEvent);
|
||||
|
||||
const RowRenderer = (props: ListRowProps) =>
|
||||
{
|
||||
const item = roomVisitData.rooms[props.index];
|
||||
|
||||
return (
|
||||
<Grid key={ props.key } style={ props.style } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom">
|
||||
<Text className="g-col-2">{ item.enterHour.toString().padStart(2, '0') }: { item.enterMinute.toString().padStart(2, '0') }</Text>
|
||||
<Text className="g-col-7">{ item.roomName }</Text>
|
||||
<Text bold underline pointer variant="primary" className="g-col-3" onClick={ event => TryVisitRoom(item.roomId) }>Visit Room</Text>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetRoomVisitsMessageComposer(userId));
|
||||
}, [userId]);
|
||||
|
||||
if(!userId) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-visits" simple>
|
||||
<NitroCardHeaderView headerText={ 'User Visits' } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black" gap={ 1 }>
|
||||
<Column gap={ 0 } overflow="hidden">
|
||||
<Column gap={ 2 }>
|
||||
<Grid gap={ 1 } className="text-black fw-bold border-bottom pb-1">
|
||||
<Base className="g-col-2">Time</Base>
|
||||
<Base className="g-col-7">Room name</Base>
|
||||
<Base className="g-col-3">Visit</Base>
|
||||
</Grid>
|
||||
</Column>
|
||||
<Column className="log-container striped-children" overflow="auto" gap={ 0 }>
|
||||
{ roomVisitData &&
|
||||
<AutoSizer defaultWidth={ 400 } defaultHeight={ 200 }>
|
||||
{ ({ height, width }) =>
|
||||
{
|
||||
return (
|
||||
<List
|
||||
width={ width }
|
||||
height={ height }
|
||||
rowCount={ roomVisitData.rooms.length }
|
||||
rowHeight={ 20 }
|
||||
className={'log-entry-container' }
|
||||
rowRenderer={ RowRenderer }
|
||||
/>
|
||||
);
|
||||
} }
|
||||
</AutoSizer> }
|
||||
</Column>
|
||||
</Column>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import { ModMessageMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { Button, Text } from '../../../../common';
|
||||
import { NotificationAlertEvent } from '../../../../events';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { ISelectedUser } from '../../utils/ISelectedUser';
|
||||
|
||||
interface ModToolsUserSendMessageViewProps
|
||||
{
|
||||
user: ISelectedUser;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsUserSendMessageView: FC<ModToolsUserSendMessageViewProps> = props =>
|
||||
{
|
||||
const { user = null, onCloseClick = null } = props;
|
||||
const [ message, setMessage ] = useState('');
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
{
|
||||
if(message.trim().length === 0)
|
||||
{
|
||||
dispatchUiEvent(new NotificationAlertEvent([ 'Please write a message to user.' ], null, null, null, 'Error', null));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModMessageMessageComposer(user.userId, message, -999));
|
||||
|
||||
onCloseClick();
|
||||
}, [ message, user, onCloseClick ]);
|
||||
|
||||
if(!user) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-message" simple>
|
||||
<NitroCardHeaderView headerText={'Send Message'} onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<Text>Message To: { user.username }</Text>
|
||||
<textarea className="form-control" value={ message } onChange={ event => setMessage(event.target.value) }></textarea>
|
||||
<Button fullWidth onClick={ sendMessage }>Send message</Button>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
import { FriendlyTime, GetModeratorUserInfoMessageComposer, ModeratorUserInfoData, ModeratorUserInfoEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { ModToolsOpenUserChatlogEvent } from '../../../../../events/mod-tools/ModToolsOpenUserChatlogEvent';
|
||||
import { CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../layout';
|
||||
import { ModToolsUserModActionView } from '../user-mod-action/ModToolsUserModActionView';
|
||||
import { ModToolsUserRoomVisitsView } from '../user-room-visits/ModToolsUserRoomVisitsView';
|
||||
import { ModToolsSendUserMessageView } from '../user-sendmessage/ModToolsSendUserMessageView';
|
||||
import { ModToolsUserViewProps } from './ModToolsUserView.types';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { Button, Column, Grid } from '../../../../common';
|
||||
import { ModToolsOpenUserChatlogEvent } from '../../../../events/mod-tools/ModToolsOpenUserChatlogEvent';
|
||||
import { CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { ModToolsUserModActionView } from './ModToolsUserModActionView';
|
||||
import { ModToolsUserRoomVisitsView } from './ModToolsUserRoomVisitsView';
|
||||
import { ModToolsUserSendMessageView } from './ModToolsUserSendMessageView';
|
||||
|
||||
interface ModToolsUserViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
||||
|
||||
export const ModToolsUserView: FC<ModToolsUserViewProps> = props =>
|
||||
{
|
||||
@ -17,11 +23,6 @@ export const ModToolsUserView: FC<ModToolsUserViewProps> = props =>
|
||||
const [ modActionVisible, setModActionVisible ] = useState(false);
|
||||
const [ roomVisitsVisible, setRoomVisitsVisible ] = useState(false);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetModeratorUserInfoMessageComposer(userId));
|
||||
}, [ userId ]);
|
||||
|
||||
const onModtoolUserInfoEvent = useCallback((event: ModeratorUserInfoEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
@ -29,7 +30,7 @@ export const ModToolsUserView: FC<ModToolsUserViewProps> = props =>
|
||||
if(!parser || parser.data.userId !== userId) return;
|
||||
|
||||
setUserInfo(parser.data);
|
||||
}, [setUserInfo, userId]);
|
||||
}, [ userId ]);
|
||||
|
||||
CreateMessageHook(ModeratorUserInfoEvent, onModtoolUserInfoEvent);
|
||||
|
||||
@ -98,52 +99,58 @@ export const ModToolsUserView: FC<ModToolsUserViewProps> = props =>
|
||||
];
|
||||
}, [ userInfo ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetModeratorUserInfoMessageComposer(userId));
|
||||
}, [ userId ]);
|
||||
|
||||
if(!userInfo) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<NitroCardView className="nitro-mod-tools-user" simple={true}>
|
||||
<NitroCardView className="nitro-mod-tools-user" simple>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('modtools.userinfo.title', [ 'username' ], [ userInfo.userName ]) } onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<NitroLayoutGrid>
|
||||
<NitroLayoutGridColumn size={ 8 }>
|
||||
<Grid overflow="hidden">
|
||||
<Column size={ 8 } overflow="auto">
|
||||
<table className="table table-striped table-sm table-text-small text-black m-0">
|
||||
<tbody>
|
||||
{ userProperties.map( (property, index) =>
|
||||
{
|
||||
|
||||
return (
|
||||
<tr key={index}>
|
||||
<tr key={ index }>
|
||||
<th scope="row">{ LocalizeText(property.localeKey) }</th>
|
||||
<td>
|
||||
{ property.value }
|
||||
{ property.showOnline && <i className={ `icon icon-pf-${ userInfo.online ? 'online' : 'offline' } ms-2` } /> }
|
||||
</td>
|
||||
{ property.showOnline &&
|
||||
<i className={ `icon icon-pf-${ userInfo.online ? 'online' : 'offline' } ms-2` } /> }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}) }
|
||||
</tbody>
|
||||
</table>
|
||||
</NitroLayoutGridColumn>
|
||||
<NitroLayoutGridColumn size={ 4 }>
|
||||
<NitroLayoutButton variant="primary" size="sm" onClick={ event => dispatchUiEvent(new ModToolsOpenUserChatlogEvent(userId)) }>
|
||||
</Column>
|
||||
<Column size={ 4 } gap={ 1 }>
|
||||
<Button onClick={ event => dispatchUiEvent(new ModToolsOpenUserChatlogEvent(userId)) }>
|
||||
Room Chat
|
||||
</NitroLayoutButton>
|
||||
<NitroLayoutButton variant="primary" size="sm" onClick={ event => setSendMessageVisible(!sendMessageVisible) }>
|
||||
</Button>
|
||||
<Button onClick={ event => setSendMessageVisible(!sendMessageVisible) }>
|
||||
Send Message
|
||||
</NitroLayoutButton>
|
||||
<NitroLayoutButton variant="primary" size="sm" onClick={ event => setRoomVisitsVisible(!roomVisitsVisible) }>
|
||||
</Button>
|
||||
<Button onClick={ event => setRoomVisitsVisible(!roomVisitsVisible) }>
|
||||
Room Visits
|
||||
</NitroLayoutButton>
|
||||
<NitroLayoutButton variant="primary" size="sm" onClick={ event => setModActionVisible(!modActionVisible) }>
|
||||
</Button>
|
||||
<Button onClick={ event => setModActionVisible(!modActionVisible) }>
|
||||
Mod Action
|
||||
</NitroLayoutButton>
|
||||
</NitroLayoutGridColumn>
|
||||
</NitroLayoutGrid>
|
||||
</Button>
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{ sendMessageVisible &&
|
||||
<ModToolsSendUserMessageView user={ { userId: userId, username: userInfo.userName } } onCloseClick={ () => setSendMessageVisible(false) } /> }
|
||||
<ModToolsUserSendMessageView user={ { userId: userId, username: userInfo.userName } } onCloseClick={ () => setSendMessageVisible(false) } /> }
|
||||
{ modActionVisible &&
|
||||
<ModToolsUserModActionView user={ { userId: userId, username: userInfo.userName } } onCloseClick={ () => setModActionVisible(false) } /> }
|
||||
{ roomVisitsVisible &&
|
@ -1,5 +0,0 @@
|
||||
export interface ModToolsUserChatlogViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
.nitro-mod-tools-user {
|
||||
width: 350px;
|
||||
height: 370px;
|
||||
|
||||
.username {
|
||||
color: #1E7295;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: $black;
|
||||
|
||||
> :not(caption) > * > * {
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
&.table-striped > tbody > tr:nth-of-type(odd) {
|
||||
color: $black;
|
||||
background: rgba(0, 0, 0, .05);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface ModToolsUserViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,202 +0,0 @@
|
||||
import { CallForHelpTopicData, DefaultSanctionMessageComposer, ModAlertMessageComposer, ModBanMessageComposer, ModKickMessageComposer, ModMessageMessageComposer, ModMuteMessageComposer, ModTradingLockMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { NotificationAlertType } from '../../../../../views/notification-center/common/NotificationAlertType';
|
||||
import { NotificationUtilities } from '../../../../../views/notification-center/common/NotificationUtilities';
|
||||
import { useModToolsContext } from '../../../context/ModToolsContext';
|
||||
import { ModActionDefinition } from '../../../utils/ModActionDefinition';
|
||||
import { ModToolsUserModActionViewProps } from './ModToolsUserModActionView.types';
|
||||
|
||||
const actions = [
|
||||
new ModActionDefinition(1, 'Alert', ModActionDefinition.ALERT, 1, 0),
|
||||
new ModActionDefinition(2, 'Mute 1h', ModActionDefinition.MUTE, 2, 0),
|
||||
new ModActionDefinition(4, 'Ban 7 days', ModActionDefinition.BAN, 4, 0),
|
||||
new ModActionDefinition(3, 'Ban 18h', ModActionDefinition.BAN, 3, 0),
|
||||
new ModActionDefinition(5, 'Ban 30 days (step 1)', ModActionDefinition.BAN, 5, 0),
|
||||
new ModActionDefinition(7, 'Ban 30 days (step 2)', ModActionDefinition.BAN, 7, 0),
|
||||
new ModActionDefinition(6, 'Ban 100 years', ModActionDefinition.BAN, 6, 0),
|
||||
new ModActionDefinition(106, 'Ban avatar-only 100 years', ModActionDefinition.BAN, 6, 0),
|
||||
new ModActionDefinition(101, 'Kick', ModActionDefinition.KICK, 0, 0),
|
||||
new ModActionDefinition(102, 'Lock trade 1 week', ModActionDefinition.TRADE_LOCK, 0, 168),
|
||||
new ModActionDefinition(104, 'Lock trade permanent', ModActionDefinition.TRADE_LOCK, 0, 876000),
|
||||
new ModActionDefinition(105, 'Message', ModActionDefinition.MESSAGE, 0, 0),
|
||||
];
|
||||
|
||||
export const ModToolsUserModActionView: FC<ModToolsUserModActionViewProps> = props =>
|
||||
{
|
||||
const { user = null, onCloseClick = null } = props;
|
||||
const { modToolsState = null, dispatchModToolsState = null } = useModToolsContext();
|
||||
const { cfhCategories = null, settings = null } = modToolsState;
|
||||
const [ selectedTopic, setSelectedTopic ] = useState(-1);
|
||||
const [ selectedAction, setSelectedAction ] = useState(-1);
|
||||
const [ message, setMessage ] = useState<string>('');
|
||||
|
||||
const topics = useMemo(() =>
|
||||
{
|
||||
const values: CallForHelpTopicData[] = [];
|
||||
|
||||
if(!cfhCategories) return values;
|
||||
|
||||
for(let category of cfhCategories)
|
||||
{
|
||||
for(let topic of category.topics)
|
||||
{
|
||||
values.push(topic)
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}, [cfhCategories]);
|
||||
|
||||
const sendDefaultSanction = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new DefaultSanctionMessageComposer(user.userId, selectedTopic, message));
|
||||
onCloseClick();
|
||||
}, [message, onCloseClick, selectedTopic, user.userId]);
|
||||
|
||||
const sendSanction = useCallback(() =>
|
||||
{
|
||||
if( (selectedTopic === -1) || (selectedAction === -1) )
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You must select a CFH topic and Sanction', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!settings || !settings.cfhPermission)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You do not have permission to do this', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
const category = topics[selectedTopic];
|
||||
const sanction = actions[selectedAction];
|
||||
|
||||
if(!category)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You must select a CFH topic', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!sanction)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You must select a sanction', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
const messageOrDefault = message.trim().length === 0 ? LocalizeText('help.cfh.topic.' + category.id) : message;
|
||||
|
||||
switch(sanction.actionType)
|
||||
{
|
||||
case ModActionDefinition.ALERT:
|
||||
|
||||
if(!settings.alertPermission)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
if(message.trim().length === 0)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('Please write a message to user', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModAlertMessageComposer(user.userId, message, category.id));
|
||||
|
||||
break;
|
||||
case ModActionDefinition.MUTE:
|
||||
SendMessageHook(new ModMuteMessageComposer(user.userId, messageOrDefault, category.id));
|
||||
|
||||
break;
|
||||
case ModActionDefinition.BAN:
|
||||
|
||||
if(!settings.banPermission)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModBanMessageComposer(user.userId, messageOrDefault, category.id, selectedAction, (sanction.actionId === 106)));
|
||||
|
||||
break;
|
||||
|
||||
case ModActionDefinition.KICK:
|
||||
|
||||
if(!settings.kickPermission)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('You have insufficient permissions', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModKickMessageComposer(user.userId, messageOrDefault, category.id));
|
||||
|
||||
break;
|
||||
|
||||
case ModActionDefinition.TRADE_LOCK:
|
||||
{
|
||||
const numSeconds = sanction.actionLengthHours * 60;
|
||||
SendMessageHook(new ModTradingLockMessageComposer(user.userId, messageOrDefault, numSeconds, category.id));
|
||||
}
|
||||
break;
|
||||
|
||||
case ModActionDefinition.MESSAGE:
|
||||
|
||||
if(message.trim().length === 0)
|
||||
{
|
||||
NotificationUtilities.simpleAlert('Please write a message to user', NotificationAlertType.DEFAULT, null, null, 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModMessageMessageComposer(user.userId, message, category.id));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
onCloseClick();
|
||||
}, [message, onCloseClick, selectedAction, selectedTopic, settings, topics, user.userId]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-action" simple={true}>
|
||||
<NitroCardHeaderView headerText={'Mod Action: ' + (user ? user.username : '')} onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{ user &&
|
||||
<>
|
||||
<div className="form-group mb-2">
|
||||
<select className="form-control form-control-sm" value={selectedTopic} onChange={event => setSelectedTopic(parseInt(event.target.value))}>
|
||||
<option value={-1}>CFH Topic:</option>
|
||||
{ topics.map( (topic,index) =>
|
||||
{
|
||||
return (<option key={index} value={index}>{LocalizeText('help.cfh.topic.' + topic.id)}</option>)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group mb-2">
|
||||
<select className="form-control form-control-sm" value={selectedAction} onChange={event => setSelectedAction(parseInt(event.target.value))}>
|
||||
<option value={-1}>Sanction type:</option>
|
||||
{ actions.map( (action, index) =>
|
||||
{
|
||||
return (<option key={index} value={index}>{ action.name }</option>)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group mb-2">
|
||||
<label>Optional message type, overrides default</label>
|
||||
<textarea className="form-control" value={message} onChange={event => setMessage(event.target.value)}/>
|
||||
</div>
|
||||
|
||||
<div className="form-group mb-2">
|
||||
<div className="d-flex justify-content-between">
|
||||
<button type="button" className="btn btn-danger w-100 me-2" onClick={ () => sendSanction()}>Sanction</button>
|
||||
<button className="btn btn-success w-100" onClick={ () => sendDefaultSanction()}>Default Sanction</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { ISelectedUser } from '../../../utils/ISelectedUser';
|
||||
|
||||
export interface ModToolsUserModActionViewProps
|
||||
{
|
||||
user: ISelectedUser;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
.nitro-mod-tools-user-visits {
|
||||
min-width: 300px;
|
||||
|
||||
.user-visits {
|
||||
min-height: 200px;
|
||||
|
||||
.roomvisits-container {
|
||||
div.room-visit {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
import { GetRoomVisitsMessageComposer, RoomVisitsData, RoomVisitsEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { AutoSizer, List, ListRowProps, ListRowRenderer } from 'react-virtualized';
|
||||
import { TryVisitRoom } from '../../../../../api';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ModToolsUserRoomVisitsViewProps } from './ModToolsUserRoomVisitsView.types';
|
||||
|
||||
export const ModToolsUserRoomVisitsView: FC<ModToolsUserRoomVisitsViewProps> = props =>
|
||||
{
|
||||
const { userId = null, onCloseClick = null } = props;
|
||||
|
||||
const [roomVisitData, setRoomVisitData] = useState<RoomVisitsData>(null);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetRoomVisitsMessageComposer(userId));
|
||||
}, [userId]);
|
||||
|
||||
const onModtoolReceivedRoomsUserEvent = useCallback((event: RoomVisitsEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser || parser.data.userId !== userId) return;
|
||||
|
||||
setRoomVisitData(parser.data);
|
||||
}, [userId]);
|
||||
|
||||
CreateMessageHook(RoomVisitsEvent, onModtoolReceivedRoomsUserEvent);
|
||||
|
||||
const RowRenderer: ListRowRenderer = (props: ListRowProps) =>
|
||||
{
|
||||
const item = roomVisitData.rooms[props.index];
|
||||
|
||||
return (
|
||||
<div style={props.style} key={props.key} className="row room-visit align-items-center">
|
||||
<div className="col-auto text-center">{item.enterHour.toString().padStart(2, '0')}:{item.enterMinute.toString().padStart(2, '0')}</div>
|
||||
<div className="col-7"><span className="fw-bold">Room: </span>{item.roomName}</div>
|
||||
<button className="btn btn-sm btn-link col-auto fw-bold text-primary" onClick={() => TryVisitRoom(item.roomId)}>Visit Room</button>
|
||||
</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-visits" simple={true}>
|
||||
<NitroCardHeaderView headerText={'User Visits'} onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{roomVisitData &&
|
||||
<div className="row h-100 w-100 user-visits">
|
||||
<AutoSizer defaultWidth={400} defaultHeight={200}>
|
||||
{({ height, width }) =>
|
||||
{
|
||||
return (
|
||||
<List
|
||||
width={width}
|
||||
height={height}
|
||||
rowCount={roomVisitData.rooms.length}
|
||||
rowHeight={30}
|
||||
className={'roomvisits-container'}
|
||||
rowRenderer={RowRenderer}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
}
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
|
||||
export interface ModToolsUserRoomVisitsViewProps
|
||||
{
|
||||
userId: number;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { ISelectedUser } from '../../../utils/ISelectedUser';
|
||||
|
||||
export interface ModToolsSendUserMessageViewProps
|
||||
{
|
||||
user: ISelectedUser;
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import { ModMessageMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { NotificationAlertEvent } from '../../../../../events';
|
||||
import { dispatchUiEvent, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { ModToolsSendUserMessageViewProps } from './ModToolsSendUserMessage.types';
|
||||
|
||||
export const ModToolsSendUserMessageView: FC<ModToolsSendUserMessageViewProps> = props =>
|
||||
{
|
||||
const { user = null, onCloseClick = null } = props;
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
{
|
||||
if(message.trim().length === 0)
|
||||
{
|
||||
dispatchUiEvent(new NotificationAlertEvent(['Please write a message to user.'], null, null, null, 'Error', null));
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessageHook(new ModMessageMessageComposer(user.userId, message, -999));
|
||||
|
||||
onCloseClick();
|
||||
}, [message, onCloseClick, user.userId]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-mod-tools-user-message" simple={true}>
|
||||
<NitroCardHeaderView headerText={'Send Message'} onCloseClick={ () => onCloseClick() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{user && <>
|
||||
<div>Message To: {user.username}</div>
|
||||
<div className="form-group mb-2">
|
||||
<textarea className="form-control" value={message} onChange={e => setMessage(e.target.value)}></textarea>
|
||||
</div>
|
||||
|
||||
<div className="form-group mb-2">
|
||||
<button type="button" className="btn btn-primary" onClick={ () => sendMessage()}>Send message</button>
|
||||
</div>
|
||||
</>}
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -9,6 +9,7 @@ body {
|
||||
user-select: none;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
img {
|
||||
|
Loading…
Reference in New Issue
Block a user