Inline chat moderation UI (#1331)

* - mock detect when user turns into moderator
- add moderator indicator to display on messages and username changer

* also mock moderator flag in message payload about user to display indicator

* add some menu looking icons and a menu of actions

* WIP chat moderators

* Add support for admin promoting a user to moderator

* WIP-
open a more info panel of user+message info; add some a11y to buttons

* style the details panel

* adjust positioning of menus

* Merge fixes. ChatClient->Client ChatServer->Server

* Remove moderator bool placeholders to use real state

* Support inline hiding of messages by moderators

* Support inline banning of chat users

* Cleanup linter warnings

* Puppeteer tests fail after typing take place

* Manually resolve conflicts in chat between moderator feature and develop

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
gingervitis
2021-11-02 19:27:41 -07:00
committed by GitHub
parent 4a52ba9f35
commit 9a91324456
23 changed files with 902 additions and 116 deletions

View File

@@ -6,7 +6,10 @@ import Message from './message.js';
import ChatInput from './chat-input.js';
import { CALLBACKS, SOCKET_MESSAGE_TYPES } from '../../utils/websocket.js';
import { jumpToBottom, debounce } from '../../utils/helpers.js';
import { extraUserNamesFromMessageHistory } from '../../utils/chat.js';
import {
extraUserNamesFromMessageHistory,
checkIsModerator,
} from '../../utils/chat.js';
import {
URL_CHAT_HISTORY,
MESSAGE_JUMPTOBOTTOM_BUFFER,
@@ -21,6 +24,7 @@ export default class Chat extends Component {
messages: [],
newMessagesReceived: false,
webSocketConnected: true,
isModerator: false,
};
this.scrollableMessagesContainer = createRef();
@@ -191,31 +195,34 @@ export default class Chat extends Component {
(item) => item.id === messageId
);
// check moderator status
if (messageType === SOCKET_MESSAGE_TYPES.CONNECTED_USER_INFO) {
const modStatusUpdate = checkIsModerator(message);
if (modStatusUpdate !== this.state.isModerator) {
this.setState({
isModerator: modStatusUpdate,
});
}
}
const updatedMessageList = [...curMessages];
// Change the visibility of messages by ID.
if (messageType === 'VISIBILITY-UPDATE') {
const idsToUpdate = message.ids;
const visible = message.visible;
updatedMessageList.forEach((item) => {
if (idsToUpdate.includes(item.id)) {
item.visible = visible;
}
this.forceRender = true;
this.setState({
messages: updatedMessageList,
});
});
return;
this.forceRender = true;
} else if (existingIndex === -1 && messageVisible) {
// insert message at timestamp
const convertedMessage = {
...message,
type: 'CHAT',
};
// insert message at timestamp
const insertAtIndex = curMessages.findIndex((item, index) => {
const time = item.timestamp || messageTimestamp;
const nextMessage =
@@ -252,7 +259,11 @@ export default class Chat extends Component {
}
// if window is blurred and we get a new message, add 1 to title
if (!readonly && messageType === 'CHAT' && this.windowBlurred) {
if (
!readonly &&
messageType === SOCKET_MESSAGE_TYPES.CHAT &&
this.windowBlurred
) {
this.numMessagesSinceBlur += 1;
}
}
@@ -366,10 +377,9 @@ export default class Chat extends Component {
}
render(props, state) {
const { username, readonly, chatInputEnabled, inputMaxBytes } = props;
const { messages, chatUserNames, webSocketConnected } = state;
this.forceRender = false;
const { username, readonly, chatInputEnabled, inputMaxBytes, accessToken } =
props;
const { messages, chatUserNames, webSocketConnected, isModerator } = state;
const messageList = messages
.filter((message) => message.visible !== false)
@@ -379,6 +389,8 @@ export default class Chat extends Component {
message=${message}
username=${username}
key=${message.id}
isModerator=${isModerator}
accessToken=${accessToken}
/>`
);