140 lines
No EOL
4 KiB
Vue
140 lines
No EOL
4 KiB
Vue
<template>
|
|
<div>
|
|
<div class="seen-by-container">
|
|
<v-tooltip location="top" open-delay="500" v-if="seenBy.length > 0">
|
|
<template v-slot:activator="{ props }">
|
|
<div v-bind="props" 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.roomMember.userId" class="seen-by-user" size="16" color="grey"
|
|
v-show="index < SHOW_LIMIT" @click="open">
|
|
<AuthedImage v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
|
<span v-else class="text-white headline">{{
|
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
|
}}</span>
|
|
</v-avatar>
|
|
</transition-group>
|
|
</div>
|
|
</template>
|
|
<span>{{ $t("message.seen_by_count", seenBy.length) }}</span>
|
|
</v-tooltip>
|
|
</div>
|
|
<BottomSheet ref="seenByListBottomSheet">
|
|
<v-list>
|
|
<v-list-subheader class="text-uppercase"> {{ $t("message.seen_by") }}</v-list-subheader>
|
|
<v-list-item v-for="(member, index) in seenBy" :key="index" class="text-left">
|
|
<template v-slot:prepend>
|
|
<v-avatar size="40" color="grey">
|
|
<AuthedImage v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
|
<span v-else class="text-white headline">{{
|
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
|
}}</span>
|
|
</v-avatar>
|
|
</template>
|
|
<v-list-item-title>{{member.roomMember.name}}</v-list-item-title>
|
|
<v-list-item-subtitle>{{ seenByTimeStamp(member.readTimestamp) }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</v-list>
|
|
</BottomSheet>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import BottomSheet from "../BottomSheet.vue";
|
|
import AuthedImage from "../AuthedImage.vue";
|
|
import utils from "../../plugins/utils.js";
|
|
|
|
export default {
|
|
components: {
|
|
BottomSheet,
|
|
AuthedImage
|
|
},
|
|
props: {
|
|
room: {
|
|
type: Object,
|
|
default: function () {
|
|
return null;
|
|
},
|
|
},
|
|
event: {
|
|
type: Object,
|
|
default: function () {
|
|
return null;
|
|
}
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
seenBy: [],
|
|
SHOW_LIMIT: 5,
|
|
utils
|
|
}
|
|
},
|
|
mounted() {
|
|
this.update();
|
|
if (this.room) {
|
|
this.room.on("Room.receipt", this.onReceipt);
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
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: {
|
|
seenByTimeStamp(timestamp) {
|
|
let dayDiff = utils.dayDiffToday(timestamp);
|
|
if (dayDiff < 3) {
|
|
return this.$t("message.time_ago", dayDiff) + ' '+utils.formatTime(timestamp);
|
|
} else {
|
|
return utils.formatTime(timestamp);
|
|
}
|
|
},
|
|
open() {
|
|
this.$refs.seenByListBottomSheet.open();
|
|
},
|
|
onReceipt(ignoredevent) {
|
|
this.update();
|
|
},
|
|
memberAvatar(member) {
|
|
if (member) {
|
|
return member.getAvatarUrl(
|
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
16,
|
|
16,
|
|
"scale",
|
|
true,
|
|
false,
|
|
this.$matrix.useAuthedMedia
|
|
);
|
|
}
|
|
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 => {
|
|
return { readTimestamp: receipt.data.ts, roomMember: this.room.getMember(receipt.userId) }
|
|
}
|
|
);
|
|
},
|
|
},
|
|
watch: {
|
|
event() {
|
|
this.update();
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@use "@/assets/css/chat.scss" as *;
|
|
</style> |