Merge branch 'chat-deletion' into 'develop'
Chat message deletion Closes #358 and #391 See merge request soapbox-pub/soapbox-fe!247
This commit is contained in:
commit
e8a9737fc9
8 changed files with 101 additions and 5 deletions
|
@ -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 });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -713,5 +713,6 @@
|
|||
.react-toggle-track-check,
|
||||
.react-toggle-track-x {
|
||||
height: 16px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue