WIP
This commit is contained in:
parent
83ac3eccc5
commit
6c2c29a7da
6 changed files with 114 additions and 58 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
})
|
||||
])
|
||||
|
|
Reference in a new issue