Implement "seen by" functionality

This commit is contained in:
N-Pex 2023-04-28 11:01:22 +02:00
parent e593b34326
commit 92ec6b0280
5 changed files with 139 additions and 2 deletions

View file

@ -356,6 +356,40 @@ body {
.quick-reaction-container .emoji {
display: inline;
}
.seen-by-container {
display: flex;
align-items: center;
justify-content: flex-end;
height: 16px;
.clickable {
display: flex;
height: 16px;
}
div {
height: 16px;
}
margin-top: 3px;
.more {
margin-right: 10px;
color: #444444;
font-size: 12px;
}
.seen-by-user {
width: 16px !important;
height: 16px !important;
margin-left: -5px !important;
vertical-align: top;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to /* .list-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateX(24px);
}
}
}
.messageIn {

View file

@ -88,7 +88,8 @@
"outgoing_message_deleted_text": "You deleted this message.",
"incoming_message_deleted_text": "This message was deleted.",
"not_allowed_to_send": "Only admins and moderators are allowed to send to the room",
"reaction_count_more": "{reactionCount} more"
"reaction_count_more": "{reactionCount} more",
"seen_by": "Seen by no members | Seen by 1 member | Seen by {count} members"
},
"room": {
"invitations": "You have no invitations | You have 1 invitation | You have {count} invitations",

View file

@ -21,14 +21,17 @@
</v-btn>
</div>
<QuickReactions :event="event" :timelineSet="timelineSet" v-on="$listeners"/>
<SeenBy :room="room" :event="event"/>
</div>
</template>
<script>
import SeenBy from "./SeenBy.vue";
import messageMixin from "./messageMixin";
export default {
mixins: [messageMixin],
mixins: [messageMixin],
components: { SeenBy }
};
</script>

View file

@ -25,14 +25,17 @@
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
</v-avatar>
<QuickReactions :event="event" :timelineSet="timelineSet" v-on="$listeners"/>
<SeenBy :room="room" :event="event"/>
</div>
</template>
<script>
import SeenBy from "./SeenBy.vue";
import messageMixin from "./messageMixin";
export default {
mixins: [messageMixin],
components: { SeenBy }
};
</script>
<style lang="scss">

View file

@ -0,0 +1,96 @@
<template>
<div class="seen-by-container">
<v-tooltip top open-delay="500">
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on" class="clickable">
<div class="more" v-if="seenBy.length > 0">{{ moreItems }}</div>
<transition-group name="list" tag="div" v-if="seenBy.length > 0">
<v-avatar v-for="(member, index) in seenBy" :key="member.userId" class="seen-by-user" size="16" color="grey"
v-show="index < SHOW_LIMIT">
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
<span v-else class="white--text headline">{{
member.name.substring(0, 1).toUpperCase()
}}</span>
</v-avatar>
</transition-group>
</div>
</template>
<span>{{ $tc("message.seen_by", seenBy.length) }}</span>
</v-tooltip>
</div>
</template>
<script>
export default {
props: {
room: {
type: Object,
default: function () {
return null;
},
},
event: {
type: Object,
default: function () {
return null;
}
},
},
data() {
return {
seenBy: [],
SHOW_LIMIT: 5,
}
},
mounted() {
this.update();
if (this.room) {
this.room.on("Room.receipt", this.onReceipt);
}
},
beforeDestroy() {
if (this.room) {
this.room.off("Room.receipt", this.onReceipt);
}
},
computed: {
moreItems() {
if (this.seenBy.length > this.SHOW_LIMIT) {
return `+${this.seenBy.length - this.SHOW_LIMIT}`;
}
return "";
}
},
methods: {
onReceipt(ignoredevent) {
this.update();
},
memberAvatar(member) {
if (member) {
return member.getAvatarUrl(
this.$matrix.matrixClient.getHomeserverUrl(),
16,
16,
"scale",
true
);
}
return null;
},
update() {
this.seenBy = ((this.room && this.event) ? this.room.getReceiptsForEvent(this.event) : [])
.filter(receipt => receipt.type == 'm.read' && receipt.userId !== this.$matrix.currentUserId)
.map(receipt => this.room.getMember(receipt.userId));
},
},
watch: {
event() {
this.update();
}
}
};
</script>
<style lang="scss">
@import "@/assets/css/chat.scss";
</style>