Merge branch 'chat-deletion' into 'develop'

Chat message deletion

Closes #358 and #391

See merge request soapbox-pub/soapbox-fe!247
chats-push-2
Alex Gleason 2020-09-22 22:16:15 +00:00
commit e8a9737fc9
8 zmienionych plików z 101 dodań i 5 usunięć

Wyświetl plik

@ -23,6 +23,10 @@ export const CHAT_READ_REQUEST = 'CHAT_READ_REQUEST';
export const CHAT_READ_SUCCESS = 'CHAT_READ_SUCCESS';
export const CHAT_READ_FAIL = 'CHAT_READ_FAIL';
export const CHAT_MESSAGE_DELETE_REQUEST = 'CHAT_MESSAGE_DELETE_REQUEST';
export const CHAT_MESSAGE_DELETE_SUCCESS = 'CHAT_MESSAGE_DELETE_SUCCESS';
export const CHAT_MESSAGE_DELETE_FAIL = 'CHAT_MESSAGE_DELETE_FAIL';
export function fetchChats() {
return (dispatch, getState) => {
dispatch({ type: CHATS_FETCH_REQUEST });
@ -150,3 +154,14 @@ export function markChatRead(chatId, lastReadId) {
});
};
}
export function deleteChatMessage(chatId, messageId) {
return (dispatch, getState) => {
dispatch({ type: CHAT_MESSAGE_DELETE_REQUEST, chatId, messageId });
api(getState).delete(`/api/v1/pleroma/chats/${chatId}/messages/${messageId}`).then(({ data }) => {
dispatch({ type: CHAT_MESSAGE_DELETE_SUCCESS, chatId, messageId, chatMessage: data });
}).catch(error => {
dispatch({ type: CHAT_MESSAGE_DELETE_FAIL, chatId, messageId, error });
});
};
}

Wyświetl plik

@ -25,6 +25,17 @@ export function initReport(account, status) {
};
};
export function initReportById(accountId) {
return (dispatch, getState) => {
dispatch({
type: REPORT_INIT,
account: getState().getIn(['accounts', accountId]),
});
dispatch(openModal('REPORT'));
};
};
export function cancelReport() {
return {
type: REPORT_CANCEL,

Wyświetl plik

@ -18,6 +18,7 @@ import IconButton from 'soapbox/components/icon_button';
const messages = defineMessages({
placeholder: { id: 'chat_box.input.placeholder', defaultMessage: 'Send a message…' },
send: { id: 'chat_box.actions.send', defaultMessage: 'Send' },
});
const mapStateToProps = (state, { chatId }) => ({
@ -164,11 +165,17 @@ class ChatBox extends ImmutablePureComponent {
}
renderActionButton = () => {
const { intl } = this.props;
const { resetFileKey } = this.state;
return this.canSubmit() ? (
<div className='chat-box__send'>
<IconButton icon='send' size={16} onClick={this.sendMessage} />
<IconButton
icon='send'
title={intl.formatMessage(messages.send)}
size={16}
onClick={this.sendMessage}
/>
</div>
) : (
<UploadButton onSelectFile={this.handleFiles} resetFileKey={resetFileKey} />

Wyświetl plik

@ -5,16 +5,21 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, defineMessages } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { fetchChatMessages } from 'soapbox/actions/chats';
import { fetchChatMessages, deleteChatMessage } from 'soapbox/actions/chats';
import emojify from 'soapbox/features/emoji/emoji';
import classNames from 'classnames';
import { openModal } from 'soapbox/actions/modal';
import { escape, throttle } from 'lodash';
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
import Bundle from 'soapbox/features/ui/components/bundle';
import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container';
import { initReportById } from 'soapbox/actions/reports';
const messages = defineMessages({
today: { id: 'chats.dividers.today', defaultMessage: 'Today' },
more: { id: 'chats.actions.more', defaultMessage: 'More' },
delete: { id: 'chats.actions.delete', defaultMessage: 'Delete message' },
report: { id: 'chats.actions.report', defaultMessage: 'Report user' },
});
const timeChange = (prev, curr) => {
@ -198,7 +203,8 @@ class ChatMessageList extends ImmutablePureComponent {
parseContent = chatMessage => {
const content = chatMessage.get('content') || '';
const pending = chatMessage.get('pending', false);
const formatted = pending ? this.parsePendingContent(content) : content;
const deleting = chatMessage.get('deleting', false);
const formatted = (pending && !deleting) ? this.parsePendingContent(content) : content;
const emojiMap = makeEmojiMap(chatMessage);
return emojify(formatted, emojiMap.toJS());
}
@ -211,8 +217,24 @@ class ChatMessageList extends ImmutablePureComponent {
<div className='chat-messages__divider' key={key}>{text}</div>
)
handleDeleteMessage = (chatId, messageId) => {
return () => {
this.props.dispatch(deleteChatMessage(chatId, messageId));
};
}
handleReportUser = (userId) => {
return () => {
this.props.dispatch(initReportById(userId));
};
}
renderMessage = (chatMessage) => {
const { me } = this.props;
const { me, intl } = this.props;
const menu = [
{ text: intl.formatMessage(messages.delete), action: this.handleDeleteMessage(chatMessage.get('chat_id'), chatMessage.get('id')) },
{ text: intl.formatMessage(messages.report), action: this.handleReportUser(chatMessage.get('account_id')) },
];
return (
<div
@ -226,12 +248,22 @@ class ChatMessageList extends ImmutablePureComponent {
title={this.getFormattedTimestamp(chatMessage)}
className='chat-message__bubble'
ref={this.setBubbleRef}
tabIndex={0}
>
{this.maybeRenderMedia(chatMessage)}
<span
className='chat-message__content'
dangerouslySetInnerHTML={{ __html: this.parseContent(chatMessage) }}
/>
<div className='chat-message__menu'>
<DropdownMenuContainer
items={menu}
icon='ellipsis-h'
size={18}
direction='top'
title={intl.formatMessage(messages.more)}
/>
</div>
</div>
</div>
);

Wyświetl plik

@ -3,6 +3,7 @@ import {
CHAT_MESSAGES_FETCH_SUCCESS,
CHAT_MESSAGE_SEND_REQUEST,
CHAT_MESSAGE_SEND_SUCCESS,
CHAT_MESSAGE_DELETE_SUCCESS,
} from 'soapbox/actions/chats';
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
@ -59,6 +60,8 @@ export default function chatMessageLists(state = initialState, action) {
return updateList(state, action.chatId, action.chatMessages.map(chat => chat.id));
case CHAT_MESSAGE_SEND_SUCCESS:
return replaceMessage(state, action.chatId, action.uuid, action.chatMessage.id);
case CHAT_MESSAGE_DELETE_SUCCESS:
return state.update(action.chatId, chat => chat.delete(action.messageId));
default:
return state;
}

Wyświetl plik

@ -3,6 +3,8 @@ import {
CHAT_MESSAGES_FETCH_SUCCESS,
CHAT_MESSAGE_SEND_REQUEST,
CHAT_MESSAGE_SEND_SUCCESS,
CHAT_MESSAGE_DELETE_REQUEST,
CHAT_MESSAGE_DELETE_SUCCESS,
} from 'soapbox/actions/chats';
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
import { Map as ImmutableMap, fromJS } from 'immutable';
@ -43,6 +45,11 @@ export default function chatMessages(state = initialState, action) {
return importMessage(state, fromJS(action.chatMessage)).delete(action.uuid);
case STREAMING_CHAT_UPDATE:
return importLastMessages(state, fromJS([action.chat]));
case CHAT_MESSAGE_DELETE_REQUEST:
return state.update(action.messageId, chatMessage =>
chatMessage.set('pending', true).set('deleting', true));
case CHAT_MESSAGE_DELETE_SUCCESS:
return state.delete(action.messageId);
default:
return state;
}

Wyświetl plik

@ -146,14 +146,23 @@
max-width: 70%;
border-radius: 10px;
background-color: var(--background-color);
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: break-word;
white-space: break-spaces;
position: relative;
a {
color: var(--brand-color--hicontrast);
}
&:hover,
&:focus,
&:active, {
.chat-message__menu {
opacity: 1;
pointer-events: all;
}
}
}
&--me .chat-message__bubble {
@ -164,6 +173,17 @@
&--pending .chat-message__bubble {
opacity: 0.5;
}
&__menu {
position: absolute;
top: -8px;
right: -8px;
background: var(--background-color);
border-radius: 999px;
opacity: 0;
pointer-events: none;
transition: 0.2s;
}
}
.chat-list {

Wyświetl plik

@ -713,5 +713,6 @@
.react-toggle-track-check,
.react-toggle-track-x {
height: 16px;
color: white;
}
}