Merge branch '485-mvp-for-journalist-mode' into 'dev'
"Private chat" header for direct chats See merge request keanuapp/keanuapp-weblite!229
This commit is contained in:
commit
098a9a2fdf
9 changed files with 437 additions and 37 deletions
|
|
@ -86,9 +86,8 @@ body {
|
|||
font-family: "Inter", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 11 * $chat-text-size;
|
||||
color: var(--v-foreground-color);
|
||||
background-color: var(--v-background-color) !important;
|
||||
border: 1px solid var(--v-foreground-color);
|
||||
color: white;
|
||||
background-color: red !important;
|
||||
border-radius: $chat-standard-padding / 2;
|
||||
height: $chat-standard-padding;
|
||||
margin-top: $chat-standard-padding-xs;
|
||||
|
|
@ -99,6 +98,26 @@ body {
|
|||
color: var(--v-secondary-color);
|
||||
}
|
||||
|
||||
.leave-button {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 11.54 * $chat-text-size;
|
||||
line-height: 140%;
|
||||
color: white !important;
|
||||
background-color: #ff3300 !important;
|
||||
border-radius: $small-button-height / 2;
|
||||
min-height: 0;
|
||||
height: $small-button-height !important;
|
||||
margin-top: $chat-standard-padding-xs;
|
||||
margin-bottom: $chat-standard-padding-xs;
|
||||
padding-left: $chat-standard-padding-s !important;
|
||||
padding-right: $chat-standard-padding-s !important;
|
||||
width: auto !important;
|
||||
.v-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-dropdown {
|
||||
margin: 0px 8px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.0247 8.80006H15.4908V6.42878C15.4908 2.88379 12.5789 0 9.00064 0C5.42053 0 2.50904 2.88379 2.50904 6.42878V8.80047H1.97615C0.88518 8.80047 0 9.67678 0 10.7572V20.0423C0 21.1231 0.884952 22 1.97615 22H16.0248C17.1153 22 18 21.1232 18 20.0423L17.9999 10.7568C17.9999 9.67638 17.1151 8.80006 16.025 8.80006H16.0247ZM9.82754 15.5262V16.7406C9.82754 16.9215 9.6795 17.0687 9.4959 17.0687H8.50277C8.32063 17.0687 8.17185 16.9216 8.17185 16.7406V15.5262C7.7193 15.2498 7.41503 14.7589 7.41503 14.193C7.41503 13.3265 8.12456 12.6242 9.0001 12.6242C9.87461 12.6242 10.5841 13.3265 10.5841 14.193C10.584 14.7589 10.2795 15.2498 9.82754 15.5262ZM12.6451 8.80006H5.35551V6.42878C5.35551 4.43786 6.99007 2.81942 9.00073 2.81942C11.0097 2.81942 12.6451 4.43763 12.6451 6.42878V8.80006Z"
|
||||
fill="white" />
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -132,7 +132,10 @@
|
|||
"join_invite": "Only people you invite can join.",
|
||||
"info_permissions": "You can change ‘join permissions’ at any time in the room settings.",
|
||||
"got_it": "Got it",
|
||||
"no_past_messages": "Welcome! For your security, past messages are not available."
|
||||
"no_past_messages": "Welcome! For your security, past messages are not available.",
|
||||
"direct_hi": "Hi!",
|
||||
"direct_info": "You’ve connected to {user}. Leave a message and they’ll be notified.",
|
||||
"direct_private_chat": "Private Chat"
|
||||
},
|
||||
"new_room": {
|
||||
"new_room": "New Room",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
<template>
|
||||
<div class="chat-root fill-height d-flex flex-column">
|
||||
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0"
|
||||
<ChatHeaderPrivate class="chat-header flex-grow-0 flex-shrink-0"
|
||||
v-on:header-click="onHeaderClick"
|
||||
v-on:view-room-details="viewRoomDetails"
|
||||
v-on:notify="onNotificationDialog"
|
||||
v-if="!useFileModeNonAdmin" />
|
||||
v-if="!useFileModeNonAdmin && $matrix.isDirectRoom(room)" />
|
||||
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0"
|
||||
v-on:header-click="onHeaderClick"
|
||||
v-on:view-room-details="viewRoomDetails"
|
||||
v-on:notify="onNotificationDialog"
|
||||
v-else-if="!useFileModeNonAdmin" />
|
||||
<AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useVoiceMode" :room="room"
|
||||
:events="events" :autoplay="!showRecorder"
|
||||
:timelineSet="timelineSet"
|
||||
|
|
@ -53,6 +58,7 @@
|
|||
<resize-observer ref="chatContainerResizer" @notify="handleChatContainerResize" />
|
||||
|
||||
<CreatedRoomWelcomeHeader v-if="showCreatedRoomWelcomeHeader" v-on:close="closeCreateRoomWelcomeHeader" />
|
||||
<DirectChatWelcomeHeader v-if="showDirectChatWelcomeHeader" v-on:close="closeDirectChatWelcomeHeader" />
|
||||
|
||||
<div v-for="(event, index) in filteredEvents" :key="event.getId()" :eventId="event.getId()">
|
||||
<!-- DAY Marker, shown for every new day in the timeline -->
|
||||
|
|
@ -358,9 +364,11 @@ import util, { ROOM_TYPE_VOICE_MODE, ROOM_TYPE_FILE_MODE } from "../plugins/util
|
|||
import MessageOperations from "./messages/MessageOperations.vue";
|
||||
import AvatarOperations from "./messages/AvatarOperations.vue";
|
||||
import ChatHeader from "./ChatHeader";
|
||||
import ChatHeaderPrivate from "./ChatHeaderPrivate.vue";
|
||||
import VoiceRecorder from "./VoiceRecorder";
|
||||
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
||||
import CreatedRoomWelcomeHeader from "./CreatedRoomWelcomeHeader";
|
||||
import DirectChatWelcomeHeader from "./DirectChatWelcomeHeader";
|
||||
import NoHistoryRoomWelcomeHeader from "./NoHistoryRoomWelcomeHeader.vue";
|
||||
import MessageOperationsBottomSheet from "./MessageOperationsBottomSheet";
|
||||
import StickerPickerBottomSheet from "./StickerPickerBottomSheet";
|
||||
|
|
@ -374,6 +382,7 @@ import FileDropLayout from "./file_mode/FileDropLayout";
|
|||
import { requestNotificationAndServiceWorker, windowNotificationPermission, notificationCount } from "../plugins/notificationAndServiceWorker.js"
|
||||
import logoMixin from "./logoMixin";
|
||||
import roomTypeMixin from "./roomTypeMixin";
|
||||
import roomMembersMixin from "./roomMembersMixin";
|
||||
|
||||
const sizeOf = require("image-size");
|
||||
const dataUriToBuffer = require("data-uri-to-buffer");
|
||||
|
|
@ -409,13 +418,15 @@ ScrollPosition.prototype.prepareFor = function (direction) {
|
|||
|
||||
export default {
|
||||
name: "Chat",
|
||||
mixins: [chatMixin, logoMixin, roomTypeMixin, sendAttachmentsMixin],
|
||||
mixins: [chatMixin, logoMixin, roomTypeMixin, sendAttachmentsMixin, roomMembersMixin],
|
||||
components: {
|
||||
ChatHeader,
|
||||
ChatHeaderPrivate,
|
||||
MessageOperations,
|
||||
VoiceRecorder,
|
||||
RoomInfoBottomSheet,
|
||||
CreatedRoomWelcomeHeader,
|
||||
DirectChatWelcomeHeader,
|
||||
NoHistoryRoomWelcomeHeader,
|
||||
MessageOperationsBottomSheet,
|
||||
StickerPickerBottomSheet,
|
||||
|
|
@ -480,7 +491,10 @@ export default {
|
|||
lastRR: null,
|
||||
|
||||
/** If we just created this room, show a small welcome header with info */
|
||||
showCreatedRoomWelcomeHeader: false,
|
||||
hideCreatedRoomWelcomeHeader: false,
|
||||
|
||||
/** For direct chats, show a small welcome header with info about the other party */
|
||||
hideDirectChatWelcomeHeader: false,
|
||||
|
||||
/** An array of recent emojis. Used in the "message operations" popup. */
|
||||
recentEmojis: [],
|
||||
|
|
@ -703,6 +717,27 @@ export default {
|
|||
}
|
||||
}
|
||||
return this.events;
|
||||
},
|
||||
|
||||
roomCreatedByUsRecently() {
|
||||
const createEvent = this.room && this.room.currentState.getStateEvents("m.room.create", "");
|
||||
if (createEvent) {
|
||||
const creatorId = createEvent.getContent().creator;
|
||||
return (creatorId == this.$matrix.currentUserId && createEvent.getLocalAge() < 5 * 60000 /* 5 minutes */);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isDirectRoom() {
|
||||
return this.room.getJoinRule() == "invite" && this.joinedAndInvitedMembers.length == 2;
|
||||
},
|
||||
|
||||
showCreatedRoomWelcomeHeader() {
|
||||
return !this.hideCreatedRoomWelcomeHeader && this.roomCreatedByUsRecently && !this.isDirectRoom;
|
||||
},
|
||||
|
||||
showDirectChatWelcomeHeader() {
|
||||
return !this.hideDirectChatWelcomeHeader && this.roomCreatedByUsRecently && this.isDirectRoom;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -739,7 +774,8 @@ export default {
|
|||
this.timelineWindow = null;
|
||||
this.typingMembers = [];
|
||||
this.initialLoadDone = false;
|
||||
this.showCreatedRoomWelcomeHeader = false;
|
||||
this.hideDirectChatWelcomeHeader = false;
|
||||
this.hideCreatedRoomWelcomeHeader = false;
|
||||
|
||||
// Stop RR timer
|
||||
this.stopRRTimer();
|
||||
|
|
@ -833,16 +869,6 @@ export default {
|
|||
this.notificationDialog = false;
|
||||
},
|
||||
onRoomJoined(initialEventId) {
|
||||
// Was this room just created (by you)? Show a small info header in
|
||||
// that case!
|
||||
const createEvent = this.room.currentState.getStateEvents("m.room.create", "");
|
||||
if (createEvent) {
|
||||
const creatorId = createEvent.getContent().creator;
|
||||
if (creatorId == this.$matrix.currentUserId && createEvent.getLocalAge() < 5 * 60000 /* 5 minutes */) {
|
||||
this.showCreatedRoomWelcomeHeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Listen to events
|
||||
this.$matrix.on("Room.timeline", this.onEvent);
|
||||
this.$matrix.on("RoomMember.typing", this.onUserTyping);
|
||||
|
|
@ -887,7 +913,7 @@ export default {
|
|||
self.initialLoadDone = true;
|
||||
if (initialEventId && !this.showCreatedRoomWelcomeHeader) {
|
||||
self.scrollToEvent(initialEventId);
|
||||
} else if (this.showCreatedRoomWelcomeHeader) {
|
||||
} else if (this.showCreatedRoomWelcomeHeader || this.showDirectChatWelcomeHeader) {
|
||||
self.onScroll();
|
||||
}
|
||||
self.restartRRTimer();
|
||||
|
|
@ -1266,7 +1292,7 @@ export default {
|
|||
this.timelineWindow
|
||||
.paginate(EventTimeline.BACKWARDS, 10, true)
|
||||
.then((success) => {
|
||||
if (success) {
|
||||
if (success && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("up");
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -1294,7 +1320,7 @@ export default {
|
|||
.then((success) => {
|
||||
if (success) {
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
if (!this.useVoiceMode) {
|
||||
if (!this.useVoiceMode && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("down");
|
||||
this.$nextTick(() => {
|
||||
// restore scroll position!
|
||||
|
|
@ -1639,7 +1665,17 @@ export default {
|
|||
},
|
||||
|
||||
closeCreateRoomWelcomeHeader() {
|
||||
this.showCreatedRoomWelcomeHeader = false;
|
||||
this.hideCreatedRoomWelcomeHeader = false;
|
||||
this.$nextTick(() => {
|
||||
// We change the layout when removing the welcome header, so call
|
||||
// onScroll here to handle updates (e.g. remove the "scroll to last" if we now
|
||||
// can see all messages).
|
||||
this.onScroll();
|
||||
});
|
||||
},
|
||||
|
||||
closeDirectChatWelcomeHeader() {
|
||||
this.hideDirectChatWelcomeHeader = true;
|
||||
this.$nextTick(() => {
|
||||
// We change the layout when removing the welcome header, so call
|
||||
// onScroll here to handle updates (e.g. remove the "scroll to last" if we now
|
||||
|
|
|
|||
162
src/components/ChatHeaderPrivate.vue
Normal file
162
src/components/ChatHeaderPrivate.vue
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<v-container fluid v-if="room">
|
||||
<v-row class="chat-header-row flex-nowrap">
|
||||
<v-col
|
||||
cols="auto"
|
||||
class="chat-header-members text-start ma-0 pa-0"
|
||||
>
|
||||
<v-avatar size="40" class="clickable me-2 chat-header-avatar" color="grey" @click.stop="onAvatarClicked">
|
||||
<v-img v-if="privatePartyAvatar(40)" :src="privatePartyAvatar(40)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
privateParty.name.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
|
||||
<v-col class="chat-header-name ma-0 pa-0 flex-shrink-1 flex-grow-1 flex-nowrap" @click.stop="onHeaderClicked">
|
||||
<div class="room-title-row">
|
||||
<div class="room-name-inline text-truncate" :title="privateParty.name">
|
||||
{{ privateParty.name }}
|
||||
</div>
|
||||
<v-icon v-if="$matrix.joinedRooms.length > 1" class="icon-dropdown" size="11">$vuetify.icons.ic_dropdown</v-icon>
|
||||
<div v-if="$matrix.joinedRooms.length > 1 && notifications" :class="{ 'notification-alert': true, 'popup-open': showMissedItemsInfo }">
|
||||
<v-icon class="icon-circle" size="11">circle</v-icon>
|
||||
<!-- MISSED ITEMS POPUP -->
|
||||
<!-- <div class="missed-items-popup-background" v-if="showMissedItemsInfo" @click.stop="setHasShownMissedItemsHint()"></div> -->
|
||||
<div class="missed-items-popup" v-if="showMissedItemsInfo" @click.stop="setHasShownMissedItemsHint()">
|
||||
<div class="text">{{ notificationsText }}</div>
|
||||
<div class="button clickable" @click.stop="setHasShownMissedItemsHint()">{{$t('menu.ok')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="num-members">
|
||||
<div v-if="roomIsEncrypted" class="private-chat"><v-icon color="#616161">$vuetify.icons.ic_lock</v-icon>{{ $t("room_welcome.direct_private_chat") }}</div>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="$matrix.joinedRooms.length > 1" cols="auto" class="text-end ma-0 pa-0 ms-1">
|
||||
<v-avatar :class="{ 'avatar-32': true, 'clickable': true, 'popup-open': showProfileInfo }" size="26"
|
||||
color="#e0e0e0" @click.stop="showProfileInfo = true">
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0 ms-1">
|
||||
<!-- <v-btn id="btn-purge-room" v-if="userCanPurgeRoom" class="mx-2 box-shadow-none" fab dark small color="red"
|
||||
@click.stop="showPurgeConfirmation = true">
|
||||
<v-icon light>$vuetify.icons.ic_moderator-delete</v-icon>
|
||||
</v-btn> -->
|
||||
<v-btn v-if="$matrix.joinedRooms.length == 1" id="btn-leave-room" class="box-shadow-none leave-button" color="red" @click.stop="leaveRoom">
|
||||
<v-icon color="white">$vuetify.icons.ic_member-leave</v-icon>{{ $t('room.leave') }}
|
||||
</v-btn>
|
||||
<v-btn id="btn-leave-room" class="mx-2 box-shadow-none" fab dark small color="red" @click.stop="leaveRoom" v-else>
|
||||
<v-icon color="white">$vuetify.icons.ic_member-leave</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col v-if="$matrix.joinedRooms.length > 1" cols="auto" class="text-end ma-0 pa-0 ms-1 clickable close-button more-menu-button">
|
||||
<div :class="{ 'popup-open': showMoreMenu }">
|
||||
<v-btn class="mx-2 box-shadow-none" fab dark small color="transparent" @click.stop="onShowMoreMenu">
|
||||
<v-icon size="15">$vuetify.icons.ic_more</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- "REALLY LEAVE?" dialog -->
|
||||
<LeaveRoomDialog :show="showLeaveConfirmation" :room="room" @close="showLeaveConfirmation = false" />
|
||||
|
||||
<!-- PROFILE INFO POPUP -->
|
||||
<ProfileInfoPopup :show="showProfileInfo" @close="showProfileInfo = false" />
|
||||
|
||||
<!-- MORE MENU POPUP -->
|
||||
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" @close="showMoreMenu = false"
|
||||
v-on:leave="showLeaveConfirmation = true" />
|
||||
|
||||
<!-- PURGE ROOM POPUP -->
|
||||
<PurgeRoomDialog :show="showPurgeConfirmation" :room="room" @close="showPurgeConfirmation = false" />
|
||||
|
||||
<RoomExport :room="room" v-if="downloadingChat" v-on:close="downloadingChat = false" />
|
||||
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
||||
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
||||
import MoreMenuPopup from "../components/MoreMenuPopup";
|
||||
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
||||
import RoomExport from "../components/RoomExport";
|
||||
|
||||
import ChatHeader from "./ChatHeader.vue";
|
||||
|
||||
export default {
|
||||
name: "ChatHeaderPrivate",
|
||||
extends: ChatHeader,
|
||||
components: {
|
||||
LeaveRoomDialog,
|
||||
ProfileInfoPopup,
|
||||
MoreMenuPopup,
|
||||
PurgeRoomDialog,
|
||||
RoomExport
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
.popup-open {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.popup-open::after {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
// Need to move the "more items" arrow to the left, since it's too close to the edge
|
||||
// and would interfere with the dialog rounding...
|
||||
.more-menu-button & {
|
||||
left: calc(50% - 4px);
|
||||
}
|
||||
content: " ";
|
||||
top: 42px;
|
||||
margin-left: -10px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transform: rotate(45deg);
|
||||
border-radius: 2px;
|
||||
background-color: currentColor;
|
||||
z-index: 400;
|
||||
pointer-events: none;
|
||||
animation-duration: 0.3s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: fadein;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.private-chat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.private-chat .v-icon {
|
||||
width: 9px;
|
||||
height: 11px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
</style>
|
||||
72
src/components/DirectChatWelcomeHeader.vue
Normal file
72
src/components/DirectChatWelcomeHeader.vue
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<div style="text-align: center;">
|
||||
<div class="created-room-welcome-header">
|
||||
<v-avatar class="typing-user" size="40" color="grey" v-if="isPrivate">
|
||||
<img v-if="privatePartyAvatar(80)" :src="privatePartyAvatar(80)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
privateParty.name.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<h2>{{ $t("room_welcome.direct_hi") }}</h2>
|
||||
<div class="mt-2" v-if="privateParty">{{ $t("room_welcome.direct_info", { user: privateParty.name }) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "CreatedRoomWelcomeHeader",
|
||||
mixins: [roomInfoMixin],
|
||||
computed: {
|
||||
roomHistoryDescription() {
|
||||
const visibility = this.$matrix.getRoomHistoryVisibility(this.room);
|
||||
switch (visibility) {
|
||||
case "world_readable":
|
||||
return this.$t("room_welcome.room_history_is", {
|
||||
type: this.$t("message.room_history_world_readable"),
|
||||
});
|
||||
case "shared":
|
||||
return this.$t("room_welcome.room_history_is", {
|
||||
type: this.$t("message.room_history_shared"),
|
||||
});
|
||||
case "invited":
|
||||
return this.$t("room_welcome.room_history_is", {
|
||||
type: this.$t("message.room_history_invited"),
|
||||
});
|
||||
case "joined":
|
||||
return this.$t("room_welcome.room_history_joined");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
publicRoomLinkCopied: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyPublicLink() {
|
||||
const self = this;
|
||||
this.$copyText(this.publicRoomLink).then(
|
||||
function (ignored) {
|
||||
// Success!
|
||||
self.publicRoomLinkCopied = true;
|
||||
setInterval(() => {
|
||||
// Hide again
|
||||
self.publicRoomLinkCopied = false;
|
||||
}, 3000);
|
||||
},
|
||||
function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
|
|
@ -132,6 +132,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<v-container
|
||||
v-else
|
||||
fluid
|
||||
fill-height
|
||||
class="loading-indicator"
|
||||
>
|
||||
<v-row align="center" justify="center">
|
||||
<v-col class="text-center">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
></v-progress-circular>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -304,19 +321,10 @@ export default {
|
|||
});
|
||||
} else if (this.roomId.startsWith("@")) {
|
||||
// Direct chat with user
|
||||
this.$matrix
|
||||
.getPublicUserInfo(this.roomId)
|
||||
.then((info) => {
|
||||
console.log("Got user info:", info);
|
||||
this.roomName = info.displayname;
|
||||
this.roomAvatar = info.avatar;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Failed to get user info: ", err);
|
||||
})
|
||||
.finally(() => {
|
||||
this.waitingForInfo = false;
|
||||
});
|
||||
this.waitingForInfo = false;
|
||||
this.$nextTick(() => {
|
||||
this.handleJoin();
|
||||
});
|
||||
} else {
|
||||
// Private room, try to get name
|
||||
const room = this.$matrix.getRoom(this.roomId);
|
||||
|
|
|
|||
|
|
@ -82,6 +82,23 @@ export default {
|
|||
return isAdmin;
|
||||
},
|
||||
|
||||
isPrivate() {
|
||||
return this.$matrix.isDirectRoom(this.room);
|
||||
},
|
||||
|
||||
/**
|
||||
* If this is a direct chat with someone, return that member here.
|
||||
* @returns MXMember of the one we are chatting with, or 'undefined'.
|
||||
*/
|
||||
privateParty() {
|
||||
if (this.isPrivate) {
|
||||
const membersButMe = this.room.getMembers().filter(m => m.userId != this.$matrix.currentUserId);
|
||||
if (membersButMe.length == 1) {
|
||||
return membersButMe[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
room: {
|
||||
|
|
@ -165,5 +182,19 @@ export default {
|
|||
this.updatePermissions();
|
||||
}
|
||||
},
|
||||
|
||||
privatePartyAvatar(size) {
|
||||
const other = this.privateParty;
|
||||
if (other) {
|
||||
return other.getAvatarUrl(
|
||||
this.$matrix.matrixClient.getHomeserverUrl(),
|
||||
size,
|
||||
size,
|
||||
"scale",
|
||||
true
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
}
|
||||
69
src/components/roomMembersMixin.js
Normal file
69
src/components/roomMembersMixin.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
export default {
|
||||
data() {
|
||||
return {
|
||||
joinedAndInvitedMembers: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$matrix.on("Room.timeline", this.roomMembersMixinOnEvent);
|
||||
this.updateMembers();
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.$matrix.off("Room.timeline", this.roomMembersMixinOnEvent);
|
||||
},
|
||||
|
||||
computed: {
|
||||
joinedMembers() {
|
||||
return this.joinedAndInvitedMembers.filter(m => m.membership === "join");
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
room: {
|
||||
handler() {
|
||||
this.updateMembers();
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
roomMembersMixinOnEvent(event) {
|
||||
if (this.room && this.room.roomId == event.getRoomId()) {
|
||||
// For this room
|
||||
if (event.getType() == "m.room.member") {
|
||||
this.updateMembers();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sortMemberFunction(a, b) {
|
||||
const myUserId = this.$matrix.currentUserId;
|
||||
|
||||
// Place ourselves at the top!
|
||||
if (a.userId == myUserId) {
|
||||
return -1;
|
||||
} else if (b.userId == myUserId) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Then sort by power level
|
||||
if (a.powerLevel > b.powerLevel) {
|
||||
return -1;
|
||||
} else if (b.powerLevel > a.powerLevel) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Then by name
|
||||
const aName = a.user ? a.user.displayName : a.name;
|
||||
const bName = b.user ? b.user.displayName : b.name;
|
||||
return aName.localeCompare(bName);
|
||||
},
|
||||
|
||||
updateMembers() {
|
||||
if (this.room) {
|
||||
this.joinedAndInvitedMembers = this.room.getMembers().sort(this.sortMemberFunction);
|
||||
} else {
|
||||
this.joinedAndInvitedMembers = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue