forked from mystiq/hydrogen-web
Merge pull request #138 from vector-im/bwindels/filter-room-list
Room list filtering
This commit is contained in:
commit
a7db44eabf
12 changed files with 419 additions and 106 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {RoomTileViewModel} from "./roomlist/RoomTileViewModel.js";
|
||||
import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js";
|
||||
import {RoomViewModel} from "./room/RoomViewModel.js";
|
||||
import {SessionStatusViewModel} from "./SessionStatusViewModel.js";
|
||||
import {ViewModel} from "../ViewModel.js";
|
||||
|
@ -29,22 +30,22 @@ export class SessionViewModel extends ViewModel {
|
|||
reconnector: sessionContainer.reconnector,
|
||||
session: sessionContainer.session,
|
||||
})));
|
||||
this._leftPanelViewModel = new LeftPanelViewModel(this.childOptions({
|
||||
rooms: this._session.rooms,
|
||||
openRoom: this._openRoom.bind(this)
|
||||
}));
|
||||
this._currentRoomTileViewModel = null;
|
||||
this._currentRoomViewModel = null;
|
||||
const roomTileVMs = this._session.rooms.mapValues((room, emitChange) => {
|
||||
return new RoomTileViewModel({
|
||||
room,
|
||||
emitChange,
|
||||
emitOpen: this._openRoom.bind(this)
|
||||
});
|
||||
});
|
||||
this._roomList = roomTileVMs.sortValues((a, b) => a.compare(b));
|
||||
}
|
||||
|
||||
start() {
|
||||
this._sessionStatusViewModel.start();
|
||||
}
|
||||
|
||||
get leftPanelViewModel() {
|
||||
return this._leftPanelViewModel;
|
||||
}
|
||||
|
||||
get sessionStatusViewModel() {
|
||||
return this._sessionStatusViewModel;
|
||||
}
|
||||
|
|
58
src/domain/session/leftpanel/LeftPanelViewModel.js
Normal file
58
src/domain/session/leftpanel/LeftPanelViewModel.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ViewModel} from "../../ViewModel.js";
|
||||
import {RoomTileViewModel} from "./RoomTileViewModel.js";
|
||||
import {RoomFilter} from "./RoomFilter.js";
|
||||
import {ApplyMap} from "../../../observable/map/ApplyMap.js";
|
||||
|
||||
export class LeftPanelViewModel extends ViewModel {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
const {rooms, openRoom} = options;
|
||||
const roomTileVMs = rooms.mapValues((room, emitChange) => {
|
||||
return new RoomTileViewModel({
|
||||
room,
|
||||
emitChange,
|
||||
emitOpen: openRoom
|
||||
});
|
||||
});
|
||||
this._roomListFilterMap = new ApplyMap(roomTileVMs);
|
||||
this._roomList = this._roomListFilterMap.sortValues((a, b) => a.compare(b));
|
||||
}
|
||||
|
||||
get roomList() {
|
||||
return this._roomList;
|
||||
}
|
||||
|
||||
clearFilter() {
|
||||
this._roomListFilterMap.setApply(null);
|
||||
this._roomListFilterMap.applyOnce((roomId, vm) => vm.hidden = false);
|
||||
}
|
||||
|
||||
setFilter(query) {
|
||||
query = query.trim();
|
||||
if (query.length === 0) {
|
||||
this.clearFilter();
|
||||
} else {
|
||||
const filter = new RoomFilter(query);
|
||||
this._roomListFilterMap.setApply((roomId, vm) => {
|
||||
vm.hidden = !filter.matches(vm);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
26
src/domain/session/leftpanel/RoomFilter.js
Normal file
26
src/domain/session/leftpanel/RoomFilter.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
export class RoomFilter {
|
||||
constructor(query) {
|
||||
this._parts = query.split(" ").map(s => s.toLowerCase().trim());
|
||||
}
|
||||
|
||||
matches(roomTileVM) {
|
||||
const name = roomTileVM.name.toLowerCase();
|
||||
return this._parts.every(p => name.includes(p));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -29,6 +30,18 @@ export class RoomTileViewModel extends ViewModel {
|
|||
this._emitOpen = emitOpen;
|
||||
this._isOpen = false;
|
||||
this._wasUnreadWhenOpening = false;
|
||||
this._hidden = false;
|
||||
}
|
||||
|
||||
get hidden() {
|
||||
return this._hidden;
|
||||
}
|
||||
|
||||
set hidden(value) {
|
||||
if (value !== this._hidden) {
|
||||
this._hidden = value;
|
||||
this.emitChange("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// called by parent for now (later should integrate with router)
|
|
@ -426,7 +426,7 @@ export function tests() {
|
|||
function createStorageMock(session, pendingEvents = []) {
|
||||
return {
|
||||
readTxn() {
|
||||
return Promise.resolve({
|
||||
return {
|
||||
session: {
|
||||
get(key) {
|
||||
return Promise.resolve(session[key]);
|
||||
|
@ -442,7 +442,7 @@ export function tests() {
|
|||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
storeNames: {}
|
||||
};
|
||||
|
|
81
src/observable/map/ApplyMap.js
Normal file
81
src/observable/map/ApplyMap.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableMap} from "./BaseObservableMap.js";
|
||||
|
||||
export class ApplyMap extends BaseObservableMap {
|
||||
constructor(source, apply) {
|
||||
super();
|
||||
this._source = source;
|
||||
this._apply = apply;
|
||||
this._subscription = null;
|
||||
}
|
||||
|
||||
setApply(apply) {
|
||||
this._apply = apply;
|
||||
if (apply) {
|
||||
this.applyOnce(this._apply);
|
||||
}
|
||||
}
|
||||
|
||||
applyOnce(apply) {
|
||||
for (const [key, value] of this._source) {
|
||||
apply(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
onAdd(key, value) {
|
||||
if (this._apply) {
|
||||
this._apply(key, value);
|
||||
}
|
||||
this.emitAdd(key, value);
|
||||
}
|
||||
|
||||
onRemove(key, value) {
|
||||
this.emitRemove(key, value);
|
||||
}
|
||||
|
||||
onUpdate(key, value, params) {
|
||||
if (this._apply) {
|
||||
this._apply(key, value, params);
|
||||
}
|
||||
this.emitUpdate(key, value, params);
|
||||
}
|
||||
|
||||
onSubscribeFirst() {
|
||||
this._subscription = this._source.subscribe(this);
|
||||
if (this._apply) {
|
||||
this.applyOnce(this._apply);
|
||||
}
|
||||
super.onSubscribeFirst();
|
||||
}
|
||||
|
||||
onUnsubscribeLast() {
|
||||
super.onUnsubscribeLast();
|
||||
this._subscription = this._subscription();
|
||||
}
|
||||
|
||||
onReset() {
|
||||
if (this._apply) {
|
||||
this.applyOnce(this._apply);
|
||||
}
|
||||
this.emitReset();
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._source[Symbol.iterator]();
|
||||
}
|
||||
}
|
|
@ -17,56 +17,149 @@ limitations under the License.
|
|||
import {BaseObservableMap} from "./BaseObservableMap.js";
|
||||
|
||||
export class FilteredMap extends BaseObservableMap {
|
||||
constructor(source, mapper, updater) {
|
||||
constructor(source, filter) {
|
||||
super();
|
||||
this._source = source;
|
||||
this._mapper = mapper;
|
||||
this._updater = updater;
|
||||
this._mappedValues = new Map();
|
||||
this._filter = filter;
|
||||
/** @type {Map<string, bool>} */
|
||||
this._included = null;
|
||||
this._subscription = null;
|
||||
}
|
||||
|
||||
onAdd(key, value) {
|
||||
const mappedValue = this._mapper(value);
|
||||
this._mappedValues.set(key, mappedValue);
|
||||
this.emitAdd(key, mappedValue);
|
||||
setFilter(filter) {
|
||||
this._filter = filter;
|
||||
this.update();
|
||||
}
|
||||
|
||||
onRemove(key, _value) {
|
||||
const mappedValue = this._mappedValues.get(key);
|
||||
if (this._mappedValues.delete(key)) {
|
||||
this.emitRemove(key, mappedValue);
|
||||
/**
|
||||
* reapply the filter
|
||||
*/
|
||||
update() {
|
||||
// TODO: need to check if we have a subscriber already? If not, we really should not iterate the source?
|
||||
if (this._filter) {
|
||||
const hadFilterBefore = !!this._included;
|
||||
this._included = this._included || new Map();
|
||||
for (const [key, value] of this._source) {
|
||||
const isIncluded = this._filter(value, key);
|
||||
const wasIncluded = hadFilterBefore ? this._included.get(key) : true;
|
||||
this._included.set(key, isIncluded);
|
||||
this._emitForUpdate(wasIncluded, isIncluded, key, value);
|
||||
}
|
||||
} else { // no filter
|
||||
// did we have a filter before?
|
||||
if (this._included) {
|
||||
// add any non-included items again
|
||||
for (const [key, value] of this._source) {
|
||||
if (!this._included.get(key)) {
|
||||
this.emitAdd(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._included = null;
|
||||
}
|
||||
}
|
||||
|
||||
onChange(key, value, params) {
|
||||
const mappedValue = this._mappedValues.get(key);
|
||||
if (mappedValue !== undefined) {
|
||||
const newParams = this._updater(value, params);
|
||||
if (newParams !== undefined) {
|
||||
this.emitChange(key, mappedValue, newParams);
|
||||
onAdd(key, value) {
|
||||
if (this._filter) {
|
||||
const included = this._filter(value, key);
|
||||
this._included.set(key, included);
|
||||
if (!included) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.emitAdd(key, value);
|
||||
}
|
||||
|
||||
onRemove(key, value) {
|
||||
if (this._filter && !this._included.get(key)) {
|
||||
return;
|
||||
}
|
||||
this.emitRemove(key, value);
|
||||
}
|
||||
|
||||
onUpdate(key, value, params) {
|
||||
if (this._filter) {
|
||||
const wasIncluded = this._included.get(key);
|
||||
const isIncluded = this._filter(value, key);
|
||||
this._included.set(key, isIncluded);
|
||||
this._emitForUpdate(wasIncluded, isIncluded, key, value, params);
|
||||
}
|
||||
this.emitUpdate(key, value, params);
|
||||
}
|
||||
|
||||
_emitForUpdate(wasIncluded, isIncluded, key, value, params = null) {
|
||||
if (wasIncluded && !isIncluded) {
|
||||
this.emitRemove(key, value);
|
||||
} else if (!wasIncluded && isIncluded) {
|
||||
this.emitAdd(key, value);
|
||||
} else if (wasIncluded && isIncluded) {
|
||||
this.emitUpdate(key, value, params);
|
||||
}
|
||||
}
|
||||
|
||||
onSubscribeFirst() {
|
||||
for (let [key, value] of this._source) {
|
||||
const mappedValue = this._mapper(value);
|
||||
this._mappedValues.set(key, mappedValue);
|
||||
}
|
||||
this._subscription = this._source.subscribe(this);
|
||||
this.update();
|
||||
super.onSubscribeFirst();
|
||||
}
|
||||
|
||||
onUnsubscribeLast() {
|
||||
super.onUnsubscribeLast();
|
||||
this._mappedValues.clear();
|
||||
this._included = null;
|
||||
this._subscription = this._subscription();
|
||||
}
|
||||
|
||||
onReset() {
|
||||
this._mappedValues.clear();
|
||||
this.update();
|
||||
this.emitReset();
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._mappedValues.entries()[Symbol.iterator];
|
||||
return new FilterIterator(this._source, this._included);
|
||||
}
|
||||
}
|
||||
|
||||
class FilterIterator {
|
||||
constructor(map, _included) {
|
||||
this._included = _included;
|
||||
this._sourceIterator = map.entries();
|
||||
}
|
||||
|
||||
next() {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const sourceResult = this._sourceIterator.next();
|
||||
if (sourceResult.done) {
|
||||
return sourceResult;
|
||||
}
|
||||
const key = sourceResult.value[1];
|
||||
if (this._included.get(key)) {
|
||||
return sourceResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import {ObservableMap} from "./ObservableMap.js";
|
||||
// export function tests() {
|
||||
// return {
|
||||
// "filter preloaded list": assert => {
|
||||
// const source = new ObservableMap();
|
||||
// source.add("one", 1);
|
||||
// source.add("two", 2);
|
||||
// source.add("three", 3);
|
||||
// const odds = Array.from(new FilteredMap(source, x => x % 2 !== 0));
|
||||
// assert.equal(odds.length, 2);
|
||||
|
||||
// },
|
||||
// "filter added values": assert => {
|
||||
|
||||
// },
|
||||
// "filter removed values": assert => {
|
||||
|
||||
// },
|
||||
// "filter changed values": assert => {
|
||||
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -67,6 +67,7 @@ export class MappedMap extends BaseObservableMap {
|
|||
}
|
||||
|
||||
onUnsubscribeLast() {
|
||||
super.onUnsubscribeLast();
|
||||
this._subscription = this._subscription();
|
||||
this._mappedValues.clear();
|
||||
}
|
||||
|
|
|
@ -14,10 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
.LeftPanel {
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.LeftPanel .filter {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.LeftPanel .filter input {
|
||||
display: block;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.LeftPanel ul {
|
||||
|
@ -26,19 +35,25 @@ limitations under the License.
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.LeftPanel li {
|
||||
.RoomList {
|
||||
flex: 1 0 0;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.RoomList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.LeftPanel div.description {
|
||||
.RoomList .description {
|
||||
margin: 0;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.LeftPanel .description > .name {
|
||||
.RoomList .description > .name {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -14,70 +14,29 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ListView} from "../general/ListView.js";
|
||||
import {RoomTile} from "./RoomTile.js";
|
||||
import {LeftPanelView} from "./leftpanel/LeftPanelView.js";
|
||||
import {RoomView} from "./room/RoomView.js";
|
||||
import {SwitchView} from "../general/SwitchView.js";
|
||||
import {TemplateView} from "../general/TemplateView.js";
|
||||
import {RoomPlaceholderView} from "./RoomPlaceholderView.js";
|
||||
import {SessionStatusView} from "./SessionStatusView.js";
|
||||
import {tag} from "../general/html.js";
|
||||
|
||||
export class SessionView {
|
||||
constructor(viewModel) {
|
||||
this._viewModel = viewModel;
|
||||
this._middleSwitcher = null;
|
||||
this._roomList = null;
|
||||
this._currentRoom = null;
|
||||
this._root = null;
|
||||
this._onViewModelChange = this._onViewModelChange.bind(this);
|
||||
}
|
||||
|
||||
root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._viewModel.on("change", this._onViewModelChange);
|
||||
this._sessionStatusBar = new SessionStatusView(this._viewModel.sessionStatusViewModel);
|
||||
this._roomList = new ListView(
|
||||
{
|
||||
className: "RoomList",
|
||||
list: this._viewModel.roomList,
|
||||
onItemClick: (roomTile, event) => roomTile.clicked(event)
|
||||
},
|
||||
(room) => new RoomTile(room)
|
||||
);
|
||||
this._middleSwitcher = new SwitchView(new RoomPlaceholderView());
|
||||
|
||||
this._root = tag.div({className: "SessionView"}, [
|
||||
this._sessionStatusBar.mount(),
|
||||
tag.div({className: "main"}, [
|
||||
tag.div({className: "LeftPanel"}, this._roomList.mount()),
|
||||
this._middleSwitcher.mount()
|
||||
export class SessionView extends TemplateView {
|
||||
render(t, vm) {
|
||||
return t.div({
|
||||
className: "SessionView",
|
||||
"room-shown": vm => !!vm.currentRoom
|
||||
}, [
|
||||
t.view(new SessionStatusView(vm.sessionStatusViewModel)),
|
||||
t.div({className: "main"}, [
|
||||
t.view(new LeftPanelView(vm.leftPanelViewModel)),
|
||||
t.mapView(vm => vm.currentRoom, currentRoom => {
|
||||
if (currentRoom) {
|
||||
return new RoomView(currentRoom);
|
||||
} else {
|
||||
return new RoomPlaceholderView();
|
||||
}
|
||||
})
|
||||
])
|
||||
]);
|
||||
|
||||
return this._root;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this._roomList.unmount();
|
||||
this._middleSwitcher.unmount();
|
||||
this._viewModel.off("change", this._onViewModelChange);
|
||||
}
|
||||
|
||||
_onViewModelChange(prop) {
|
||||
if (prop === "currentRoom") {
|
||||
if (this._viewModel.currentRoom) {
|
||||
this._root.classList.add("room-shown");
|
||||
this._middleSwitcher.switch(new RoomView(this._viewModel.currentRoom));
|
||||
} else {
|
||||
this._root.classList.remove("room-shown");
|
||||
this._middleSwitcher.switch(new RoomPlaceholderView());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// changing viewModel not supported for now
|
||||
update() {}
|
||||
}
|
||||
|
|
55
src/ui/web/session/leftpanel/LeftPanelView.js
Normal file
55
src/ui/web/session/leftpanel/LeftPanelView.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {ListView} from "../../general/ListView.js";
|
||||
import {TemplateView} from "../../general/TemplateView.js";
|
||||
import {RoomTileView} from "./RoomTileView.js";
|
||||
|
||||
export class LeftPanelView extends TemplateView {
|
||||
render(t, vm) {
|
||||
const filterInput = t.input({
|
||||
type: "text",
|
||||
placeholder: vm.i18n`Filter rooms…`,
|
||||
"aria-label": vm.i18n`Filter rooms by name`,
|
||||
autocomplete: true,
|
||||
name: "room-filter",
|
||||
onInput: event => vm.setFilter(event.target.value),
|
||||
onKeydown: event => {
|
||||
if (event.key === "Escape" || event.key === "Esc") {
|
||||
filterInput.value = "";
|
||||
vm.clearFilter();
|
||||
}
|
||||
}
|
||||
});
|
||||
return t.div({className: "LeftPanel"}, [
|
||||
t.div({className: "filter"}, [
|
||||
filterInput,
|
||||
t.button({onClick: () => {
|
||||
filterInput.value = "";
|
||||
vm.clearFilter();
|
||||
}}, vm.i18n`Clear`)
|
||||
]),
|
||||
t.view(new ListView(
|
||||
{
|
||||
className: "RoomList",
|
||||
list: vm.roomList,
|
||||
onItemClick: (roomTile, event) => roomTile.clicked(event)
|
||||
},
|
||||
roomTileVM => new RoomTileView(roomTileVM)
|
||||
))
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,16 +15,26 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {TemplateView} from "../general/TemplateView.js";
|
||||
import {renderAvatar} from "../common.js";
|
||||
import {TemplateView} from "../../general/TemplateView.js";
|
||||
import {renderAvatar} from "../../common.js";
|
||||
|
||||
export class RoomTile extends TemplateView {
|
||||
export class RoomTileView extends TemplateView {
|
||||
render(t, vm) {
|
||||
return t.li({"className": {"active": vm => vm.isOpen}}, [
|
||||
const classes = {
|
||||
"active": vm => vm.isOpen,
|
||||
"hidden": vm => vm.hidden
|
||||
};
|
||||
return t.li({"className": classes}, [
|
||||
renderAvatar(t, vm, 32),
|
||||
t.div({className: "description"}, [
|
||||
t.div({className: {"name": true, unread: vm => vm.isUnread}}, vm => vm.name),
|
||||
t.div({className: {"badge": true, highlighted: vm => vm.isHighlighted, hidden: vm => !vm.badgeCount}}, vm => vm.badgeCount),
|
||||
t.div({
|
||||
className: {
|
||||
"badge": true,
|
||||
highlighted: vm => vm.isHighlighted,
|
||||
hidden: vm => !vm.badgeCount
|
||||
}
|
||||
}, vm => vm.badgeCount),
|
||||
])
|
||||
]);
|
||||
}
|
Loading…
Reference in a new issue