Merge branch '418-more-emojis-reaction-support' into 'dev'

ux: new emojis UI and display all added emojis reactions

See merge request keanuapp/keanuapp-weblite!174
This commit is contained in:
N Pex 2023-04-17 13:57:28 +00:00
commit e32afa78cf
7 changed files with 102 additions and 65 deletions

View file

@ -1,9 +1 @@
@mixin emoji-reaction-transform($type: 'right') { // Sass Mixins
@if $type == 'right' {
transform: translateX(20px) translateX(100%);
}
@if $type == 'left' {
transform: translateX(-20px) translateX(-100%);
}
}

View file

@ -352,6 +352,10 @@ body {
.message-wrapper { .message-wrapper {
position: relative; position: relative;
user-select: none; user-select: none;
.quick-reaction-container .emoji {
display: inline;
}
} }
.messageIn { .messageIn {
@ -438,6 +442,13 @@ body {
.link { .link {
color: inherit; color: inherit;
} }
.quick-reaction-container {
margin-left: 42px;
[dir="rtl"] & {
margin-right: 42px;
}
}
} }
.messageOut { .messageOut {
@ -539,6 +550,13 @@ body {
.link { .link {
color: inherit; color: inherit;
} }
.quick-reaction-container {
margin-right: 42px;
[dir="rtl"] & {
margin-left: 42px;
}
}
} }
.sender, .sender,
@ -712,53 +730,6 @@ body {
text-align: center; text-align: center;
} }
.quick-reaction-container {
display: inline-block;
position: relative;
vertical-align: bottom;
top: 18px;
z-index: 2;
background-color: #f7f7f7;
border: 1px solid rgba(white, 0.9);
border-radius: 13px;
height: 26px;
width: max-content;
padding: 0px 6px;
@include emoji-reaction-transform('left');
.messageOut & {
@include emoji-reaction-transform('right');
[dir="rtl"] & {
@include emoji-reaction-transform('left');
}
}
.quick-reaction {
position: relative;
top: -2px;
margin: 0px 0px;
padding: 1px;
font-size: 10px;
&:hover {
//border: 1px solid #888888;
background-color: #e2e2e2;
}
.quick-reaction-count {
color: #888888;
font-size: 0.7rem;
}
}
.sent .quick-reaction-count {
color: black;
font-weight: 700;
// background-color: palegreen;
}
[dir="rtl"] & {
@include emoji-reaction-transform('right');
}
}
.download-overlay { .download-overlay {
position: absolute; position: absolute;
left: 0; left: 0;

View file

@ -7,7 +7,11 @@
"global": { "global": {
"save": "Save", "save": "Save",
"password_didnot_match": "Password didn't match", "password_didnot_match": "Password didn't match",
"password_hint": "Minimum 12 character containing atleast one numeric, one uppercase and one lowercase letter" "password_hint": "Minimum 12 character containing atleast one numeric, one uppercase and one lowercase letter",
"show_less": "Show less",
"show_more": "Show more",
"add_reaction": "Add reaction",
"click_to_remove": "Click to remove"
}, },
"menu": { "menu": {
"start_private_chat": "Private chat with this user", "start_private_chat": "Private chat with this user",
@ -83,7 +87,8 @@
"time_ago": "Today | Yesterday | {count} days ago", "time_ago": "Today | Yesterday | {count} days ago",
"outgoing_message_deleted_text": "You deleted this message.", "outgoing_message_deleted_text": "You deleted this message.",
"incoming_message_deleted_text": "This message was deleted.", "incoming_message_deleted_text": "This message was deleted.",
"not_allowed_to_send": "Only admins and moderators are allowed to send to the room" "not_allowed_to_send": "Only admins and moderators are allowed to send to the room",
"reaction_count_more": "{reactionCount} more"
}, },
"room": { "room": {
"invitations": "You have no invitations | You have 1 invitation | You have {count} invitations", "invitations": "You have no invitations | You have 1 invitation | You have {count} invitations",

View file

@ -56,7 +56,12 @@
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction" :timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
v-on:context-menu="showContextMenuForEvent($event)" v-on:own-avatar-clicked="viewProfile" v-on:context-menu="showContextMenuForEvent($event)" v-on:own-avatar-clicked="viewProfile"
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)" v-on:download="download(event)" v-on:other-avatar-clicked="showAvatarMenuForEvent($event)" v-on:download="download(event)"
v-on:poll-closed="pollWasClosed(event)" /> v-on:poll-closed="pollWasClosed(event)"
v-on:more="
isEmojiQuickReaction = true
showMoreMessageOperations($event)
"
/>
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> --> <!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
<!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}</div> --> <!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}</div> -->
<div v-if="event.getId() == readMarker && index < events.length - 1" class="read-marker" <div v-if="event.getId() == readMarker && index < events.length - 1" class="read-marker"

View file

@ -20,7 +20,7 @@
<v-icon>more_vert</v-icon> <v-icon>more_vert</v-icon>
</v-btn> </v-btn>
</div> </div>
<QuickReactions :event="event" :timelineSet="timelineSet" /> <QuickReactions :event="event" :timelineSet="timelineSet" v-on="$listeners"/>
</div> </div>
</template> </template>

View file

@ -8,7 +8,6 @@
<div class="status">{{ event.status }}</div> <div class="status">{{ event.status }}</div>
</div> </div>
<QuickReactions :event="event" :timelineSet="timelineSet" />
<div class="op-button" ref="opbutton" v-if="!event.isRedacted()"> <div class="op-button" ref="opbutton" v-if="!event.isRedacted()">
<v-btn id="btn-show-menu" icon @click.stop="showContextMenu($refs.opbutton)"> <v-btn id="btn-show-menu" icon @click.stop="showContextMenu($refs.opbutton)">
<v-icon>more_vert</v-icon> <v-icon>more_vert</v-icon>
@ -25,6 +24,7 @@
<img v-if="userAvatar" :src="userAvatar" /> <img v-if="userAvatar" :src="userAvatar" />
<span v-else class="white--text headline">{{ userAvatarLetter }}</span> <span v-else class="white--text headline">{{ userAvatarLetter }}</span>
</v-avatar> </v-avatar>
<QuickReactions :event="event" :timelineSet="timelineSet" v-on="$listeners"/>
</div> </div>
</template> </template>

View file

@ -1,13 +1,67 @@
<template> <template>
<div class="quick-reaction-container" v-show="reactions"> <div class="quick-reaction-container" v-show="reactions">
<span :class="{'quick-reaction':true,'sent':value.includes($matrix.currentUserId)}" v-for="(value, name) in reactionMap" :key="name" @click.stop="onClickEmoji(name)"> <div
{{ name }} <span class="quick-reaction-count">{{ value.length }}</span> class="emoji"
</span> v-for="(value, name, index) in reactionMap"
:key="name"
v-show="showAllReaction || index < REACTION_LIMIT"
>
<v-tooltip top v-if="value.includes($matrix.currentUserId)">
<template v-slot:activator="{ on, attrs }">
<v-chip
class="pa-2 ma-1 ml-0"
outlined
small
v-bind="attrs"
v-on="on"
@click="onClickEmoji(name)"
>
{{ name }} {{ value.length }}
</v-chip>
</template>
<span v-if="value.includes($matrix.currentUserId)">{{ $t("global.click_to_remove") }}</span>
</v-tooltip>
<v-chip
v-else
class="pa-2 ma-1 ml-0"
outlined
small
>
{{ name }} {{ value.length }}
</v-chip>
</div>
<v-chip
v-if="totalReaction > REACTION_LIMIT"
@click="showAllReaction = !showAllReaction"
class="pa-2 ma-1 ml-0"
outlined
small
>
{{ otherReactionText }}
</v-chip>
<v-tooltip top v-if="!!totalReaction">
<template v-slot:activator="{ on, attrs }">
<v-chip
outlined
small
class="pa-2 ma-1 ml-0"
v-bind="attrs"
v-on="on"
@click="more"
>
<v-icon small> $vuetify.icons.addReaction </v-icon>
</v-chip>
</template>
<span>{{ $t("global.add_reaction") }}</span>
</v-tooltip>
</div> </div>
</template> </template>
<script> <script>
import messageOperationsMixin from "./messageOperationsMixin";
export default { export default {
mixins: [messageOperationsMixin],
props: { props: {
event: { event: {
type: Object, type: Object,
@ -25,7 +79,9 @@ export default {
data() { data() {
return { return {
reactionMap: {}, reactionMap: {},
reactions: null reactions: null,
REACTION_LIMIT: 5,
showAllReaction: false
} }
}, },
mounted() { mounted() {
@ -38,6 +94,14 @@ export default {
this.reactions.off('Relations.add', this.onAddRelation); this.reactions.off('Relations.add', this.onAddRelation);
} }
}, },
computed: {
totalReaction() {
return Object.keys(this.reactionMap).length
},
otherReactionText() {
return this.showAllReaction ? this.$t("global.show_less") : this.$t("message.reaction_count_more", { reactionCount: this.totalReaction - this.REACTION_LIMIT })
}
},
methods: { methods: {
onRelationsCreated() { onRelationsCreated() {
this.reactions = this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), 'm.annotation', 'm.reaction'); this.reactions = this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), 'm.annotation', 'm.reaction');