Add Chats

This commit is contained in:
eugenijm 2020-05-07 16:10:53 +03:00
parent a0ddcbdf5b
commit aa2cf51c05
69 changed files with 2794 additions and 161 deletions

View file

@ -0,0 +1,109 @@
import { mapState, mapGetters } from 'vuex'
import Popover from '../popover/popover.vue'
import Attachment from '../attachment/attachment.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
import StatusContent from '../status_content/status_content.vue'
import ChatMessageDate from '../chat_message_date/chat_message_date.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
const ChatMessage = {
name: 'ChatMessage',
props: [
'author',
'edited',
'noHeading',
'chatViewItem',
'hoveredMessageChain'
],
components: {
Popover,
Attachment,
StatusContent,
UserAvatar,
Gallery,
LinkPreview,
ChatMessageDate
},
computed: {
// Returns HH:MM (hours and minutes) in local time.
createdAt () {
const time = this.chatViewItem.data.created_at
return time.toLocaleTimeString('en', { hour: '2-digit', minute: '2-digit', hour12: false })
},
isCurrentUser () {
return this.message.account_id === this.currentUser.id
},
message () {
return this.chatViewItem.data
},
userProfileLink () {
return generateProfileLink(this.author.id, this.author.screen_name, this.$store.state.instance.restrictedNicknames)
},
isMessage () {
return this.chatViewItem.type === 'message'
},
messageForStatusContent () {
return {
summary: '',
statusnet_html: this.message.content,
text: this.message.content,
attachments: this.message.attachments
}
},
hasAttachment () {
return this.message.attachments.length > 0
},
...mapState({
betterShadow: state => state.interface.browserSupport.cssFilter,
currentUser: state => state.users.currentUser,
restrictedNicknames: state => state.instance.restrictedNicknames
}),
ellipsisButtonWrapperStyle () {
let res = {
'opacity': this.hovered || this.menuOpened ? '1' : '0'
}
if (this.isCurrentUser) {
res.right = '5px'
} else {
res.left = '5px'
}
return res
},
popoverMarginStyle () {
if (this.isCurrentUser) {
return {}
} else {
return { left: 50 }
}
},
...mapGetters(['mergedConfig', 'findUser'])
},
data () {
return {
hovered: false,
menuOpened: false
}
},
methods: {
onHover (bool) {
this.$emit('hover', { isHovered: bool, messageChainId: this.chatViewItem.messageChainId })
},
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
}
}
}
export default ChatMessage

View file

@ -0,0 +1,157 @@
@import '../../_variables.scss';
.chat-message-wrapper {
&.hovered-message-chain {
.animated.avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
}
&:last-child {
margin-bottom: 16px;
}
.chat-message-menu {
transition: opacity 0.1s;
opacity: 0;
position: absolute;
top: -10px;
button {
padding-top: 3px;
padding-bottom: 3px;
}
}
.icon-ellipsis {
cursor: pointer;
&:hover, .extra-button-popover.open & {
color: $fallback--text;
color: var(--text, $fallback--text);
}
border-radius: $fallback--chatMessageRadius;
border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
}
.popover {
width: 12rem;
}
.chat-message {
display: flex;
padding-bottom: 7px;
}
.avatar-wrapper {
margin-right: 10px;
width: 32px;
}
.link-preview, .attachments {
margin-bottom: 0.9em;
}
.chat-message-inner {
display: flex;
flex-direction: column;
align-items: flex-start;
max-width: 80%;
min-width: 10rem;
width: 100%;
&.with-media {
width: 100%;
.gallery-row {
overflow: hidden;
}
.status {
width: 100%;
}
}
}
.status {
border-radius: $fallback--chatMessageRadius;
border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
display: flex;
padding: 0.75em;
}
.created-at {
float: right;
font-size: 0.8em;
margin: -10px 0 -5px 4px;
font-style: italic;
opacity: 0.8;
}
.without-attachment {
.status-content {
white-space: normal;
&::after {
margin-right: 75px;
content: " ";
display: inline-block;
}
}
}
.incoming {
a {
color: var(--chatMessageIncomingLink, $fallback--link);
}
.status {
color: var(--chatMessageIncomingText, $fallback--text);
background-color: var(--chatMessageIncomingBg, $fallback--bg);
border: 1px solid var(--chatMessageIncomingBorder, --border);
}
.created-at {
a {
color: var(--chatMessageIncomingText, $fallback--text);
}
}
}
.outgoing {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-content: end;
justify-content: flex-end;
a {
color: var(--chatMessageOutgoingLink, $fallback--link);
}
.status {
color: var(--chatMessageOutgoingText, $fallback--text);
background-color: var(--chatMessageOutgoingBg, $fallback--lightBg);
border: 1px solid var(--chatMessageOutgoingBorder, --lightBg);
}
.chat-message-inner {
align-items: flex-end;
}
}
}
.chat-message-date-separator {
text-align: center;
margin: 1.4em 0;
font-size: 0.9em;
user-select: none;
color: $fallback--text;
color: var(--faintedText, $fallback--text);
}

View file

@ -0,0 +1,99 @@
<template>
<div
v-if="isMessage"
class="chat-message-wrapper"
:class="{ 'hovered-message-chain': hoveredMessageChain }"
@mouseover="onHover(true)"
@mouseleave="onHover(false)"
>
<div
class="chat-message"
:class="[{ 'outgoing': isCurrentUser, 'incoming': !isCurrentUser }]"
>
<div
v-if="!isCurrentUser"
class="avatar-wrapper"
>
<router-link
v-if="chatViewItem.isHead"
:to="userProfileLink"
>
<UserAvatar
:compact="true"
:better-shadow="betterShadow"
:user="author"
/>
</router-link>
</div>
<div class="chat-message-inner">
<div
class="status-body"
:style="{ 'min-width': message.attachment ? '80%' : '' }"
>
<div
class="media status"
:class="{ 'without-attachment': !hasAttachment }"
style="position: relative"
@mouseenter="hovered = true"
@mouseleave="hovered = false"
>
<div
class="chat-message-menu"
:style="ellipsisButtonWrapperStyle"
>
<Popover
trigger="click"
placement="top"
:bound-to-selector="isCurrentUser ? '' : '.scrollable-message-list'"
:bound-to="{ x: 'container' }"
:margin="popoverMarginStyle"
@show="menuOpened = true"
@close="menuOpened = false"
>
<div slot="content">
<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-ellipsis" />
</button>
</Popover>
</div>
<StatusContent
:status="messageForStatusContent"
:full-content="true"
>
<span
slot="footer"
class="created-at"
>
{{ createdAt }}
</span>
</StatusContent>
</div>
</div>
</div>
</div>
</div>
<div
v-else
class="chat-message-date-separator"
>
<ChatMessageDate :date="chatViewItem.date" />
</div>
</template>
<script src="./chat_message.js" ></script>
<style lang="scss">
@import './chat_message.scss';
</style>