Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chat): message replies #4102

Merged
merged 7 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions components/views/chat/chatbar/Chatbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const Chatbar = Vue.extend({
isSharpCorners(): boolean {
return (
Boolean(this.files.length) ||
Boolean(this.ui.replyChatbarContent.id) ||
Boolean(this.ui.replyChatbarMessage) ||
this.commandPreview ||
this.chat.countError
)
Expand Down Expand Up @@ -133,11 +133,7 @@ const Chatbar = Vue.extend({
handler(value) {
const message = this.chat.draftMessages[this.conversationId]
this.$refs.editable?.resetHistory()
this.$store.commit('ui/setReplyChatbarContent', {
id: '',
payload: '',
from: '',
})
this.$store.commit('ui/clearReplyChatbarMessage')
this.$store.dispatch('ui/setChatbarContent', { content: message })
// in desktop, stay chatbar focused when switching recipient
if (this.$device.isDesktop) {
Expand Down Expand Up @@ -228,6 +224,7 @@ const Chatbar = Vue.extend({
body: value,
at: Date.now(),
attachments: [],
replyToId: this.ui.replyChatbarMessage.id,
})
// if (
// this.ui.replyChatbarContent.from &&
Expand Down
6 changes: 3 additions & 3 deletions components/views/chat/chatbar/reply/Reply.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div v-if="ui.replyChatbarContent.id" class="is-chatbar-reply">
<div v-if="ui.replyChatbarMessage" class="is-chatbar-reply">
<div class="markdown">
{{ $t('conversation.reply_to') }}
<strong>{{ ui.replyChatbarContent.from }}</strong>
<strong>{{ ui.replyChatbarMessage.from }}</strong>
</div>
<div class="reply-close" @click="setReplyChatbarContent">
<div class="reply-close" @click="clearReplyChatbarMessage">
<x-icon size="1x" />
</div>
</div>
13 changes: 2 additions & 11 deletions components/views/chat/chatbar/reply/Reply.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,8 @@ export default Vue.extend({
...mapState(['ui']),
},
methods: {
/**
* @method setReplyChatbarContent
* @description Adds message reply by committing chatbar content to setReplyChatbarContent in state
* @example @click="setReplyChatbarContent"
*/
setReplyChatbarContent() {
this.$store.commit('ui/setReplyChatbarContent', {
id: '',
payload: '',
from: '',
})
clearReplyChatbarMessage() {
this.$store.commit('ui/clearReplyChatbarMessage')
},
},
})
Expand Down
3 changes: 2 additions & 1 deletion components/views/chat/conversation/Conversation.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
/>
<UiCaretDivider v-if="item.isFirstUnreadMessage" text="New Messages" />
<Message
:message="item.message"
:key="item.message.id"
:message="item.message"
:showHeader="!item.isSameAuthor"
:replies="item.replies"
/>
</template>
</div>
Expand Down
51 changes: 30 additions & 21 deletions components/views/chat/conversation/Conversation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface ChatItem {
timeDiff: number
isNextDay: boolean
isFirstUnreadMessage: boolean
replies: (ConversationMessage & { id: string })[]
}

export default Vue.extend({
Expand All @@ -29,28 +30,36 @@ export default Vue.extend({
return iridium.connector?.id ?? ''
},
chatItems(): ChatItem[] {
return this.messages.map((message, index) => {
const prevMessage = index >= 0 ? this.messages[index - 1] : undefined
const isSameAuthor = prevMessage
? message.from === prevMessage.from
: false
const timeDiff = prevMessage ? message.at - prevMessage.at : 0
const isNextDay = prevMessage
? !this.$dayjs(prevMessage.at).isSame(message.at, 'day')
: false
const lastReadAt = this.conversation.lastReadAt
const isFirstUnreadMessage =
message.at > lastReadAt &&
(prevMessage ? prevMessage.at <= lastReadAt : true)
return this.messages
.filter((message) => message.replyToId)
.map((message, index) => {
const prevMessage = index >= 0 ? this.messages[index - 1] : undefined
const isSameAuthor = prevMessage
? message.from === prevMessage.from
: false
const timeDiff = prevMessage ? message.at - prevMessage.at : 0
const isNextDay = prevMessage
? !this.$dayjs(prevMessage.at).isSame(message.at, 'day')
: false
const lastReadAt = this.conversation.lastReadAt
const isFirstUnreadMessage =
message.at > lastReadAt &&
(prevMessage ? prevMessage.at <= lastReadAt : true)
const replies = this.messages.filter(
(replyMessage) => replyMessage.replyToId === message.id,
)

return {
message,
isSameAuthor,
timeDiff,
isNextDay,
isFirstUnreadMessage,
}
})
console.log('replies', replies)

return {
message,
isSameAuthor,
timeDiff,
isNextDay,
isFirstUnreadMessage,
replies,
}
})
},
},
methods: {},
Expand Down
92 changes: 45 additions & 47 deletions components/views/chat/message/Message.html
Original file line number Diff line number Diff line change
@@ -1,52 +1,50 @@
<UiContextMenu
class="chat-message"
:class="{ 'show-header': showHeader }"
:items="contextMenuValues"
>
<template v-if="showHeader">
<div class="avatar">
<UiCircle
:type="author.src ? 'image' :'random'"
:seed="author.id"
:source="author.src"
<UiContextMenu class="chat-message-container" :items="contextMenuValues">
<div
class="chat-message"
:class="{ 'show-header': showHeader, 'is-replying-to': isReplyingTo }"
>
<template v-if="showHeader">
<div class="avatar">
<UiCircle
:type="author.src ? 'image' :'random'"
:seed="author.id"
:source="author.src"
/>
</div>
<div class="header">
<span :click="showQuickProfile">
<UiUserName :username="author.name" />
</span>
</div>
</template>
<div class="timestamp">{{ timestamp }}</div>
<div class="body">
<MessageMarkdown
:text="wrapEmoji(markdownToHtml(message.body))"
class="markdown"
:class="{ bigmoji: containsOnlyEmoji(message.body) }"
data-cy="chat-message"
/>
</div>
<div class="header">
<span :click="showQuickProfile">
<UiUserName :username="author.name" />
<span class="status editing" v-if="message.editingAt">
<UiLoadersSpinner spinning />
</span>
<span
class="status edited"
data-cy="message-edited"
v-else-if="message.editedAt"
>
({{$t('ui.edited')}})
</span>
<MessageActions
:setReplyChatbarMessage="setReplyChatbarMessage"
:emojiReaction="emojiReaction"
:editMessage="editMessage"
:message="message"
/>
</div>
<div class="footer">
<MessageReactions :message="message" :emojiReaction="emojiReaction" />
<MessageReplies :replies="replies" />
</div>
</template>
<div class="timestamp">{{ timestamp }}</div>
<div class="body">
<MessageMarkdown
v-if="message.body"
:text="wrapEmoji(markdownToHtml(message.body))"
class="markdown"
:class="{ bigmoji: containsOnlyEmoji(message.body) }"
data-cy="chat-message"
/>
<span class="status editing" v-if="message.editingAt">
<UiLoadersSpinner spinning />
</span>
<span
class="status edited"
data-cy="message-edited"
v-else-if="message.editedAt"
>
({{$t('ui.edited')}})
</span>
<MessageGlyph
v-if="message.glyph"
:source="message.glyph.src"
:pack="message.glyph.pack"
/>
<MessageActions
:setReplyChatbarContent="setReplyChatbarContent"
:emojiReaction="emojiReaction"
:editMessage="editMessage"
:message="message"
/>
<MessageReactions :message="message" :emojiReaction="emojiReaction" />
</div>
</UiContextMenu>
32 changes: 24 additions & 8 deletions components/views/chat/message/Message.less
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,41 @@
.chat-message {
display: grid;
text-align: left;
// border-left: 3px solid transparent;
padding-left: @xlight-spacing;

&.is-replying-to {
background-color: fade(@satellite-color, 10%);
// border-left-color: @satellite-color;
}

&:not(.show-header) {
grid-template-areas: 'timestamp body';
grid-template-areas:
'timestamp body'
'timestamp footer';
grid-template-columns: 52px;

.timestamp {
> .timestamp {
opacity: 0;
}

&:hover,
&:focus-within {
.timestamp {
> .timestamp {
opacity: 1;
}
}

.timestamp {
> .timestamp {
line-height: 24px;
}
}

&.show-header {
grid-template-areas:
'avatar header timestamp'
'avatar body body';
'avatar body body'
'avatar footer footer';
grid-template-columns: 52px auto 1fr;
margin-top: @normal-spacing;

Expand Down Expand Up @@ -77,12 +87,18 @@
}
}

&:hover {
.body {
.footer {
grid-area: footer;
background: none;
padding: 0;
}

&:not(.is-replying-to):hover {
> .body > .markdown {
&:extend(.background-semitransparent-light);
}

.message-actions {
> .body > .message-actions {
opacity: 1;
}
}
Expand Down
41 changes: 11 additions & 30 deletions components/views/chat/message/Message.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
<script lang="ts">
import Vue, { PropType } from 'vue'
import { mapState, mapGetters } from 'vuex'

import { ArchiveIcon } from 'satellite-lucide-icons'
import { UIMessage, Group } from '~/types/messaging'

import { toHTML } from '~/libraries/ui/Markdown'
import { ContextMenuItem, EmojiUsage, ModalWindows } from '~/store/ui/types'
import { isMimeEmbeddableImage } from '~/utilities/FileType'
Expand All @@ -23,9 +20,13 @@ export default Vue.extend({
},
props: {
message: {
type: Object as PropType<ConversationMessage>,
type: Object as PropType<ConversationMessage & { id: string }>,
required: true,
},
replies: {
type: Array as PropType<(ConversationMessage & { id: string })[]>,
default: [] as (ConversationMessage & { id: string })[],
},
showHeader: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -67,6 +68,9 @@ export default Vue.extend({
// }
// return this.groups[this.conversation.id]
},
isReplyingTo(): boolean {
return this.ui.replyChatbarMessage?.id === this.message.id
},
timestamp(): string {
return this.getTimestamp({ time: this.message.at })
},
Expand All @@ -80,7 +84,7 @@ export default Vue.extend({
const mainList = [
{ text: 'quickReaction', func: this.quickReaction },
{ text: this.$t('context.reaction'), func: this.emojiReaction },
{ text: this.$t('context.reply'), func: this.setReplyChatbarContent },
{ text: this.$t('context.reply'), func: this.setReplyChatbarMessage },
// AP-1120 copy link functionality
// { text: this.$t('context.copy_link'), func: (this as any).testFunc },
]
Expand Down Expand Up @@ -239,31 +243,8 @@ export default Vue.extend({
}
})
},
/**
* @method setReplyChatbarContent DocsTODO
* @description
* @example
*/
setReplyChatbarContent() {
if (this.isGroup) {
this.toggleModal(ModalWindows.CALL_TO_ACTION)
return
}
const myTextilePublicKey = this.$TextileManager.getIdentityPublicKey()
const { id, type, payload, to, from } = this.message
let finalPayload = payload
if (['image', 'video', 'audio', 'file'].includes(type)) {
finalPayload = `*${this.$t('conversation.multimedia')}*`
} else if (type === 'glyph') {
finalPayload = `<img src=${payload} width='16px' height='16px' />`
}
this.$store.commit('ui/setReplyChatbarContent', {
id,
payload: finalPayload,
from: this.from,
messageID: this.message.id,
to: to === myTextilePublicKey ? from : to,
})
setReplyChatbarMessage() {
this.$store.commit('ui/setReplyChatbarMessage', this.message)
this.$nextTick(() => this.$store.dispatch('ui/setChatbarFocus'))
},
/**
Expand Down
2 changes: 1 addition & 1 deletion components/views/chat/message/actions/Actions.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class="reply-command"
:class="{'coming-soon': isGroup}"
v-tooltip.top="$t(isGroup ? 'ui.coming_soon' : 'messaging.reply')"
@click="setReplyChatbarContent"
@click="setReplyChatbarMessage"
>
<corner-down-right-icon size="1x" :class="'control-icon'" />
</div>
Expand Down
Loading