parent
2b3f99c421
commit
6d97fd030d
9 changed files with 240 additions and 42 deletions
70
package-lock.json
generated
70
package-lock.json
generated
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"axios": "^0.21.0",
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
"vue-resize": "^0.5.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-sanitize": "^0.2.1",
|
||||
"vue-swipeable-bottom-sheet": "^0.0.5",
|
||||
"vuetify": "^2.2.11",
|
||||
"vuex": "^3.5.1",
|
||||
"vuex-persist": "^3.1.3"
|
||||
|
|
@ -5286,17 +5287,17 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
"hmac-drbg": "^1.0.1",
|
||||
"inherits": "^2.0.4",
|
||||
"minimalistic-assert": "^1.0.1",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
|
@ -6642,6 +6643,14 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/handle-thing": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||
|
|
@ -13938,6 +13947,14 @@
|
|||
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vue-swipeable-bottom-sheet": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-swipeable-bottom-sheet/-/vue-swipeable-bottom-sheet-0.0.5.tgz",
|
||||
"integrity": "sha512-PcARUGu6tZ22WRwNum6mTlnMQk/DwqdcD3cQavshIICntD9OzPelkY4mlfJezx3BspfiTO0UI33pQ3x9tmzfcg==",
|
||||
"dependencies": {
|
||||
"hammerjs": "^2.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-template-compiler": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz",
|
||||
|
|
@ -19613,17 +19630,17 @@
|
|||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
"hmac-drbg": "^1.0.1",
|
||||
"inherits": "^2.0.4",
|
||||
"minimalistic-assert": "^1.0.1",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
|
|
@ -20677,6 +20694,11 @@
|
|||
"pify": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||
},
|
||||
"handle-thing": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||
|
|
@ -26528,6 +26550,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"vue-swipeable-bottom-sheet": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-swipeable-bottom-sheet/-/vue-swipeable-bottom-sheet-0.0.5.tgz",
|
||||
"integrity": "sha512-PcARUGu6tZ22WRwNum6mTlnMQk/DwqdcD3cQavshIICntD9OzPelkY4mlfJezx3BspfiTO0UI33pQ3x9tmzfcg==",
|
||||
"requires": {
|
||||
"hammerjs": "^2.0.8"
|
||||
}
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
"vue-resize": "^0.5.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-sanitize": "^0.2.1",
|
||||
"vue-swipeable-bottom-sheet": "^0.0.5",
|
||||
"vuetify": "^2.2.11",
|
||||
"vuex": "^3.5.1",
|
||||
"vuex-persist": "^3.1.3"
|
||||
|
|
|
|||
|
|
@ -677,4 +677,31 @@ $admin-fg: white;
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-sheet .card {
|
||||
z-index: 10; /* Above mic button etc. */
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.room-info-sheet {
|
||||
background-color: white;
|
||||
|
||||
.current-room {
|
||||
padding: 25px;
|
||||
background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%), #F5F5F7;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.room-avatar {
|
||||
background-color: #ededed;
|
||||
width: 44px !important;
|
||||
height: 44px !important;
|
||||
margin-bottom: 20px;
|
||||
.headline {
|
||||
font-size: 70 * $chat-text-size !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,14 @@ $chat-button-height: 50px;
|
|||
color: #505050;
|
||||
}
|
||||
|
||||
.h4 {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 11 * $chat-text-size;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.v-btn.outlined-button {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-weight: 700;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<template>
|
||||
<div class="chat-root fill-height d-flex flex-column" ma-0 pa-0>
|
||||
<ChatHeader class="chat-header flex-grow-1 flex-shrink-1" />
|
||||
<ChatHeader
|
||||
class="chat-header flex-grow-1 flex-shrink-1"
|
||||
v-on:header-click="$refs.roomInfoSheet.open()"
|
||||
/>
|
||||
<div
|
||||
class="chat-content flex-grow-1 flex-shrink-1"
|
||||
ref="chatContainer"
|
||||
|
|
@ -160,7 +163,7 @@
|
|||
elevation="0"
|
||||
v-blur
|
||||
style="z-index: 10"
|
||||
v-longTap:250="[showRecordingUI,startRecording]"
|
||||
v-longTap:250="[showRecordingUI, startRecording]"
|
||||
>
|
||||
<v-icon :color="showRecorder ? 'white' : 'black'">mic</v-icon>
|
||||
</v-btn>
|
||||
|
|
@ -288,6 +291,9 @@
|
|||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<RoomInfoBottomSheet ref="roomInfoSheet" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -312,6 +318,7 @@ import util from "../plugins/utils";
|
|||
import MessageOperations from "./messages/MessageOperations.vue";
|
||||
import ChatHeader from "./ChatHeader";
|
||||
import VoiceRecorder from "./VoiceRecorder";
|
||||
import RoomInfoBottomSheet from "./RoomInfoBottomSheet";
|
||||
|
||||
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
|
||||
|
||||
|
|
@ -364,6 +371,7 @@ export default {
|
|||
DebugEvent,
|
||||
MessageOperations,
|
||||
VoiceRecorder,
|
||||
RoomInfoBottomSheet
|
||||
},
|
||||
|
||||
data() {
|
||||
|
|
@ -698,7 +706,10 @@ export default {
|
|||
switch (event.getType()) {
|
||||
case "m.room.member":
|
||||
if (event.getContent().membership == "join") {
|
||||
if (event.getPrevContent() && event.getPrevContent().membership == "join") {
|
||||
if (
|
||||
event.getPrevContent() &&
|
||||
event.getPrevContent().membership == "join"
|
||||
) {
|
||||
// We we already joined, so this must be a display name and/or avatar update!
|
||||
return ContactChanged;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,20 +4,17 @@
|
|||
<v-col
|
||||
cols="auto"
|
||||
class="chat-header-members text-start ma-0 pa-0"
|
||||
style="overflow:hidden;cursor:pointer" @click.stop="onHeaderClicked"
|
||||
>
|
||||
<v-avatar size="40" class="mr-2">
|
||||
<v-img :src="room.avatar" />
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
|
||||
<v-col class="ma-0 pa-0 flex-shrink-1 flex-nowrap" style="overflow:hidden">
|
||||
<div class="d-flex flex-nowrap room-name" @click.stop="showRoomList = true">{{ room.summary.info.title }} <v-icon>expand_more</v-icon></div>
|
||||
<RoomList v-if="showRoomList" v-click-outside="hideRoomList" @close="hideRoomList" />
|
||||
<v-col class="ma-0 pa-0 flex-shrink-1 flex-nowrap" style="overflow:hidden;cursor:pointer" @click.stop="onHeaderClicked">
|
||||
<div class="d-flex flex-nowrap room-name">{{ room.summary.info.title }} <!--<v-icon>expand_more</v-icon>--></div>
|
||||
<div class="num-members">{{ memberCount }}{{ memberCount > 1 ? " members" : " member" }}</div>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0">
|
||||
<v-btn text class="info-button" @click.stop="showRoomInfo"><v-icon color="black">info</v-icon></v-btn>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0">
|
||||
<v-btn text class="leave-button" @click.stop="leaveRoom">Leave</v-btn>
|
||||
</v-col>
|
||||
|
|
@ -31,18 +28,15 @@
|
|||
|
||||
<script>
|
||||
import LeaveRoomDialog from '../components/LeaveRoomDialog';
|
||||
import RoomList from "../components/RoomList";
|
||||
|
||||
export default {
|
||||
name: "ChatHeader",
|
||||
components: {
|
||||
LeaveRoomDialog,
|
||||
RoomList
|
||||
LeaveRoomDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
memberCount: null,
|
||||
showRoomList: false,
|
||||
showLeaveConfirmation: false
|
||||
};
|
||||
},
|
||||
|
|
@ -82,6 +76,10 @@ export default {
|
|||
this.updateMemberCount();
|
||||
}
|
||||
},
|
||||
|
||||
onHeaderClicked() {
|
||||
this.$emit("header-click", {event: this.event});
|
||||
},
|
||||
|
||||
updateMemberCount() {
|
||||
if (!this.room) {
|
||||
|
|
@ -91,17 +89,9 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
showRoomInfo() {
|
||||
this.$navigation.push({ name: "RoomInfo" });
|
||||
},
|
||||
|
||||
leaveRoom() {
|
||||
this.showLeaveConfirmation = true;
|
||||
},
|
||||
|
||||
hideRoomList() {
|
||||
this.showRoomList = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
96
src/components/RoomInfoBottomSheet.vue
Normal file
96
src/components/RoomInfoBottomSheet.vue
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<SwipeableBottomSheet
|
||||
class="bottom-sheet"
|
||||
ref="roomInfoSheet"
|
||||
:halfY="0.5"
|
||||
:openY="0.1"
|
||||
:data-closed="closed ? 1 : 0"
|
||||
>
|
||||
<div class="room-info-sheet" ref="roomInfoSheetContent">
|
||||
<div class="text-center current-room">
|
||||
<v-avatar class="room-avatar">
|
||||
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
||||
<span v-else class="white--text headline">{{
|
||||
roomName.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<div class="h4">This group</div>
|
||||
<div class="h2">{{ roomName }}</div>
|
||||
<v-btn
|
||||
height="20px"
|
||||
color="black"
|
||||
class="filled-button"
|
||||
@click.stop="showDetails"
|
||||
>View details</v-btn
|
||||
>
|
||||
</div>
|
||||
<room-list :title="'Other groups'" v-on:close="close" />
|
||||
</div>
|
||||
</SwipeableBottomSheet>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SwipeableBottomSheet from "vue-swipeable-bottom-sheet/src/components/SwipeableBottomSheet";
|
||||
import RoomList from "./RoomList.vue";
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "RoomInfoBottomSheet",
|
||||
mixins: [roomInfoMixin],
|
||||
components: {
|
||||
SwipeableBottomSheet,
|
||||
RoomList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
closed: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$watch(
|
||||
"$refs.roomInfoSheet.state",
|
||||
(new_value, ignored_old_value) => {
|
||||
this.closed = new_value == 'close';
|
||||
}
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
if (this.$refs.roomInfoSheet.state == "half") {
|
||||
this.$refs.roomInfoSheet.setState("close");
|
||||
} else {
|
||||
// Reset scroll before opening!
|
||||
this.$refs.roomInfoSheetContent.parentElement.scrollTop = 0;
|
||||
this.$refs.roomInfoSheet.setState("half");
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$refs.roomInfoSheet.setState("close");
|
||||
},
|
||||
|
||||
showDetails() {
|
||||
this.close();
|
||||
this.$navigation.push({ name: "RoomInfo" });
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
/* Default implementation only dims background when fully open,
|
||||
so we use our own flag (data-closed) here to that we can
|
||||
dim also when it is just half open */
|
||||
.bottom-sheet[data-closed="0"] .bg {
|
||||
display: block;
|
||||
transition: all 0.3s;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-list dense class="room-list">
|
||||
<v-subheader>ROOMS</v-subheader>
|
||||
<div class="h4">{{title}}</div>
|
||||
<v-list-item-group v-model="currentRoomId" color="primary">
|
||||
<v-list-item v-for="room in $matrix.rooms" :key="room.roomId" :value="room.roomId">
|
||||
<v-list-item-avatar size="40" color="#e0e0e0">
|
||||
|
|
@ -22,6 +22,13 @@ import util from "../plugins/utils";
|
|||
export default {
|
||||
name: "RoomList",
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: "Rooms"
|
||||
}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentRoomId: null,
|
||||
}),
|
||||
|
|
|
|||
28
src/components/roomInfoMixin.js
Normal file
28
src/components/roomInfoMixin.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
export default {
|
||||
computed: {
|
||||
room() {
|
||||
return this.$matrix.currentRoom;
|
||||
},
|
||||
|
||||
roomName() {
|
||||
if (this.room) {
|
||||
return this.room.name;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
roomTopic() {
|
||||
if (this.room) {
|
||||
return this.room.topic;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
roomAvatar() {
|
||||
if (this.room) {
|
||||
return this.room.avatar;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue