Merge branch '482-show-list-of-seenBy' into 'dev'
Show member list of seen by See merge request keanuapp/keanuapp-weblite!207
This commit is contained in:
commit
791fa5936a
8 changed files with 91 additions and 43 deletions
|
|
@ -208,7 +208,6 @@ body {
|
||||||
@media #{map-get($display-breakpoints, 'sm-and-down')} {
|
@media #{map-get($display-breakpoints, 'sm-and-down')} {
|
||||||
margin-top: 72px;
|
margin-top: 72px;
|
||||||
margin-bottom: 70px;
|
margin-bottom: 70px;
|
||||||
z-index: 9;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,8 @@
|
||||||
"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",
|
"reaction_count_more": "{reactionCount} more",
|
||||||
"seen_by": "Seen by no members | Seen by 1 member | Seen by {count} members",
|
"seen_by_count": "Seen by no members | Seen by 1 member | Seen by {count} members",
|
||||||
|
"seen_by": "Seen by",
|
||||||
"file": "File",
|
"file": "File",
|
||||||
"files": "Files",
|
"files": "Files",
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ export default {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
z-index: 10;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-sheet-bg {
|
.bottom-sheet-bg {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<div v-if="showSenderAndTime" class="senderAndTime">
|
<div v-if="showSenderAndTime" class="senderAndTime">
|
||||||
<div class="sender">{{ eventSenderDisplayName(event) }}</div>
|
<div class="sender">{{ eventSenderDisplayName(event) }}</div>
|
||||||
<div class="time">
|
<div class="time">
|
||||||
{{ formatTime(event.event.origin_server_ts) }}
|
{{ utils.formatTime(event.event.origin_server_ts) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<v-avatar class="avatar" ref="avatar" size="32" color="#ededed" @click.stop="otherAvatarClicked($refs.avatar.$el)">
|
<v-avatar class="avatar" ref="avatar" size="32" color="#ededed" @click.stop="otherAvatarClicked($refs.avatar.$el)">
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="messageOut">
|
<div class="messageOut">
|
||||||
<div class="senderAndTime">
|
<div class="senderAndTime">
|
||||||
<div class="time">
|
<div class="time">
|
||||||
{{ formatTime(event.event.origin_server_ts) }}
|
{{ utils.formatTime(event.event.origin_server_ts) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="status">{{ event.status }}</div>
|
<div class="status">{{ event.status }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,57 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="seen-by-container">
|
<div>
|
||||||
<v-tooltip top open-delay="500" v-if="seenBy.length > 0">
|
<div class="seen-by-container">
|
||||||
<template v-slot:activator="{ on, attrs }">
|
<v-tooltip top open-delay="500" v-if="seenBy.length > 0">
|
||||||
<div v-bind="attrs" v-on="on" class="clickable">
|
<template v-slot:activator="{ on, attrs }">
|
||||||
<div class="more" v-if="seenBy.length > 0">{{ moreItems }}</div>
|
<div v-bind="attrs" v-on="on" class="clickable">
|
||||||
<transition-group name="list" tag="div" v-if="seenBy.length > 0">
|
<div class="more" v-if="seenBy.length > 0">{{ moreItems }}</div>
|
||||||
<v-avatar v-for="(member, index) in seenBy" :key="member.userId" class="seen-by-user" size="16" color="grey"
|
<transition-group name="list" tag="div" v-if="seenBy.length > 0">
|
||||||
v-show="index < SHOW_LIMIT">
|
<v-avatar v-for="(member, index) in seenBy" :key="member.roomMember.userId" class="seen-by-user" size="16" color="grey"
|
||||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
v-show="index < SHOW_LIMIT" @click="open">
|
||||||
|
<img v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
||||||
|
<span v-else class="white--text headline">{{
|
||||||
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
||||||
|
}}</span>
|
||||||
|
</v-avatar>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span>{{ $tc("message.seen_by_count", seenBy.length) }}</span>
|
||||||
|
</v-tooltip>
|
||||||
|
</div>
|
||||||
|
<BottomSheet
|
||||||
|
:halfY="0.12"
|
||||||
|
ref="seenByListBottomSheet"
|
||||||
|
>
|
||||||
|
<v-list>
|
||||||
|
<v-subheader class="text-uppercase"> {{ $tc("message.seen_by") }}</v-subheader>
|
||||||
|
<v-list-item v-for="(member, index) in seenBy" :key="index">
|
||||||
|
<v-list-item-icon>
|
||||||
|
<v-avatar size="40" color="grey">
|
||||||
|
<img v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.name.substring(0, 1).toUpperCase()
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</transition-group>
|
</v-list-item-icon>
|
||||||
</div>
|
<v-list-item-content class="text-left">
|
||||||
</template>
|
<v-list-item-title>{{member.roomMember.name}}</v-list-item-title>
|
||||||
<span>{{ $tc("message.seen_by", seenBy.length) }}</span>
|
<v-list-item-subtitle>{{ seenByTimeStamp(member.readTimestamp) }}</v-list-item-subtitle>
|
||||||
</v-tooltip>
|
</v-list-item-content>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</BottomSheet>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import BottomSheet from "../BottomSheet.vue"
|
||||||
|
import utils from "../../plugins/utils.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
BottomSheet
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
room: {
|
room: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
@ -40,6 +70,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
seenBy: [],
|
seenBy: [],
|
||||||
SHOW_LIMIT: 5,
|
SHOW_LIMIT: 5,
|
||||||
|
utils
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -62,6 +93,17 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
seenByTimeStamp(timestamp) {
|
||||||
|
let dayDiff = utils.dayDiffToday(timestamp);
|
||||||
|
if (dayDiff < 3) {
|
||||||
|
return this.$tc("message.time_ago", dayDiff) + ' '+utils.formatTime(timestamp);
|
||||||
|
} else {
|
||||||
|
return utils.formatTime(timestamp);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open() {
|
||||||
|
this.$refs.seenByListBottomSheet.open();
|
||||||
|
},
|
||||||
onReceipt(ignoredevent) {
|
onReceipt(ignoredevent) {
|
||||||
this.update();
|
this.update();
|
||||||
},
|
},
|
||||||
|
|
@ -80,7 +122,10 @@ export default {
|
||||||
update() {
|
update() {
|
||||||
this.seenBy = ((this.room && this.event) ? this.room.getReceiptsForEvent(this.event) : [])
|
this.seenBy = ((this.room && this.event) ? this.room.getReceiptsForEvent(this.event) : [])
|
||||||
.filter(receipt => receipt.type == 'm.read' && receipt.userId !== this.$matrix.currentUserId)
|
.filter(receipt => receipt.type == 'm.read' && receipt.userId !== this.$matrix.currentUserId)
|
||||||
.map(receipt => this.room.getMember(receipt.userId));
|
.map(receipt => {
|
||||||
|
return { readTimestamp: receipt.data.ts, roomMember: this.room.getMember(receipt.userId) }
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import QuickReactions from "./QuickReactions.vue";
|
import QuickReactions from "./QuickReactions.vue";
|
||||||
import * as linkify from 'linkifyjs';
|
import * as linkify from 'linkifyjs';
|
||||||
import linkifyHtml from 'linkify-html';
|
import linkifyHtml from 'linkify-html';
|
||||||
|
import utils from "../../plugins/utils"
|
||||||
|
|
||||||
linkify.options.defaults.className = "link";
|
linkify.options.defaults.className = "link";
|
||||||
linkify.options.defaults.target = { url: "_blank" };
|
linkify.options.defaults.target = { url: "_blank" };
|
||||||
|
|
||||||
|
|
@ -39,6 +41,7 @@ export default {
|
||||||
event: {},
|
event: {},
|
||||||
inReplyToEvent: null,
|
inReplyToEvent: null,
|
||||||
inReplyToSender: null,
|
inReplyToSender: null,
|
||||||
|
utils
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -231,22 +234,6 @@ export default {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
formatTime(time) {
|
|
||||||
const date = new Date();
|
|
||||||
date.setTime(time);
|
|
||||||
|
|
||||||
const today = new Date();
|
|
||||||
if (
|
|
||||||
date.getDate() == today.getDate() &&
|
|
||||||
date.getMonth() == today.getMonth() &&
|
|
||||||
date.getFullYear() == today.getFullYear()
|
|
||||||
) {
|
|
||||||
// For today, skip the date part
|
|
||||||
return date.toLocaleTimeString();
|
|
||||||
}
|
|
||||||
return date.toLocaleString();
|
|
||||||
},
|
|
||||||
|
|
||||||
formatTimeAgo(time) {
|
formatTimeAgo(time) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setTime(time);
|
date.setTime(time);
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class Util {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttachment(matrixClient, event, progressCallback, asBlob = false, abortController = undefined) {
|
getAttachment(matrixClient, event, progressCallback, asBlob = false, abortController = undefined) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
|
|
@ -317,7 +317,7 @@ class Util {
|
||||||
// Find the exact match (= object equality)
|
// Find the exact match (= object equality)
|
||||||
return e.error === err
|
return e.error === err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
matrixClient.resendEvent(event, matrixClient.getRoom(event.getRoomId()))
|
matrixClient.resendEvent(event, matrixClient.getRoom(event.getRoomId()))
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
|
@ -353,7 +353,7 @@ class Util {
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size
|
size: file.size
|
||||||
};
|
};
|
||||||
|
|
||||||
// If audio, send duration in ms as well
|
// If audio, send duration in ms as well
|
||||||
if (file.duration) {
|
if (file.duration) {
|
||||||
info.duration = file.duration;
|
info.duration = file.duration;
|
||||||
|
|
@ -463,11 +463,11 @@ class Util {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return 'true' if we should use voice mode for the given room.
|
* Return 'true' if we should use voice mode for the given room.
|
||||||
*
|
*
|
||||||
* The default value is given by the room itself. If the "type" of the
|
* The default value is given by the room itself. If the "type" of the
|
||||||
* room is set to 'im.keanu.room_type_voice' then we default to voice mode,
|
* room is set to 'im.keanu.room_type_voice' then we default to voice mode,
|
||||||
* else not. The user can then override this default by flipping the "voice mode"
|
* else not. The user can then override this default by flipping the "voice mode"
|
||||||
* swicth on room settings (it will be persisted as a user specific tag on the room)
|
* swicth on room settings (it will be persisted as a user specific tag on the room)
|
||||||
*/
|
*/
|
||||||
useVoiceMode(roomOrNull) {
|
useVoiceMode(roomOrNull) {
|
||||||
if (roomOrNull) {
|
if (roomOrNull) {
|
||||||
|
|
@ -478,7 +478,7 @@ class Util {
|
||||||
if (tags && tags["ui_options"]) {
|
if (tags && tags["ui_options"]) {
|
||||||
return tags["ui_options"]["voice_mode"] === 1;
|
return tags["ui_options"]["voice_mode"] === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Was the room created with a voice mode type?
|
// Was the room created with a voice mode type?
|
||||||
const createEvent = room.currentState.getStateEvents(
|
const createEvent = room.currentState.getStateEvents(
|
||||||
"m.room.create",
|
"m.room.create",
|
||||||
|
|
@ -779,6 +779,22 @@ class Util {
|
||||||
return then.format('L');
|
return then.format('L');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatTime(time) {
|
||||||
|
const date = new Date();
|
||||||
|
date.setTime(time);
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
if (
|
||||||
|
date.getDate() == today.getDate() &&
|
||||||
|
date.getMonth() == today.getMonth() &&
|
||||||
|
date.getFullYear() == today.getFullYear()
|
||||||
|
) {
|
||||||
|
// For today, skip the date part
|
||||||
|
return date.toLocaleTimeString();
|
||||||
|
}
|
||||||
|
return date.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
formatRecordDuration(ms) {
|
formatRecordDuration(ms) {
|
||||||
return dayjs.duration(ms).format("HH:mm:ss");
|
return dayjs.duration(ms).format("HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue