Room info bottom sheet

Issue #89.
This commit is contained in:
N-Pex 2021-03-11 13:55:10 +01:00
parent 2b3f99c421
commit 6d97fd030d
9 changed files with 240 additions and 42 deletions

70
package-lock.json generated
View file

@ -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",

View file

@ -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"

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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>

View 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>

View file

@ -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,
}),

View 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 "";
},
},
}