Merge pull request #365 from MidhunSureshR/room-info

Add right panel with Room information
This commit is contained in:
Bruno Windels 2021-06-24 15:08:34 +00:00 committed by GitHub
commit 80fff87950
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 394 additions and 34 deletions

View file

@ -12,6 +12,6 @@ module.exports = {
"no-console": "off",
"no-empty": "off",
"no-prototype-builtins": "off",
"no-unused-vars": "warn",
"no-unused-vars": "warn"
}
};

View file

@ -37,7 +37,7 @@ function allowsChild(parent, child) {
// downside of the approach: both of these will control which tile is selected
return type === "room" || type === "empty-grid-tile";
case "room":
return type === "lightbox";
return type === "lightbox" || type === "details";
default:
return false;
}
@ -113,6 +113,9 @@ export function parseUrlPath(urlPath, currentNavPath, defaultSessionId) {
segments.push(roomsSegmentWithRoom(rooms, roomId, currentNavPath));
}
segments.push(new Segment("room", roomId));
if (currentNavPath.get("details")?.value) {
segments.push(new Segment("details"));
}
} else if (type === "last-session") {
let sessionSegment = currentNavPath.get("session");
if (typeof sessionSegment?.value !== "string" && defaultSessionId) {
@ -254,6 +257,25 @@ export function tests() {
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
},
"parse open-room action changing focus to an existing room with details open": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b"),
new Segment("details", true)
]);
const segments = parseUrlPath("/session/1/open-room/a", path);
assert.equal(segments.length, 4);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
assert.equal(segments[3].type, "details");
assert.equal(segments[3].value, true);
},
"parse open-room action setting a room in an empty tile": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([

View file

@ -78,13 +78,23 @@ export class RoomGridViewModel extends ViewModel {
return this._height;
}
_switchToRoom(roomId) {
const detailsShown = !!this.navigation.path.get("details")?.value;
let path = this.navigation.path.until("rooms");
path = path.with(this.navigation.segment("room", roomId));
if (detailsShown) {
path = path.with(this.navigation.segment("details", true));
}
this.navigation.applyPath(path);
}
focusTile(index) {
if (index === this._selectedIndex) {
return;
}
const vmo = this._viewModelsObservables[index];
if (vmo) {
this.navigation.push("room", vmo.id);
this._switchToRoom(vmo.id);
} else {
this.navigation.push("empty-grid-tile", index);
}
@ -146,7 +156,7 @@ export class RoomGridViewModel extends ViewModel {
this.emitChange();
viewModel?.focus();
}
/** called from SessionViewModel */
releaseRoomViewModel(roomId) {
const index = this._viewModelsObservables.findIndex(vmo => vmo && vmo.id === roomId);

View file

@ -17,6 +17,7 @@ limitations under the License.
import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js";
import {RoomViewModel} from "./room/RoomViewModel.js";
import {RoomDetailsViewModel} from "./rightpanel/RoomDetailsViewModel.js";
import {UnknownRoomViewModel} from "./room/UnknownRoomViewModel.js";
import {InviteViewModel} from "./room/InviteViewModel.js";
import {LightboxViewModel} from "./room/LightboxViewModel.js";
@ -62,6 +63,7 @@ export class SessionViewModel extends ViewModel {
if (!this._gridViewModel) {
this._updateRoom(roomId);
}
this._updateRoomDetails();
}));
if (!this._gridViewModel) {
this._updateRoom(currentRoomId.get());
@ -78,6 +80,10 @@ export class SessionViewModel extends ViewModel {
this._updateLightbox(eventId);
}));
this._updateLightbox(lightbox.get());
const details = this.navigation.observe("details");
this.track(details.subscribe(() => this._updateRoomDetails()));
this._updateRoomDetails();
}
get id() {
@ -112,6 +118,10 @@ export class SessionViewModel extends ViewModel {
return this._roomViewModelObservable?.get();
}
get roomDetailsViewModel() {
return this._roomDetailsViewModel;
}
_updateGrid(roomIds) {
const changed = !(this._gridViewModel && roomIds);
const currentRoomId = this.navigation.path.get("room");
@ -230,8 +240,7 @@ export class SessionViewModel extends ViewModel {
this._lightboxViewModel = this.disposeTracked(this._lightboxViewModel);
}
if (eventId) {
const roomId = this.navigation.path.get("room").value;
const room = this._sessionContainer.session.rooms.get(roomId);
const room = this._roomFromNavigation();
this._lightboxViewModel = this.track(new LightboxViewModel(this.childOptions({eventId, room})));
}
this.emitChange("lightboxViewModel");
@ -240,4 +249,22 @@ export class SessionViewModel extends ViewModel {
get lightboxViewModel() {
return this._lightboxViewModel;
}
_roomFromNavigation() {
const roomId = this.navigation.path.get("room")?.value;
const room = this._sessionContainer.session.rooms.get(roomId);
return room;
}
_updateRoomDetails() {
this._roomDetailsViewModel = this.disposeTracked(this._roomDetailsViewModel);
const enable = !!this.navigation.path.get("details")?.value;
if (enable) {
const room = this._roomFromNavigation();
if (!room) { return; }
this._roomDetailsViewModel = this.track(new RoomDetailsViewModel(this.childOptions({room})));
}
this.emitChange("roomDetailsViewModel");
}
}

View file

@ -92,26 +92,30 @@ export class LeftPanelViewModel extends ViewModel {
}
}
_pathForDetails(path) {
const details = this.navigation.path.get("details");
return details?.value ? path.with(details) : path;
}
toggleGrid() {
const room = this.navigation.path.get("room");
let path = this.navigation.path.until("session");
if (this.gridEnabled) {
let path = this.navigation.path.until("session");
const room = this.navigation.path.get("room");
if (room) {
path = path.with(room);
path = this._pathForDetails(path);
}
this.navigation.applyPath(path);
} else {
let path = this.navigation.path.until("session");
const room = this.navigation.path.get("room");
if (room) {
path = path.with(this.navigation.segment("rooms", [room.value]));
path = path.with(room);
path = this._pathForDetails(path);
} else {
path = path.with(this.navigation.segment("rooms", []));
path = path.with(this.navigation.segment("empty-grid-tile", 0));
}
this.navigation.applyPath(path);
}
this.navigation.applyPath(path);
}
get tileViewModels() {

View file

@ -0,0 +1,61 @@
import {ViewModel} from "../../ViewModel.js";
import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar.js";
export class RoomDetailsViewModel extends ViewModel {
constructor(options) {
super(options);
this._room = options.room;
this._onRoomChange = this._onRoomChange.bind(this);
this._room.on("change", this._onRoomChange);
}
get roomId() {
return this._room.id;
}
get canonicalAlias() {
return this._room.canonicalAlias;
}
get name() {
return this._room.name;
}
get isEncrypted() {
return !!this._room.isEncrypted;
}
get memberCount() {
return this._room.joinedMemberCount;
}
get avatarLetter() {
return avatarInitials(this.name);
}
get avatarColorNumber() {
return getIdentifierColorNumber(this.roomId)
}
avatarUrl(size) {
return getAvatarHttpUrl(this._room.avatarUrl, size, this.platform, this._room.mediaRepository);
}
get avatarTitle() {
return this.name;
}
_onRoomChange() {
this.emitChange();
}
closePanel() {
const path = this.navigation.path.until("room");
this.navigation.applyPath(path);
}
dispose() {
super.dispose();
this._room.off("change", this._onRoomChange);
}
}

View file

@ -283,10 +283,16 @@ export class RoomViewModel extends ViewModel {
console.error(err.stack);
}
}
get composerViewModel() {
return this._composerVM;
}
openDetailsPanel() {
let path = this.navigation.path.until("room");
path = path.with(this.navigation.segment("details", true));
this.navigation.applyPath(path);
}
}
class ComposerViewModel extends ViewModel {
@ -383,4 +389,4 @@ class ArchivedViewModel extends ViewModel {
get kind() {
return "archived";
}
}
}

View file

@ -362,6 +362,14 @@ export class BaseRoom extends EventEmitter {
return this.membership === "leave";
}
get canonicalAlias() {
return this._summary.data.canonicalAlias;
}
get joinedMemberCount() {
return this._summary.data.joinCount;
}
get mediaRepository() {
return this._mediaRepository;
}

View file

@ -53,6 +53,14 @@ limitations under the License.
font-size: calc(var(--avatar-size) * 0.6);
}
.hydrogen .avatar.size-52 {
--avatar-size: 52px;
width: var(--avatar-size);
height: var(--avatar-size);
line-height: var(--avatar-size);
font-size: calc(var(--avatar-size) * 0.6);
}
.hydrogen .avatar.size-30 {
--avatar-size: 30px;
width: var(--avatar-size);

View file

@ -54,6 +54,13 @@ main {
min-width: 0;
}
.right-shown{
grid-template:
"status status status" auto
"left middle right" 1fr /
300px 1fr 300px;
}
/* resize and reposition session view to account for mobile Safari which shifts
the layout viewport up without resizing it when the keyboard shows */
.hydrogen.ios .SessionView {
@ -65,7 +72,7 @@ the layout viewport up without resizing it when the keyboard shows */
.middle .close-middle { display: none; }
/* mobile layout */
@media screen and (max-width: 800px) {
.SessionView:not(.middle-shown) {
.SessionView:not(.middle-shown):not(.right-shown) {
grid-template:
"status" auto
"left" 1fr /
@ -79,8 +86,16 @@ the layout viewport up without resizing it when the keyboard shows */
1fr;
}
.SessionView:not(.middle-shown) .room-placeholder { display: none; }
.SessionView.right-shown{
grid-template:
"status" auto
"right" 1fr /
1fr;
}
.SessionView:not(.middle-shown):not(.right-shown) .room-placeholder { display: none; }
.SessionView.middle-shown .LeftPanel { display: none; }
.SessionView.right-shown .middle, .SessionView.right-shown .LeftPanel { display: none; }
/* show back button */
.middle .close-middle { display: block !important; }
@ -179,6 +194,11 @@ the layout viewport up without resizing it when the keyboard shows */
z-index: 2;
}
.menu .menu-item{
box-sizing: border-box;
width: 100%;
}
.Settings {
display: flex;
flex-direction: column;

View file

@ -18,6 +18,7 @@ limitations under the License.
@import url('layout.css');
@import url('login.css');
@import url('left-panel.css');
@import url('right-panel.css');
@import url('room.css');
@import url('timeline.css');
@import url('avatar.css');

View file

@ -0,0 +1,31 @@
.RoomDetailsView {
grid-area: right;
flex-direction: column;
}
.RoomDetailsView_avatar {
display: flex;
}
.RoomDetailsView_name h2 {
text-align: center;
}
.RoomDetailsView_row {
justify-content: space-between;
}
.RoomDetailsView_label, .RoomDetailsView_row, .RoomDetailsView, .EncryptionIconView {
display: flex;
align-items: center;
}
.EncryptionIconView {
justify-content: center;
}
.RoomDetailsView_buttons {
display: flex;
justify-content: flex-end;
width: 100%;
}

View file

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.38 12.27C15.76 11.42 16 10.43 16 9.27V3.05L8.99997 1L5.21997 2.11L15.38 12.27Z" fill="white"/>
<path d="M2.21 2.98999L2 3.04999V9.26999C2 15.63 9 17 9 17C9 17 11.71 16.47 13.76 14.53L2.21 2.98999Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.46967 0.46967C0.762563 0.176777 1.23744 0.176777 1.53033 0.46967L16.7203 15.6597C17.0132 15.9526 17.0132 16.4274 16.7203 16.7203C16.4274 17.0132 15.9526 17.0132 15.6597 16.7203L0.46967 1.53033C0.176777 1.23744 0.176777 0.762563 0.46967 0.46967Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 642 B

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 9.27V3.05L9 1L16 3.05V9.27C16 15.63 9 17 9 17C9 17 2 15.63 2 9.27Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 179 B

View file

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 19C9 19 18 15.2 18 9.50002V2.85001L9 1.52588e-05L0 2.85001L0 9.50002C0 15.2 9 19 9 19Z" fill="#C1C6CD"/>
</svg>

After

Width:  |  Height:  |  Size: 260 B

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 10C12.8284 10 13.5 9.32843 13.5 8.5C13.5 7.67157 12.8284 7 12 7C11.1716 7 10.5 7.67157 10.5 8.5C10.5 9.32843 11.1716 10 12 10ZM11 13C10.4477 13 10 12.5523 10 12C10 11.4477 10.4477 11 11 11H12C12.5523 11 13 11.4477 13 12V15.5H13.5C14.0523 15.5 14.5 15.9477 14.5 16.5C14.5 17.0523 14.0523 17.5 13.5 17.5H12C11.4477 17.5 11 17.0523 11 16.5L11 13Z" fill="#8d99a5"/>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View file

@ -0,0 +1,7 @@
<svg width="25" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1502 21.1214C16.3946 22.3074 14.2782 23 12 23C9.52367 23 7.23845 22.1817 5.4 20.8008C2.72821 18.794 1 15.5988 1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 15.797 21.0762 19.1446 18.1502 21.1214ZM12 12.55C13.8225 12.55 15.3 10.9494 15.3 8.975C15.3 7.00058 13.8225 5.4 12 5.4C10.1775 5.4 8.7 7.00058 8.7 8.975C8.7 10.9494 10.1775 12.55 12 12.55ZM12 20.8C14.3782 20.8 16.536 19.8566 18.1197 18.3237C17.1403 15.9056 14.7693 14.2 12 14.2C9.23066 14.2 6.85969 15.9056 5.88028 18.3237C7.46399 19.8566 9.62183 20.8 12 20.8Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1502 21.1214C16.3946 22.3074 14.2782 23 12 23C9.52367 23 7.23845 22.1817 5.4 20.8008C2.72821 18.794 1 15.5988 1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 15.797 21.0762 19.1446 18.1502 21.1214ZM12 12.55C13.8225 12.55 15.3 10.9494 15.3 8.975C15.3 7.00058 13.8225 5.4 12 5.4C10.1775 5.4 8.7 7.00058 8.7 8.975C8.7 10.9494 10.1775 12.55 12 12.55ZM12 20.8C14.3782 20.8 16.536 19.8566 18.1197 18.3237C17.1403 15.9056 14.7693 14.2 12 14.2C9.23066 14.2 6.85969 15.9056 5.88028 18.3237C7.46399 19.8566 9.62183 20.8 12 20.8Z" fill="#C1C6CD"/>
<path d="M18.1502 21.1214L18.9339 22.2814L18.1502 21.1214ZM5.4 20.8008L4.55919 21.9202H4.55919L5.4 20.8008ZM18.1197 18.3237L19.0934 19.3296L19.7717 18.6731L19.4173 17.7981L18.1197 18.3237ZM5.88028 18.3237L4.58268 17.7981L4.22829 18.6731L4.90659 19.3296L5.88028 18.3237ZM12 24.4C14.5662 24.4 16.9541 23.619 18.9339 22.2814L17.3665 19.9613C15.835 20.9959 13.9902 21.6 12 21.6V24.4ZM4.55919 21.9202C6.63176 23.477 9.21011 24.4 12 24.4V21.6C9.83723 21.6 7.84514 20.8865 6.24081 19.6814L4.55919 21.9202ZM-0.399998 12C-0.399998 16.0577 1.55052 19.6603 4.55919 21.9202L6.24081 19.6814C3.90591 17.9276 2.4 15.1399 2.4 12H-0.399998ZM12 -0.399998C5.15167 -0.399998 -0.399998 5.15167 -0.399998 12H2.4C2.4 6.69807 6.69807 2.4 12 2.4V-0.399998ZM24.4 12C24.4 5.15167 18.8483 -0.399998 12 -0.399998V2.4C17.3019 2.4 21.6 6.69807 21.6 12H24.4ZM18.9339 22.2814C22.2288 20.0554 24.4 16.2815 24.4 12H21.6C21.6 15.3124 19.9236 18.2337 17.3665 19.9613L18.9339 22.2814ZM13.9 8.975C13.9 10.2838 12.9459 11.15 12 11.15V13.95C14.6991 13.95 16.7 11.615 16.7 8.975H13.9ZM12 6.8C12.9459 6.8 13.9 7.66616 13.9 8.975H16.7C16.7 6.335 14.6991 4 12 4V6.8ZM10.1 8.975C10.1 7.66616 11.0541 6.8 12 6.8V4C9.30086 4 7.3 6.335 7.3 8.975H10.1ZM12 11.15C11.0541 11.15 10.1 10.2838 10.1 8.975H7.3C7.3 11.615 9.30086 13.95 12 13.95V11.15ZM17.146 17.3178C15.8129 18.6081 14.0004 19.4 12 19.4V22.2C14.756 22.2 17.2591 21.1051 19.0934 19.3296L17.146 17.3178ZM12 15.6C14.1797 15.6 16.0494 16.9415 16.8221 18.8493L19.4173 17.7981C18.2312 14.8697 15.359 12.8 12 12.8V15.6ZM7.17788 18.8493C7.95058 16.9415 9.8203 15.6 12 15.6V12.8C8.64102 12.8 5.7688 14.8697 4.58268 17.7981L7.17788 18.8493ZM12 19.4C9.99963 19.4 8.18709 18.6081 6.85397 17.3178L4.90659 19.3296C6.74088 21.1051 9.24402 22.2 12 22.2V19.4Z" fill="#C1C6CD" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -22,7 +22,6 @@ limitations under the License.
font-size: 10px;
}
.hydrogen {
font-family: 'Inter', sans-serif, 'emoji';
background-color: white;
@ -332,7 +331,6 @@ a {
align-items: center;
}
.SessionStatusView button.link {
color: currentcolor;
text-align: left;
@ -456,6 +454,10 @@ a {
background-image: url("./icons/vertical-ellipsis.svg");
}
.RoomHeader .room-info {
background-image: url("./icons/info.svg");
}
.RoomView_error {
color: red;
}
@ -660,6 +662,25 @@ button.link {
margin: 0;
}
.menu li{
margin-bottom: 10px;
}
.menu button {
border-radius: 4px;
border: none;
background-color: transparent;
text-align: left;
padding: 8px 32px 8px 8px;
font-size: 1.5rem;
height: 24px;
cursor: pointer;
}
.menu .destructive button {
color: #FF4B55;
}
.menu .quick-reactions {
display: flex;
padding: 8px 32px 8px 8px;
@ -670,20 +691,6 @@ button.link {
text-align: center;
}
.menu button {
border-radius: 4px;
display: block;
border: none;
width: 100%;
background-color: transparent;
text-align: left;
padding: 8px 32px 8px 8px;
}
.menu .destructive button {
color: #FF4B55;
}
.InviteView_body {
display: flex;
justify-content: space-around;
@ -779,3 +786,82 @@ button.link {
max-width: 200px;
width: 100%;
}
/* Right Panel */
.RoomDetailsView {
background: rgba(245, 245, 245, 0.90);
padding: 16px;
}
.RoomDetailsView_id {
color: #737D8C;
font-size: 12px;
}
.RoomDetailsView_rows{
margin-top: 36px;
width: 100%;
}
.RoomDetailsView_name h2 {
margin-bottom: 4px;
font-size: 1.8rem;
}
.RoomDetailsView_row {
margin-bottom: 20px;
font-weight: 500;
font-size: 15px;
}
.RoomDetailsView_label::before {
padding-right: 16px;
height: 24px;
width: 20px;
}
.RoomDetailsView_value {
color: #737D8C;
}
.MemberCount::before {
content: url("./icons/room-members.svg");
}
.EncryptionStatus::before {
content: url("./icons/encryption-status.svg");
}
/* Encryption icon next to avatar */
.EncryptionIconView {
width: 52px;
height: 52px;
border-radius: 100%;
background: #737D8C;
border: 3px solid #F2F5F8;
margin-left: -16px;
}
.EncryptionIconView_encrypted, .EncryptionIconView_unencrypted {
height: 24px;
width: 24px;
}
.EncryptionIconView_encrypted {
content: url("./icons/e2ee-normal.svg");
}
.EncryptionIconView_unencrypted {
content: url("./icons/e2ee-disabled.svg");
}
.RoomDetailsView .button-utility {
width: 24px;
height: 24px;
}
.RoomDetailsView .close {
background-image: url("./icons/clear.svg");
}

View file

@ -59,6 +59,6 @@ class MenuOption {
}
return t.li({
className,
}, t.button({onClick: this.callback}, this.label));
}, t.button({className:"menu-item", onClick: this.callback}, this.label));
}
}

View file

@ -25,13 +25,15 @@ import {StaticView} from "../general/StaticView.js";
import {SessionStatusView} from "./SessionStatusView.js";
import {RoomGridView} from "./RoomGridView.js";
import {SettingsView} from "./settings/SettingsView.js";
import {RoomDetailsView} from "./rightpanel/RoomDetailsView.js";
export class SessionView extends TemplateView {
render(t, vm) {
return t.div({
className: {
"SessionView": true,
"middle-shown": vm => !!vm.activeMiddleViewModel
"middle-shown": vm => !!vm.activeMiddleViewModel,
"right-shown": vm => !!vm.roomDetailsViewModel
},
}, [
t.view(new SessionStatusView(vm.sessionStatusViewModel)),
@ -53,6 +55,7 @@ export class SessionView extends TemplateView {
return new StaticView(t => t.div({className: "room-placeholder"}, t.h2(vm.i18n`Choose a room on the left side.`)));
}
}),
t.mapView(vm => vm.roomDetailsViewModel, roomDetailsViewModel => roomDetailsViewModel ? new RoomDetailsView(roomDetailsViewModel) : null),
t.mapView(vm => vm.lightboxViewModel, lightboxViewModel => lightboxViewModel ? new LightboxView(lightboxViewModel) : null)
]);
}

View file

@ -0,0 +1,51 @@
import {TemplateView} from "../../general/TemplateView.js";
import {classNames, tag} from "../../general/html.js";
import {AvatarView} from "../../avatar.js";
export class RoomDetailsView extends TemplateView {
render(t, vm) {
const encryptionString = () => vm.isEncrypted ? vm.i18n`On` : vm.i18n`Off`;
return t.div({className: "RoomDetailsView"}, [
this._createButton(t, vm),
t.div({className: "RoomDetailsView_avatar"},
[
t.view(new AvatarView(vm, 52)),
t.mapView(vm => vm.isEncrypted, isEncrypted => new EncryptionIconView(isEncrypted))
]),
t.div({className: "RoomDetailsView_name"}, [t.h2(vm => vm.name)]),
this._createRoomAliasDisplay(vm),
t.div({className: "RoomDetailsView_rows"},
[
this._createRightPanelRow(t, vm.i18n`People`, {MemberCount: true}, vm => vm.memberCount),
this._createRightPanelRow(t, vm.i18n`Encryption`, {EncryptionStatus: true}, encryptionString)
])
]);
}
_createRoomAliasDisplay(vm) {
return vm.canonicalAlias ? tag.div({className: "RoomDetailsView_id"}, [vm.canonicalAlias]) :
"";
}
_createRightPanelRow(t, label, labelClass, value) {
const labelClassString = classNames({RoomDetailsView_label: true, ...labelClass});
return t.div({className: "RoomDetailsView_row"}, [
t.div({className: labelClassString}, [label]),
t.div({className: "RoomDetailsView_value"}, value)
]);
}
_createButton(t, vm) {
return t.div({className: "RoomDetailsView_buttons"},
[
t.button({className: "close button-utility", onClick: () => vm.closePanel()})
]);
}
}
class EncryptionIconView extends TemplateView {
render(t, isEncrypted) {
return t.div({className: "EncryptionIconView"},
[t.div({className: isEncrypted ? "EncryptionIconView_encrypted" : "EncryptionIconView_unencrypted"})]);
}
}

View file

@ -68,6 +68,7 @@ export class RoomView extends TemplateView {
} else {
const vm = this.value;
const options = [];
options.push(Menu.option(vm.i18n`Room details`, () => vm.openDetailsPanel()))
if (vm.canLeave) {
options.push(Menu.option(vm.i18n`Leave room`, () => vm.leaveRoom()).setDestructive());
}