parent
f3b37f7479
commit
91dfb0bc8e
6 changed files with 196 additions and 7 deletions
|
|
@ -420,7 +420,7 @@ $admin-fg: white;
|
|||
}
|
||||
}
|
||||
|
||||
.message-operations-strut {
|
||||
.message-operations-strut, .avatar-operations-strut {
|
||||
position: relative;
|
||||
height: 0px;
|
||||
z-index: 1;
|
||||
|
|
@ -442,6 +442,16 @@ $admin-fg: white;
|
|||
// }
|
||||
}
|
||||
|
||||
.avatar-operations {
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
background-color: white;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
padding: 0px 20px;
|
||||
box-shadow: 4px 4px 8px #888888;
|
||||
}
|
||||
|
||||
.message-operations-picker {
|
||||
background-color: white;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@
|
|||
ref="chatContainer"
|
||||
style="overflow-x: hidden; overflow-y: auto"
|
||||
v-on:scroll="onScroll"
|
||||
@click="closeContextMenuIfOpen"
|
||||
@click="closeContextMenusIfOpen"
|
||||
>
|
||||
<div ref="messageOperationsStrut" class="message-operations-strut">
|
||||
<message-operations
|
||||
ref="messageOperations"
|
||||
:style="opStyle"
|
||||
:emojis="recentEmojis"
|
||||
v-on:close="showContextMenu = false"
|
||||
v-on:close="showContextMenu = false;showContextMenuAnchor = null;"
|
||||
v-if="selectedEvent && showContextMenu"
|
||||
v-on:addreaction="addReaction"
|
||||
v-on:addquickreaction="addQuickReaction"
|
||||
|
|
@ -29,6 +29,18 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div ref="avatarOperationsStrut" class="avatar-operations-strut">
|
||||
<avatar-operations
|
||||
ref="avatarOperations"
|
||||
:style="avatarOpStyle"
|
||||
v-on:close="showAvatarMenu = false;showAvatarMenuAnchor = null;"
|
||||
v-on:start-private-chat="startPrivateChat($event)"
|
||||
v-if="selectedEvent && showAvatarMenu"
|
||||
:room="room"
|
||||
:event="selectedEvent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Handle resizes, e.g. when soft keyboard is shown/hidden -->
|
||||
<resize-observer
|
||||
ref="chatContainerResizer"
|
||||
|
|
@ -82,6 +94,7 @@
|
|||
v-on:send-quick-reaction="sendQuickReaction"
|
||||
v-on:context-menu="showContextMenuForEvent($event)"
|
||||
v-on:own-avatar-clicked="viewProfile"
|
||||
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)"
|
||||
v-on:download="download(event)"
|
||||
/>
|
||||
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
|
||||
|
|
@ -355,6 +368,7 @@ import DebugEvent from "./messages/DebugEvent.vue";
|
|||
import util from "../plugins/utils";
|
||||
import MessageOperations from "./messages/MessageOperations.vue";
|
||||
import MessageOperationsPicker from "./messages/MessageOperationsPicker.vue";
|
||||
import AvatarOperations from "./messages/AvatarOperations.vue";
|
||||
import ChatHeader from "./ChatHeader";
|
||||
import VoiceRecorder from "./VoiceRecorder";
|
||||
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
||||
|
|
@ -430,6 +444,7 @@ export default {
|
|||
MessageOperationsBottomSheet,
|
||||
StickerPickerBottomSheet,
|
||||
BottomSheet,
|
||||
AvatarOperations,
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
@ -456,6 +471,8 @@ export default {
|
|||
replyToEvent: null,
|
||||
showContextMenu: false,
|
||||
showContextMenuAnchor: null,
|
||||
showAvatarMenu: false,
|
||||
showAvatarMenuAnchor: null,
|
||||
initialLoadDone: false,
|
||||
loading: false, // Set this to true during long operations to show a "spinner" overlay
|
||||
showRecorder: false,
|
||||
|
|
@ -586,6 +603,25 @@ export default {
|
|||
}
|
||||
return "top:" + top + "px;left:" + left + "px";
|
||||
},
|
||||
avatarOpStyle() {
|
||||
// Calculate where to show the context menu.
|
||||
//
|
||||
const ref = this.selectedEvent && this.$refs[this.selectedEvent.getId()];
|
||||
var top = 0;
|
||||
var left = 0;
|
||||
if (ref && ref[0]) {
|
||||
if (this.showAvatarMenuAnchor) {
|
||||
var rectAnchor = this.showAvatarMenuAnchor.getBoundingClientRect();
|
||||
var rectChat = this.$refs.avatarOperationsStrut.getBoundingClientRect();
|
||||
top = rectAnchor.top - rectChat.top;
|
||||
left = rectAnchor.left - rectChat.left;
|
||||
// if (left + 250 > rectChat.right) {
|
||||
// left = rectChat.right - 250; // Pretty ugly, but we want to make sure it does not escape the screen, and we don't have the exakt width of it (yet)!
|
||||
// }
|
||||
}
|
||||
}
|
||||
return "top:" + top + "px;left:" + left + "px";
|
||||
},
|
||||
canRecordAudio() {
|
||||
return util.browserCanRecordAudio();
|
||||
},
|
||||
|
|
@ -1258,13 +1294,44 @@ export default {
|
|||
this.showContextMenuAnchor = e.anchor;
|
||||
},
|
||||
|
||||
showAvatarMenuForEvent(e) {
|
||||
const event = e.event;
|
||||
this.selectedEvent = event;
|
||||
this.showAvatarMenu = true;
|
||||
this.showAvatarMenuAnchor = e.anchor;
|
||||
},
|
||||
|
||||
viewProfile() {
|
||||
this.$navigation.push({ name: "Profile" }, 1);
|
||||
},
|
||||
|
||||
closeContextMenuIfOpen(e) {
|
||||
startPrivateChat(e) {
|
||||
this.$matrix.getOrCreatePrivateChat(e.event.getSender())
|
||||
.then(room => {
|
||||
this.$nextTick(() => {
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(room.getCanonicalAlias() || room.roomId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
})
|
||||
},
|
||||
|
||||
closeContextMenusIfOpen(e) {
|
||||
if (this.showContextMenu) {
|
||||
this.showContextMenu = false;
|
||||
this.showContextMenuAnchor = null;
|
||||
e.preventDefault();
|
||||
}
|
||||
if (this.showAvatarMenu) {
|
||||
this.showAvatarMenu = false;
|
||||
this.showAvatarMenuAnchor = null;
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
37
src/components/messages/AvatarOperations.vue
Normal file
37
src/components/messages/AvatarOperations.vue
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<div
|
||||
:class="{
|
||||
'avatar-operations': true,
|
||||
incoming: incoming,
|
||||
outgoing: !incoming,
|
||||
}"
|
||||
>
|
||||
<v-btn v-if="incoming" text @click.stop="startPrivateChat" class="ma-0 pa-0"
|
||||
>Private chat with this user</v-btn
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import messageMixin from "./messageMixin";
|
||||
|
||||
export default {
|
||||
mixins: [messageMixin],
|
||||
mounted() {
|
||||
// Any items to show?
|
||||
if (this.room && this.event && this.$matrix.isDirectRoomWith(this.room, this.event.getSender())) {
|
||||
this.$emit("close");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startPrivateChat() {
|
||||
this.$emit("close");
|
||||
this.$emit("start-private-chat", { event: this.event });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<!-- BASE CLASS FOR INCOMING MESSAGE -->
|
||||
<div :class="messageClasses">
|
||||
<v-avatar class="avatar" size="32" color="#ededed">
|
||||
<v-avatar class="avatar" ref="avatar" size="32" color="#ededed" @click.stop="otherAvatarClicked($refs.avatar.$el)">
|
||||
<img v-if="messageEventAvatar(event)" :src="messageEventAvatar(event)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
messageEventDisplayName(event).substring(0, 1).toUpperCase()
|
||||
|
|
|
|||
|
|
@ -156,6 +156,10 @@ export default {
|
|||
this.$emit("own-avatar-clicked", {event: this.event});
|
||||
},
|
||||
|
||||
otherAvatarClicked(avatarRef) {
|
||||
this.$emit("other-avatar-clicked", {event: this.event, anchor: avatarRef});
|
||||
},
|
||||
|
||||
showContextMenu(buttonRef) {
|
||||
this.$emit("context-menu", {event: this.event,anchor: buttonRef});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export default {
|
|||
login(user) {
|
||||
const tempMatrixClient = sdk.createClient(User.homeServerUrl(user.home_server));
|
||||
var promiseLogin;
|
||||
|
||||
|
||||
const self = this;
|
||||
if (user.access_token) {
|
||||
// Logged in on "real" account
|
||||
|
|
@ -520,7 +520,7 @@ export default {
|
|||
var joined = room.getMembersWithMembership("join");
|
||||
var invited = room.getMembersWithMembership("invite");
|
||||
var members = joined.concat(invited);
|
||||
|
||||
|
||||
var kickPromises = [];
|
||||
members.forEach(member => {
|
||||
if (member.userId != self.currentUserId) {
|
||||
|
|
@ -539,6 +539,77 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a private chat room with the given user. Searches through our rooms to see
|
||||
* if a suitable room already exists. If not, one is created.
|
||||
* @param {*} userId The user to chat with.
|
||||
*/
|
||||
getOrCreatePrivateChat(userId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const room of this.rooms) {
|
||||
// Is the other member the one we are looking for?
|
||||
if (this.isDirectRoomWith(room, userId)) {
|
||||
var member = room.getMember(userId);
|
||||
if (member && member.membership == "invite") {
|
||||
// TODO Resend invite
|
||||
}
|
||||
resolve(room);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No room found, create one
|
||||
//
|
||||
const createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
//name: this.roomName,
|
||||
preset: "private_chat",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.guest_access",
|
||||
state_key: "",
|
||||
content: {
|
||||
guest_access: "forbidden",
|
||||
},
|
||||
},
|
||||
],
|
||||
invite: [userId]
|
||||
};
|
||||
return this.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
.then(({ room_id, room_alias }) => {
|
||||
resolve(this.getRoom(room_alias || room_id));
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if this room is a direct room with the given user.
|
||||
* @param { } room
|
||||
* @param {*} userId
|
||||
*/
|
||||
isDirectRoomWith(room, userId) {
|
||||
if (room._selfMembership == "join" && room.getInvitedAndJoinedMemberCount() == 2) {
|
||||
// Is the other member the one we are looking for?
|
||||
if (room.getMembersWithMembership("join").some(item => item.userId == userId)) {
|
||||
return true;
|
||||
} else if (room.getMembersWithMembership("invite").some(item => item.userId == userId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
on(event, handler) {
|
||||
if (this.matrixClient) {
|
||||
this.matrixClient.on(event, handler);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue