mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
Merge pull request #138 from vector-im/bwindels/filter-room-list
Room list filtering
This commit is contained in:
commit
a7db44eabf
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RoomTileViewModel} from "./roomlist/RoomTileViewModel.js";
|
import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js";
|
||||||
import {RoomViewModel} from "./room/RoomViewModel.js";
|
import {RoomViewModel} from "./room/RoomViewModel.js";
|
||||||
import {SessionStatusViewModel} from "./SessionStatusViewModel.js";
|
import {SessionStatusViewModel} from "./SessionStatusViewModel.js";
|
||||||
import {ViewModel} from "../ViewModel.js";
|
import {ViewModel} from "../ViewModel.js";
|
||||||
@ -29,22 +30,22 @@ export class SessionViewModel extends ViewModel {
|
|||||||
reconnector: sessionContainer.reconnector,
|
reconnector: sessionContainer.reconnector,
|
||||||
session: sessionContainer.session,
|
session: sessionContainer.session,
|
||||||
})));
|
})));
|
||||||
|
this._leftPanelViewModel = new LeftPanelViewModel(this.childOptions({
|
||||||
|
rooms: this._session.rooms,
|
||||||
|
openRoom: this._openRoom.bind(this)
|
||||||
|
}));
|
||||||
this._currentRoomTileViewModel = null;
|
this._currentRoomTileViewModel = null;
|
||||||
this._currentRoomViewModel = 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() {
|
start() {
|
||||||
this._sessionStatusViewModel.start();
|
this._sessionStatusViewModel.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get leftPanelViewModel() {
|
||||||
|
return this._leftPanelViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
get sessionStatusViewModel() {
|
get sessionStatusViewModel() {
|
||||||
return this._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 Bruno Windels <bruno@windels.cloud>
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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._emitOpen = emitOpen;
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
this._wasUnreadWhenOpening = 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)
|
// called by parent for now (later should integrate with router)
|
@ -426,7 +426,7 @@ export function tests() {
|
|||||||
function createStorageMock(session, pendingEvents = []) {
|
function createStorageMock(session, pendingEvents = []) {
|
||||||
return {
|
return {
|
||||||
readTxn() {
|
readTxn() {
|
||||||
return Promise.resolve({
|
return {
|
||||||
session: {
|
session: {
|
||||||
get(key) {
|
get(key) {
|
||||||
return Promise.resolve(session[key]);
|
return Promise.resolve(session[key]);
|
||||||
@ -442,7 +442,7 @@ export function tests() {
|
|||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
},
|
},
|
||||||
storeNames: {}
|
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";
|
import {BaseObservableMap} from "./BaseObservableMap.js";
|
||||||
|
|
||||||
export class FilteredMap extends BaseObservableMap {
|
export class FilteredMap extends BaseObservableMap {
|
||||||
constructor(source, mapper, updater) {
|
constructor(source, filter) {
|
||||||
super();
|
super();
|
||||||
this._source = source;
|
this._source = source;
|
||||||
this._mapper = mapper;
|
this._filter = filter;
|
||||||
this._updater = updater;
|
/** @type {Map<string, bool>} */
|
||||||
this._mappedValues = new Map();
|
this._included = null;
|
||||||
|
this._subscription = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(key, value) {
|
setFilter(filter) {
|
||||||
const mappedValue = this._mapper(value);
|
this._filter = filter;
|
||||||
this._mappedValues.set(key, mappedValue);
|
this.update();
|
||||||
this.emitAdd(key, mappedValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(key, _value) {
|
/**
|
||||||
const mappedValue = this._mappedValues.get(key);
|
* reapply the filter
|
||||||
if (this._mappedValues.delete(key)) {
|
*/
|
||||||
this.emitRemove(key, mappedValue);
|
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) {
|
onAdd(key, value) {
|
||||||
const mappedValue = this._mappedValues.get(key);
|
if (this._filter) {
|
||||||
if (mappedValue !== undefined) {
|
const included = this._filter(value, key);
|
||||||
const newParams = this._updater(value, params);
|
this._included.set(key, included);
|
||||||
if (newParams !== undefined) {
|
if (!included) {
|
||||||
this.emitChange(key, mappedValue, newParams);
|
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() {
|
onSubscribeFirst() {
|
||||||
for (let [key, value] of this._source) {
|
this._subscription = this._source.subscribe(this);
|
||||||
const mappedValue = this._mapper(value);
|
this.update();
|
||||||
this._mappedValues.set(key, mappedValue);
|
|
||||||
}
|
|
||||||
super.onSubscribeFirst();
|
super.onSubscribeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast() {
|
||||||
super.onUnsubscribeLast();
|
super.onUnsubscribeLast();
|
||||||
this._mappedValues.clear();
|
this._included = null;
|
||||||
|
this._subscription = this._subscription();
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset() {
|
||||||
this._mappedValues.clear();
|
this.update();
|
||||||
this.emitReset();
|
this.emitReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
[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() {
|
onUnsubscribeLast() {
|
||||||
|
super.onUnsubscribeLast();
|
||||||
this._subscription = this._subscription();
|
this._subscription = this._subscription();
|
||||||
this._mappedValues.clear();
|
this._mappedValues.clear();
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,19 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
.LeftPanel {
|
.LeftPanel {
|
||||||
overflow-y: auto;
|
display: flex;
|
||||||
overscroll-behavior: contain;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LeftPanel .filter {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.LeftPanel .filter input {
|
||||||
|
display: block;
|
||||||
|
flex: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LeftPanel ul {
|
.LeftPanel ul {
|
||||||
@ -26,19 +35,25 @@ limitations under the License.
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LeftPanel li {
|
.RoomList {
|
||||||
|
flex: 1 0 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoomList li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LeftPanel div.description {
|
.RoomList .description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LeftPanel .description > .name {
|
.RoomList .description > .name {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -14,70 +14,29 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListView} from "../general/ListView.js";
|
import {LeftPanelView} from "./leftpanel/LeftPanelView.js";
|
||||||
import {RoomTile} from "./RoomTile.js";
|
|
||||||
import {RoomView} from "./room/RoomView.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 {RoomPlaceholderView} from "./RoomPlaceholderView.js";
|
||||||
import {SessionStatusView} from "./SessionStatusView.js";
|
import {SessionStatusView} from "./SessionStatusView.js";
|
||||||
import {tag} from "../general/html.js";
|
|
||||||
|
|
||||||
export class SessionView {
|
export class SessionView extends TemplateView {
|
||||||
constructor(viewModel) {
|
render(t, vm) {
|
||||||
this._viewModel = viewModel;
|
return t.div({
|
||||||
this._middleSwitcher = null;
|
className: "SessionView",
|
||||||
this._roomList = null;
|
"room-shown": vm => !!vm.currentRoom
|
||||||
this._currentRoom = null;
|
}, [
|
||||||
this._root = null;
|
t.view(new SessionStatusView(vm.sessionStatusViewModel)),
|
||||||
this._onViewModelChange = this._onViewModelChange.bind(this);
|
t.div({className: "main"}, [
|
||||||
}
|
t.view(new LeftPanelView(vm.leftPanelViewModel)),
|
||||||
|
t.mapView(vm => vm.currentRoom, currentRoom => {
|
||||||
root() {
|
if (currentRoom) {
|
||||||
return this._root;
|
return new RoomView(currentRoom);
|
||||||
}
|
} else {
|
||||||
|
return new RoomPlaceholderView();
|
||||||
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()
|
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
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 Bruno Windels <bruno@windels.cloud>
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView.js";
|
||||||
import {renderAvatar} from "../common.js";
|
import {renderAvatar} from "../../common.js";
|
||||||
|
|
||||||
export class RoomTile extends TemplateView {
|
export class RoomTileView extends TemplateView {
|
||||||
render(t, vm) {
|
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),
|
renderAvatar(t, vm, 32),
|
||||||
t.div({className: "description"}, [
|
t.div({className: "description"}, [
|
||||||
t.div({className: {"name": true, unread: vm => vm.isUnread}}, vm => vm.name),
|
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 New Issue
Block a user