This commit is contained in:
Bruno Windels 2020-10-09 19:43:11 +02:00
parent 83ac3eccc5
commit 6c2c29a7da
6 changed files with 114 additions and 58 deletions

View file

@ -17,10 +17,11 @@ limitations under the License.
import {Segment} from "./Navigation.js"; import {Segment} from "./Navigation.js";
export class URLRouter { export class URLRouter {
constructor(history, navigation) { constructor({history, navigation, redirect}) {
this._subscription = null; this._subscription = null;
this._history = history; this._history = history;
this._navigation = navigation; this._navigation = navigation;
this._redirect = redirect;
} }
attach() { attach() {
@ -32,7 +33,7 @@ export class URLRouter {
applyUrl(url) { applyUrl(url) {
const segments = this._segmentsFromUrl(url); const segments = this._segmentsFromUrl(url);
const path = this._navigation.pathFrom(segments); const path = this._redirect(segments, this._navigation);
this._navigation.applyPath(path); this._navigation.applyPath(path);
} }

View file

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {Navigation} from "./Navigation.js"; import {Navigation, Segment} from "./Navigation.js";
import {URLRouter} from "./URLRouter.js";
export function createNavigation() { export function createNavigation() {
return new Navigation(function allowsChild(parent, child) { return new Navigation(function allowsChild(parent, child) {
@ -25,8 +26,65 @@ export function createNavigation() {
return type === "login" || type === "session"; return type === "login" || type === "session";
case "session": case "session":
return type === "room" || type === "rooms" || type === "settings"; return type === "room" || type === "rooms" || type === "settings";
case "rooms":
// downside of the approach: both of these will control which tile is selected
return type === "room" || type === "empty-grid-tile";
default: default:
return false; return false;
} }
}); });
} }
export function createRouter({history, navigation}) {
return new URLRouter({history, navigation, redirect});
}
function redirect(urlParts, navigation) {
const {path} = navigation;
const segments = urlParts.reduce((output, s) => {
// redirect open-room action to grid/non-grid url
if (s.type === "open-room") {
const rooms = path.get("rooms");
if (rooms) {
output = output.concat(roomsSegmentWithRoom(rooms, s.value, path));
}
return rooms.concat(new Segment("room", s.value));
}
return output.concat(s);
}, []);
return navigation.pathFrom(segments);
}
function roomsSegmentWithRoom(rooms, roomId, path) {
// find the index of either the current room,
// or the current selected empty tile,
// to put the new room in
// TODO: is rooms.value a string or an array?
const room = path.get("room");
let index = 0;
if (room) {
index = rooms.value.indexOf(room.value);
} else {
const emptyGridTile = path.get("empty-grid-tile");
if (emptyGridTile) {
index = emptyGridTile.value;
}
}
const newRooms = rooms.slice();
newRooms[index] = roomId;
return new Segment("rooms", newRooms);
}
function parseUrlValue(type, iterator) {
if (type === "rooms") {
const roomIds = iterator.next().value.split(",");
const selectedIndex = parseInt(iterator.next().value, 10);
const roomId = roomIds[selectedIndex];
if (roomId) {
return [new Segment(type, roomIds), new Segment("room", roomId)];
} else {
return [new Segment(type, roomIds), new Segment("empty-grid-tile", selectedIndex)];
}
}
}

View file

@ -26,7 +26,7 @@ export class RoomGridViewModel extends ViewModel {
} }
roomViewModelAt(i) { roomViewModelAt(i) {
return this._viewModels[i]?.vm; return this._viewModels[i];
} }
get focusIndex() { get focusIndex() {
@ -37,14 +37,9 @@ export class RoomGridViewModel extends ViewModel {
if (idx === this._selectedIndex) { if (idx === this._selectedIndex) {
return; return;
} }
const oldItem = this._viewModels[this._selectedIndex];
oldItem?.tileVM?.close();
this._selectedIndex = idx; this._selectedIndex = idx;
const newItem = this._viewModels[this._selectedIndex]; const vm = this._viewModels[this._selectedIndex];
if (newItem) { vm?.focus();
newItem.vm.focus();
newItem.tileVM.open();
}
this.emitChange("focusedIndex"); this.emitChange("focusedIndex");
} }
get width() { get width() {
@ -58,14 +53,12 @@ export class RoomGridViewModel extends ViewModel {
/** /**
* Sets a pair of room and room tile view models at the current index * Sets a pair of room and room tile view models at the current index
* @param {RoomViewModel} vm * @param {RoomViewModel} vm
* @param {RoomTileViewModel} tileVM
* @package * @package
*/ */
setRoomViewModel(vm, tileVM) { setRoomViewModel(vm) {
const old = this._viewModels[this._selectedIndex]; const old = this._viewModels[this._selectedIndex];
this.disposeTracked(old?.vm); this.disposeTracked(old);
old?.tileVM?.close(); this._viewModels[this._selectedIndex] = this.track(vm);
this._viewModels[this._selectedIndex] = {vm: this.track(vm), tileVM};
this.emitChange(`${this._selectedIndex}`); this.emitChange(`${this._selectedIndex}`);
} }
@ -73,7 +66,7 @@ export class RoomGridViewModel extends ViewModel {
* @package * @package
*/ */
tryFocusRoom(roomId) { tryFocusRoom(roomId) {
const index = this._viewModels.findIndex(vms => vms?.vm.id === roomId); const index = this._viewModels.findIndex(vm => vm.id === roomId);
if (index >= 0) { if (index >= 0) {
this.setFocusIndex(index); this.setFocusIndex(index);
return true; return true;
@ -82,15 +75,15 @@ export class RoomGridViewModel extends ViewModel {
} }
/** /**
* Returns the first set of room and room tile vm, * Returns the first set of room vm,
* and untracking them so they are not owned by this view model anymore. * and untracking it so it is not owned by this view model anymore.
* @package * @package
*/ */
getAndUntrackFirst() { getAndUntrackFirst() {
for (const item of this._viewModels) { for (const vm of this._viewModels) {
if (item) { if (vm) {
this.untrack(item.vm); this.untrack(vm);
return item; return vm;
} }
} }
} }

View file

@ -33,22 +33,33 @@ export class SessionViewModel extends ViewModel {
}))); })));
this._leftPanelViewModel = new LeftPanelViewModel(this.childOptions({ this._leftPanelViewModel = new LeftPanelViewModel(this.childOptions({
rooms: this._sessionContainer.session.rooms, rooms: this._sessionContainer.session.rooms,
openRoom: this._openRoom.bind(this), // this will go over navigation as well
gridEnabled: { gridEnabled: {
get: () => !!this._gridViewModel, get: () => !!this._gridViewModel,
set: value => this._enabledGrid(value) set: value => this._enabledGrid(value)
} }
})); }));
this._currentRoomTileViewModel = null;
this._currentRoomViewModel = null; this._currentRoomViewModel = null;
this._gridViewModel = null; this._gridViewModel = null;
// this gives us the active room, also in the grid?
this.track(this.navigator.observe("room").subscribe(roomId => {
}));
// this gives us a set of room ids in the grid
this.track(this.navigator.observe("rooms").subscribe(value => {
if (value) {
const roomIds = typeof value === "string" ? value.split(",") : [];
// also update grid
this._enabledGrid(roomIds);
}
}));
} }
start() { start() {
this._sessionStatusViewModel.start(); this._sessionStatusViewModel.start();
} }
get selectionId() { get activeSection() {
if (this._currentRoomViewModel) { if (this._currentRoomViewModel) {
return this._currentRoomViewModel.id; return this._currentRoomViewModel.id;
} else if (this._gridViewModel) { } else if (this._gridViewModel) {
@ -73,62 +84,56 @@ export class SessionViewModel extends ViewModel {
return this._roomList; return this._roomList;
} }
get currentRoom() { get currentRoomViewModel() {
return this._currentRoomViewModel; return this._currentRoomViewModel;
} }
// TODO: this should also happen based on URLs
_enabledGrid(enabled) { _enabledGrid(enabled) {
if (enabled) { if (enabled) {
this._gridViewModel = this.track(new RoomGridViewModel(this.childOptions({width: 3, height: 2}))); this._gridViewModel = this.track(new RoomGridViewModel(this.childOptions({width: 3, height: 2})));
// transfer current room // transfer current room
if (this._currentRoomViewModel) { if (this._currentRoomViewModel) {
this.untrack(this._currentRoomViewModel); this.untrack(this._currentRoomViewModel);
this._gridViewModel.setRoomViewModel(this._currentRoomViewModel, this._currentRoomTileViewModel); this._gridViewModel.setRoomViewModel(this._currentRoomViewModel);
this._currentRoomViewModel = null; this._currentRoomViewModel = null;
this._currentRoomTileViewModel = null;
} }
} else { } else {
const VMs = this._gridViewModel.getAndUntrackFirst(); const vm = this._gridViewModel.getAndUntrackFirst();
if (VMs) { if (vm) {
this._currentRoomViewModel = this.track(VMs.vm); this._currentRoomViewModel = this.track(vm);
this._currentRoomTileViewModel = VMs.tileVM;
this._currentRoomTileViewModel.open();
} }
this._gridViewModel = this.disposeTracked(this._gridViewModel); this._gridViewModel = this.disposeTracked(this._gridViewModel);
} }
this.emitChange("middlePanelViewType"); this.emitChange("middlePanelViewType");
} }
_closeCurrentRoom() { _openRoom(roomId) {
// no closing in grid for now as it is disabled on narrow viewports // already open?
if (!this._gridViewModel) { if (this._gridViewModel?.tryFocusRoom(roomId)) {
this._currentRoomTileViewModel?.close();
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
return true;
}
}
_openRoom(room, roomTileVM) {
if (this._gridViewModel?.tryFocusRoom(room.id)) {
return; return;
} else if (this._currentRoomViewModel?.id === room.id) { } else if (this._currentRoomViewModel?.id === roomId) {
return;
}
const room = this._session.rooms.get(roomId);
// not found? close current room and show placeholder
if (!room) {
if (this._gridViewModel) {
this._gridViewModel.setRoomViewModel(null);
} else {
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
}
return; return;
} }
const roomVM = new RoomViewModel(this.childOptions({ const roomVM = new RoomViewModel(this.childOptions({
room, room,
ownUserId: this._sessionContainer.session.user.id, ownUserId: this._sessionContainer.session.user.id,
closeCallback: () => {
if (this._closeCurrentRoom()) {
this.emitChange("currentRoom");
}
},
})); }));
roomVM.load(); roomVM.load();
if (this._gridViewModel) { if (this._gridViewModel) {
this._gridViewModel.setRoomViewModel(roomVM, roomTileVM); this._gridViewModel.setRoomViewModel(roomVM);
} else { } else {
this._closeCurrentRoom(); this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
this._currentRoomTileViewModel = roomTileVM;
this._currentRoomViewModel = this.track(roomVM); this._currentRoomViewModel = this.track(roomVM);
this.emitChange("currentRoom"); this.emitChange("currentRoom");
} }

View file

@ -45,7 +45,6 @@ export class RoomTileViewModel extends ViewModel {
} }
} }
// called by parent for now (later should integrate with router)
close() { close() {
if (this._isOpen) { if (this._isOpen) {
this._isOpen = false; this._isOpen = false;

View file

@ -32,14 +32,14 @@ export class SessionView extends TemplateView {
t.view(new SessionStatusView(vm.sessionStatusViewModel)), t.view(new SessionStatusView(vm.sessionStatusViewModel)),
t.div({className: "main"}, [ t.div({className: "main"}, [
t.view(new LeftPanelView(vm.leftPanelViewModel)), t.view(new LeftPanelView(vm.leftPanelViewModel)),
t.mapView(vm => vm.selectionId, selectionId => { t.mapView(vm => vm.activeSection, activeSection => {
switch (selectionId) { switch (activeSection) {
case "roomgrid": case "roomgrid":
return new RoomGridView(vm.roomGridViewModel); return new RoomGridView(vm.roomGridViewModel);
case "placeholder": case "placeholder":
return new StaticView(t => t.div({className: "room-placeholder"}, t.h2(vm.i18n`Choose a room on the left side.`))); return new StaticView(t => t.div({className: "room-placeholder"}, t.h2(vm.i18n`Choose a room on the left side.`)));
default: //room id default: //room id
return new RoomView(vm.currentRoom); return new RoomView(vm.currentRoomViewModel);
} }
}) })
]) ])