320 lines
No EOL
10 KiB
Vue
320 lines
No EOL
10 KiB
Vue
<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="48" class="clickable me-2 chat-header-avatar" color="grey" @click.stop="onAvatarClicked">
|
|
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
|
<span v-else class="white--text headline">{{
|
|
room.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="room.name">
|
|
{{ room.name }}
|
|
</div>
|
|
<v-icon class="icon-dropdown" size="11">$vuetify.icons.ic_dropdown</v-icon>
|
|
<div :class="{ 'notification-alert': true, 'popup-open': showMissedItemsInfo }" v-if="notifications">
|
|
<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">{{ $tc("room.members", memberCount) }}</div>
|
|
</v-col>
|
|
<v-col 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 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 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 profileInfoMixin from "../components/profileInfoMixin";
|
|
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
|
import RoomExport from "../components/RoomExport";
|
|
|
|
import roomInfoMixin from "./roomInfoMixin";
|
|
|
|
export default {
|
|
name: "ChatHeader",
|
|
mixins: [profileInfoMixin, roomInfoMixin],
|
|
components: {
|
|
LeaveRoomDialog,
|
|
ProfileInfoPopup,
|
|
MoreMenuPopup,
|
|
PurgeRoomDialog,
|
|
RoomExport
|
|
},
|
|
data() {
|
|
return {
|
|
memberCount: null,
|
|
showLeaveConfirmation: false,
|
|
showProfileInfo: false,
|
|
showPurgeConfirmation: false,
|
|
showMoreMenu: false,
|
|
downloadingChat: false,
|
|
showMissedItemsInfo: false,
|
|
|
|
/** Timer for showing the "missed items" hint */
|
|
timerMissedItems: null
|
|
};
|
|
},
|
|
mounted() {
|
|
this.$matrix.on("Room.timeline", this.onEvent);
|
|
this.updateMemberCount();
|
|
},
|
|
|
|
destroyed() {
|
|
this.$matrix.off("Room.timeline", this.onEvent);
|
|
},
|
|
|
|
computed: {
|
|
room() {
|
|
return this.$matrix.currentRoom;
|
|
},
|
|
memberAvatar() {
|
|
let roomMember;
|
|
if (this.room) {
|
|
this.room.getMembers().forEach(member => {
|
|
if (this.room.name === member.name) {
|
|
roomMember = member;
|
|
}
|
|
});
|
|
if (roomMember) {
|
|
return roomMember.getAvatarUrl(
|
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
40,
|
|
40,
|
|
"scale",
|
|
true
|
|
);
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
notifications() {
|
|
return this.$matrix.joinedRooms.some(r => (r.roomId !== this.$matrix.currentRoomId && r.getCanonicalAlias() !== this.$matrix.currentRoomId) && r.getUnreadNotificationCount("total") > 0) ||
|
|
this.$matrix.invites.length > 0;
|
|
},
|
|
notificationsText() {
|
|
const invitationCount = this.$matrix.invites.length
|
|
if (invitationCount > 0) {
|
|
return this.$tc('room.invitations', invitationCount);
|
|
}
|
|
const missedMessagesCount = this.$matrix.joinedRooms.reduce((value, r) => ((r.roomId !== this.$matrix.currentRoomId && r.getCanonicalAlias() !== this.$matrix.currentRoomId) ? (value + r.getUnreadNotificationCount("total")) : value), 0);
|
|
if (missedMessagesCount > 0) {
|
|
return this.$tc('room.unseen_messages', missedMessagesCount);
|
|
}
|
|
return "";
|
|
},
|
|
moreMenuItems() {
|
|
let items = [];
|
|
const roomLink = this.publicRoomLink;
|
|
if (roomLink) {
|
|
items.push({
|
|
icon: '$vuetify.icons.ic_link', text: this.$t('room_info.copy_invite_link'), handler: () => {
|
|
this.$copyText(this.publicRoomLink);
|
|
}
|
|
});
|
|
}
|
|
if (this.userCanExportChat) {
|
|
items.push({
|
|
icon: '$vuetify.icons.ic_download', text: this.$t('room_info.download_chat'), handler: () => {
|
|
this.downloadingChat = true;
|
|
}
|
|
});
|
|
}
|
|
items.push({
|
|
icon: '$vuetify.icons.ic_info', text: this.$t('room_info.title'), handler: () => {
|
|
this.$emit("view-room-details", { event: this.event });
|
|
}
|
|
});
|
|
items.push({
|
|
icon: 'notifications_active', text: this.$t('global.notify'), handler: () => {
|
|
this.$emit("notify");
|
|
}
|
|
});
|
|
items.push({
|
|
icon: '$vuetify.icons.ic_member-leave', text: this.$t('leave.leave'), handler: () => {
|
|
this.leaveRoom();
|
|
}
|
|
});
|
|
return items;
|
|
},
|
|
roomAvatar() {
|
|
const room = this.room;
|
|
if (this.$matrix.isDirectRoom(room)) {
|
|
if (room.avatar) {
|
|
return room.avatar;
|
|
}
|
|
const membersNotMe = room.getMembers().filter(m => m.userId != this.$matrix.currentUserId);
|
|
if (membersNotMe && membersNotMe.length == 1) {
|
|
return membersNotMe[0].getAvatarUrl(
|
|
this.$matrix.matrixClient.getHomeserverUrl(),
|
|
40,
|
|
40,
|
|
"scale",
|
|
true
|
|
);
|
|
}
|
|
}
|
|
return room.avatar;
|
|
},
|
|
},
|
|
watch: {
|
|
room: {
|
|
handler(newVal, ignoredOldVal) {
|
|
if (newVal) {
|
|
this.memberCount = newVal.getJoinedMemberCount();
|
|
} else {
|
|
this.memberCount = null;
|
|
}
|
|
},
|
|
},
|
|
notifications: {
|
|
immediate: true,
|
|
handler(val) {
|
|
if (this.$store.state.hasShownMissedItemsHint !== "1" && val > 0 && !this.showMissedItemsInfo && this.timerMissedItems == null) {
|
|
this.timerMissedItems = setTimeout(() => {
|
|
this.showMissedItemsInfo = true;
|
|
}, 3500);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
onShowMoreMenu() {
|
|
if(this.publicRoomLink == null) {
|
|
this.roomJoinRule = this.getRoomJoinRule();
|
|
}
|
|
this.showMoreMenu = true
|
|
},
|
|
setHasShownMissedItemsHint() {
|
|
this.$store.commit('setHasShownMissedItemsHint', "1");
|
|
this.showMissedItemsInfo = false;
|
|
},
|
|
onEvent(event) {
|
|
if (!this.room || event.getRoomId() !== this.room.roomId) {
|
|
return; // Not for this room
|
|
}
|
|
if (event.getType() == "m.room.member") {
|
|
this.updateMemberCount();
|
|
}
|
|
},
|
|
|
|
onHeaderClicked() {
|
|
this.$emit("header-click", { event: this.event });
|
|
},
|
|
|
|
onAvatarClicked() {
|
|
this.$emit("view-room-details", { event: this.event });
|
|
},
|
|
|
|
updateMemberCount() {
|
|
if (!this.room) {
|
|
this.memberCount = 0;
|
|
} else {
|
|
this.memberCount = this.room.getJoinedMemberCount();
|
|
}
|
|
},
|
|
|
|
leaveRoom() {
|
|
this.showLeaveConfirmation = true;
|
|
},
|
|
},
|
|
};
|
|
</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;
|
|
}
|
|
}
|
|
</style> |