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";
export class URLRouter {
constructor(history, navigation) {
constructor({history, navigation, redirect}) {
this._subscription = null;
this._history = history;
this._navigation = navigation;
this._redirect = redirect;
}
attach() {
@ -32,7 +33,7 @@ export class URLRouter {
applyUrl(url) {
const segments = this._segmentsFromUrl(url);
const path = this._navigation.pathFrom(segments);
const path = this._redirect(segments, this._navigation);
this._navigation.applyPath(path);
}

View file

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {Navigation} from "./Navigation.js";
import {Navigation, Segment} from "./Navigation.js";
import {URLRouter} from "./URLRouter.js";
export function createNavigation() {
return new Navigation(function allowsChild(parent, child) {
@ -25,8 +26,65 @@ export function createNavigation() {
return type === "login" || type === "session";
case "session":
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:
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) {
return this._viewModels[i]?.vm;
return this._viewModels[i];
}
get focusIndex() {
@ -37,14 +37,9 @@ export class RoomGridViewModel extends ViewModel {
if (idx === this._selectedIndex) {
return;
}
const oldItem = this._viewModels[this._selectedIndex];
oldItem?.tileVM?.close();
this._selectedIndex = idx;
const newItem = this._viewModels[this._selectedIndex];
if (newItem) {
newItem.vm.focus();
newItem.tileVM.open();
}
const vm = this._viewModels[this._selectedIndex];
vm?.focus();
this.emitChange("focusedIndex");
}
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
* @param {RoomViewModel} vm
* @param {RoomTileViewModel} tileVM
* @package
*/
setRoomViewModel(vm, tileVM) {
setRoomViewModel(vm) {
const old = this._viewModels[this._selectedIndex];
this.disposeTracked(old?.vm);
old?.tileVM?.close();
this._viewModels[this._selectedIndex] = {vm: this.track(vm), tileVM};
this.disposeTracked(old);
this._viewModels[this._selectedIndex] = this.track(vm);
this.emitChange(`${this._selectedIndex}`);
}
@ -73,7 +66,7 @@ export class RoomGridViewModel extends ViewModel {
* @package
*/
tryFocusRoom(roomId) {
const index = this._viewModels.findIndex(vms => vms?.vm.id === roomId);
const index = this._viewModels.findIndex(vm => vm.id === roomId);
if (index >= 0) {
this.setFocusIndex(index);
return true;
@ -82,15 +75,15 @@ export class RoomGridViewModel extends ViewModel {
}
/**
* Returns the first set of room and room tile vm,
* and untracking them so they are not owned by this view model anymore.
* Returns the first set of room vm,
* and untracking it so it is not owned by this view model anymore.
* @package
*/
getAndUntrackFirst() {
for (const item of this._viewModels) {
if (item) {
this.untrack(item.vm);
return item;
for (const vm of this._viewModels) {
if (vm) {
this.untrack(vm);
return vm;
}
}
}

View file

@ -33,22 +33,33 @@ export class SessionViewModel extends ViewModel {
})));
this._leftPanelViewModel = new LeftPanelViewModel(this.childOptions({
rooms: this._sessionContainer.session.rooms,
openRoom: this._openRoom.bind(this),
// this will go over navigation as well
gridEnabled: {
get: () => !!this._gridViewModel,
set: value => this._enabledGrid(value)
}
}));
this._currentRoomTileViewModel = null;
this._currentRoomViewModel = 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() {
this._sessionStatusViewModel.start();
}
get selectionId() {
get activeSection() {
if (this._currentRoomViewModel) {
return this._currentRoomViewModel.id;
} else if (this._gridViewModel) {
@ -73,62 +84,56 @@ export class SessionViewModel extends ViewModel {
return this._roomList;
}
get currentRoom() {
get currentRoomViewModel() {
return this._currentRoomViewModel;
}
// TODO: this should also happen based on URLs
_enabledGrid(enabled) {
if (enabled) {
this._gridViewModel = this.track(new RoomGridViewModel(this.childOptions({width: 3, height: 2})));
// transfer current room
if (this._currentRoomViewModel) {
this.untrack(this._currentRoomViewModel);
this._gridViewModel.setRoomViewModel(this._currentRoomViewModel, this._currentRoomTileViewModel);
this._gridViewModel.setRoomViewModel(this._currentRoomViewModel);
this._currentRoomViewModel = null;
this._currentRoomTileViewModel = null;
}
} else {
const VMs = this._gridViewModel.getAndUntrackFirst();
if (VMs) {
this._currentRoomViewModel = this.track(VMs.vm);
this._currentRoomTileViewModel = VMs.tileVM;
this._currentRoomTileViewModel.open();
const vm = this._gridViewModel.getAndUntrackFirst();
if (vm) {
this._currentRoomViewModel = this.track(vm);
}
this._gridViewModel = this.disposeTracked(this._gridViewModel);
}
this.emitChange("middlePanelViewType");
}
_closeCurrentRoom() {
// no closing in grid for now as it is disabled on narrow viewports
if (!this._gridViewModel) {
this._currentRoomTileViewModel?.close();
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
return true;
}
}
_openRoom(room, roomTileVM) {
if (this._gridViewModel?.tryFocusRoom(room.id)) {
_openRoom(roomId) {
// already open?
if (this._gridViewModel?.tryFocusRoom(roomId)) {
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;
}
const roomVM = new RoomViewModel(this.childOptions({
room,
ownUserId: this._sessionContainer.session.user.id,
closeCallback: () => {
if (this._closeCurrentRoom()) {
this.emitChange("currentRoom");
}
},
}));
roomVM.load();
if (this._gridViewModel) {
this._gridViewModel.setRoomViewModel(roomVM, roomTileVM);
this._gridViewModel.setRoomViewModel(roomVM);
} else {
this._closeCurrentRoom();
this._currentRoomTileViewModel = roomTileVM;
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
this._currentRoomViewModel = this.track(roomVM);
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() {
if (this._isOpen) {
this._isOpen = false;

View file

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