Chat message deletion

This commit is contained in:
eugenijm 2020-05-12 23:27:16 +03:00
parent 855f36eeda
commit d2456d5fea
12 changed files with 136 additions and 43 deletions

View file

@ -74,12 +74,16 @@ const Chat = {
return this.$t('chats.write_message') return this.$t('chats.write_message')
} }
}, },
customRef () {
return this.$store.state.chats.ref
},
...mapGetters(['currentChat', 'currentChatMessageService', 'findUser']), ...mapGetters(['currentChat', 'currentChatMessageService', 'findUser']),
...mapState({ ...mapState({
backendInteractor: state => state.api.backendInteractor, backendInteractor: state => state.api.backendInteractor,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
isMobileLayout: state => state.interface.mobileLayout, isMobileLayout: state => state.interface.mobileLayout,
openedChats: state => state.chats.openedChats openedChats: state => state.chats.openedChats,
openedChatMessageServices: state => state.chats.openedChatMessageServices
}) })
}, },
watch: { watch: {
@ -92,6 +96,11 @@ const Chat = {
} }
}) })
}, },
'currentChatMessageService.messages.length': {
handler: function () {
this.chatViewItems = chatService.getView(this.currentChatMessageService)
}
},
'$route': function (prev, next) { '$route': function (prev, next) {
this.recipientId = this.$route.params.recipient_id this.recipientId = this.$route.params.recipient_id
this.startFetching() this.startFetching()
@ -108,11 +117,6 @@ const Chat = {
this.scrollDown({ forceRead: true }) this.scrollDown({ forceRead: true })
}) })
}, },
onScopeNoticeDismissed () {
this.$nextTick(() => {
this.updateSize()
})
},
onFilesDropped () { onFilesDropped () {
this.$nextTick(() => { this.$nextTick(() => {
this.updateSize() this.updateSize()
@ -327,7 +331,6 @@ const Chat = {
let bottomedOut = this.bottomedOut() let bottomedOut = this.bottomedOut()
this.loadingOlderMessages = false this.loadingOlderMessages = false
this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => { this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => {
this.chatViewItems = chatService.getView(this.currentChatMessageService)
if (positionBeforeLoading) { if (positionBeforeLoading) {
this.$nextTick(() => { this.$nextTick(() => {
let positionAfterLoading = this.getPosition() let positionAfterLoading = this.getPosition()

View file

@ -14,7 +14,10 @@
class="go-back-button" class="go-back-button"
@click="goBack" @click="goBack"
> --> > -->
<i class="button-icon icon-left-open" @click="goBack" /> <i
class="button-icon icon-left-open"
@click="goBack"
/>
<!-- </a> --> <!-- </a> -->
<div class="title text-center"> <div class="title text-center">
<ChatTitle <ChatTitle
@ -35,7 +38,7 @@
style="margin-right: 0.3em;" style="margin-right: 0.3em;"
@click="goBack" @click="goBack"
> --> > -->
<i class="button-icon icon-info-circled" /> <i class="button-icon icon-info-circled" />
<!-- </a> --> <!-- </a> -->
</div> </div>
</div> </div>

View file

@ -1,4 +1,5 @@
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import Popover from '../popover/popover.vue'
import Attachment from '../attachment/attachment.vue' import Attachment from '../attachment/attachment.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import Gallery from '../gallery/gallery.vue' import Gallery from '../gallery/gallery.vue'
@ -16,6 +17,7 @@ const ChatMessage = {
'sequenceHovered' 'sequenceHovered'
], ],
components: { components: {
Popover,
Attachment, Attachment,
StatusContent, StatusContent,
UserAvatar, UserAvatar,
@ -46,30 +48,45 @@ const ChatMessage = {
return this.chatViewItem.type === 'message' return this.chatViewItem.type === 'message'
}, },
messageForStatusContent () { messageForStatusContent () {
let result = { return {
summary: '', summary: '',
statusnet_html: this.message.content, statusnet_html: this.message.content,
text: this.message.content text: this.message.content,
attachments: this.message.attachments
} }
if (this.message.attachment) {
result.attachments = [this.message.attachment]
} else {
result.attachments = []
}
return result
}, },
...mapState({ ...mapState({
betterShadow: state => state.interface.browserSupport.cssFilter, betterShadow: state => state.interface.browserSupport.cssFilter,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
restrictedNicknames: state => state.instance.restrictedNicknames restrictedNicknames: state => state.instance.restrictedNicknames
}), }),
wrapperStyle () {
return {
'opacity': this.hovered || this.menuOpened ? '1' : '0'
}
},
...mapGetters(['mergedConfig', 'findUser']) ...mapGetters(['mergedConfig', 'findUser'])
}, },
data () {
return {
hovered: false,
menuOpened: false
}
},
methods: { methods: {
onHover (bool) { onHover (bool) {
this.$emit('hover', { state: bool, sequenceId: this.chatViewItem.sequenceId }) this.$emit('hover', { state: bool, sequenceId: this.chatViewItem.sequenceId })
},
async deleteMessage () {
const confirmed = window.confirm(this.$t('chats.delete_confirm'))
if (confirmed) {
await this.$store.dispatch('deleteChatMessage', {
messageId: this.chatViewItem.data.id,
chatId: this.chatViewItem.data.chat_id
})
}
this.hovered = false
this.menuOpened = false
} }
} }
} }

View file

@ -24,16 +24,8 @@
.status { .status {
padding: 0.75em; padding: 0.75em;
&:hover {
.chat-message-menu {
// visibility: visible;
opacity: 1;
}
}
.chat-message-menu { .chat-message-menu {
transition: opacity 0.1s; transition: opacity 0.1s;
// visibility: hidden;
opacity: 0; opacity: 0;
button { button {
@ -41,6 +33,16 @@
padding-bottom: 3px; padding-bottom: 3px;
} }
} }
.icon-ellipsis {
cursor: pointer;
&:hover,
.extra-button-popover.open & {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
} }
.direct-conversation { .direct-conversation {

View file

@ -26,24 +26,54 @@
</router-link> </router-link>
</div> </div>
<div class="direct-conversation-inner"> <div class="direct-conversation-inner">
<div class="status-body" :style="{ 'min-width': message.attachment ? '80%' : '' }"> <div
class="status-body"
:style="{ 'min-width': message.attachment ? '80%' : '' }"
>
<div <div
class="media status" class="media status"
style="position: relative" style="position: relative"
> @mouseenter="hovered = true"
@mouseleave="hovered = false"
>
<div <div
v-if="isCurrentUser"
class="chat-message-menu" class="chat-message-menu"
style="position: absolute; right: 5px; top: -10px" style="position: absolute; right: 5px; top: -10px"
:style="wrapperStyle"
>
<Popover
trigger="click"
placement="top"
:bound-to="{ x: 'container' }"
@show="menuOpened = true"
@close="menuOpened = false"
> >
<button <div
title="more" slot="content"
> slot-scope=""
<i class="icon-dot-3" /> >
</button> <div class="dropdown-menu">
<button
class="dropdown-item dropdown-item-icon"
@click="deleteMessage"
>
<i class="icon-cancel" /> {{ $t("chats.delete") }}
</button>
</div>
</div>
<button
slot="trigger"
:title="$t('chats.more')"
>
<i class="icon-dot-3" />
</button>
</Popover>
</div> </div>
<StatusContent <StatusContent
:status="messageForStatusContent" :status="messageForStatusContent"
:full-content="true"> :full-content="true"
>
<span <span
slot="footer" slot="footer"
class="created-at" class="created-at"

View file

@ -11,7 +11,10 @@
width="23px" width="23px"
height="23px" height="23px"
/> />
<span v-if="withAvatar" style="margin-right: 0.5em" /> <span
v-if="withAvatar"
style="margin-right: 0.5em"
/>
<span <span
v-for="(user, index) in otherUsersTruncated" v-for="(user, index) in otherUsersTruncated"
:key="user.id" :key="user.id"

View file

@ -46,7 +46,10 @@
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }} <i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
</router-link> </router-link>
<router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }" style="position: relative"> <router-link
:to="{ name: 'chats', params: { username: currentUser.screen_name } }"
style="position: relative"
>
<i class="button-icon icon-chat" /> {{ $t("nav.chats") }} <i class="button-icon icon-chat" /> {{ $t("nav.chats") }}
<span <span
v-if="unreadChatCount(currentChat)" v-if="unreadChatCount(currentChat)"

View file

@ -759,7 +759,9 @@
"delete": "Delete", "delete": "Delete",
"chats": "Chats", "chats": "Chats",
"new": "New Chat", "new": "New Chat",
"empty_message_error": "Cannot post empty message" "empty_message_error": "Cannot post empty message",
"more": "More",
"delete_confirm": "Do you really want to delete this message?"
}, },
"display_date": { "display_date": {
"today": "Today" "today": "Today"

View file

@ -1,3 +1,4 @@
import { set } from 'vue'
import { find, omitBy, debounce, last } from 'lodash' import { find, omitBy, debounce, last } from 'lodash'
import chatService from '../services/chat_service/chat_service.js' import chatService from '../services/chat_service/chat_service.js'
import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js' import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js'
@ -57,7 +58,7 @@ const chats = {
setCurrentChatFetcher ({ rootState, commit }, { fetcher }) { setCurrentChatFetcher ({ rootState, commit }, { fetcher }) {
commit('setCurrentChatFetcher', { fetcher }) commit('setCurrentChatFetcher', { fetcher })
}, },
addOpenedChat ({ commit, dispatch }, { chat }) { addOpenedChat ({ rootState, commit, dispatch }, { chat }) {
commit('addOpenedChat', { dispatch, chat: parseChat(chat) }) commit('addOpenedChat', { dispatch, chat: parseChat(chat) })
dispatch('addNewUsers', [chat.account]) dispatch('addNewUsers', [chat.account])
}, },
@ -85,6 +86,10 @@ const chats = {
rootState.api.backendInteractor.readChat({ id }).then(() => { rootState.api.backendInteractor.readChat({ id }).then(() => {
// dispatch('refreshCurrentUser') // dispatch('refreshCurrentUser')
}) })
},
deleteChatMessage ({ rootState, commit, dispatch }, value) {
rootState.api.backendInteractor.deleteChatMessage(value)
commit('deleteChatMessage', value)
} }
}, },
mutations: { mutations: {
@ -97,10 +102,10 @@ const chats = {
}, },
addOpenedChat (state, { _dispatch, chat }) { addOpenedChat (state, { _dispatch, chat }) {
state.currentChatId = chat.id state.currentChatId = chat.id
state.openedChats[chat.id] = chat set(state.openedChats, chat.id, chat)
if (!state.openedChatMessageServices[chat.id]) { if (!state.openedChatMessageServices[chat.id]) {
state.openedChatMessageServices[chat.id] = chatService.empty(chat.id) set(state.openedChatMessageServices, chat.id, chatService.empty(chat.id))
} }
}, },
setCurrentChatId (state, { chatId }) { setCurrentChatId (state, { chatId }) {
@ -152,6 +157,12 @@ const chats = {
chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) }) chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) })
} }
}, },
deleteChatMessage (state, { chatId, messageId }) {
const chatMessageService = state.openedChatMessageServices[chatId]
if (chatMessageService) {
chatService.deleteMessage(chatMessageService, messageId)
}
},
resetChatNewMessageCount (state, _value) { resetChatNewMessageCount (state, _value) {
const chatMessageService = state.openedChatMessageServices[state.currentChatId] const chatMessageService = state.openedChatMessageServices[state.currentChatId]
chatService.resetNewMessageCount(chatMessageService) chatService.resetNewMessageCount(chatMessageService)

View file

@ -82,6 +82,7 @@ const PLEROMA_CHATS_URL = `/api/v1/pleroma/chats`
const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}` const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}`
const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages` const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages`
const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read` const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read`
const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}`
const oldfetch = window.fetch const oldfetch = window.fetch
@ -1192,6 +1193,14 @@ const readChat = ({ id, credentials }) => {
}) })
} }
const deleteChatMessage = ({ chatId, messageId, credentials }) => {
return promisedRequest({
url: PLEROMA_DELETE_CHAT_MESSAGE_URL(chatId, messageId),
method: 'DELETE',
credentials
})
}
const apiService = { const apiService = {
verifyCredentials, verifyCredentials,
fetchTimeline, fetchTimeline,
@ -1273,7 +1282,8 @@ const apiService = {
getOrCreateChat, getOrCreateChat,
chatMessages, chatMessages,
postChatMessage, postChatMessage,
readChat readChat,
deleteChatMessage
} }
export default apiService export default apiService

View file

@ -11,6 +11,12 @@ const empty = (chatId) => {
} }
} }
const deleteMessage = (storage, messageId) => {
if (!storage) { return }
storage.messages = storage.messages.filter(m => m.id !== messageId)
delete storage.idIndex[messageId]
}
const add = (storage, { messages: newMessages }) => { const add = (storage, { messages: newMessages }) => {
if (!storage) { return } if (!storage) { return }
for (let i = 0; i < newMessages.length; i++) { for (let i = 0; i < newMessages.length; i++) {
@ -99,6 +105,7 @@ const ChatService = {
add, add,
empty, empty,
getView, getView,
deleteMessage,
resetNewMessageCount resetNewMessageCount
} }

View file

@ -389,7 +389,9 @@ export const parseChatMessage = (message) => {
output.chat_id = parseInt(message.chat_id, 10) output.chat_id = parseInt(message.chat_id, 10)
output.content = addEmojis(message.content, message.emojis) output.content = addEmojis(message.content, message.emojis)
if (message.attachment) { if (message.attachment) {
output.attachment = parseAttachment(message.attachment) output.attachments = [parseAttachment(message.attachment)]
} else {
output.attachments = []
} }
return output return output
} }