Improve settings navigation

This commit is contained in:
ivk 2025-10-17 13:42:49 +02:00
parent 6e3ac97f75
commit 65428cb199
3 changed files with 34 additions and 13 deletions

View file

@ -418,9 +418,9 @@ export class CalendarSettingsView extends BaseTopLevelView implements TopLevelVi
onNewUrl(args: Record<string, any>, requestedPath: string) {
if (args.folder || !m.route.get().startsWith(SETTINGS_PREFIX)) {
// ensure that current viewer will be reinitialized
const folder = this._allSettingsFolders().find((folder) => folder.url === requestedPath)
const folder = this._allSettingsFolders().find((folder) => folder.matches(args.folder, args.id))
if (folder && this.selectedFolder.path === folder.path) {
if (folder && this.selectedFolder.isSameFolder(folder)) {
// folder path has not changed
this.selectedFolder = folder // instance of SettingsFolder might have been changed in membership update, so replace this instance

View file

@ -1,5 +1,5 @@
import type { lazyIcon } from "../gui/base/Icon.js"
import type { TranslationKey, MaybeTranslation } from "../misc/LanguageViewModel.js"
import type { MaybeTranslation } from "../misc/LanguageViewModel.js"
import { isSelectedPrefix } from "../gui/base/NavButton.js"
import type { lazy } from "@tutao/tutanota-utils"
import { assertMainOrNode } from "../api/common/Env.js"
@ -7,22 +7,36 @@ import { UpdatableSettingsViewer } from "./Interfaces.js"
assertMainOrNode()
type SettingsFolderPath = string | { folder: string; id: string }
interface SettingsFolderPath {
folder: string
id: string | null
}
export class SettingsFolder<T> {
readonly url: string
private readonly path: SettingsFolderPath
private _isVisibleHandler: lazy<boolean>
/**
* @param name Displayed as the folder name
* @param icon Displayed as the folder icon
* @param path Either a string which means URL like `/settings/mail` or an object which means URL like `/settings/templates/1`
* @param viewerCreator A function to produce instances of {@link UpdatableSettingsViewer}.
* @param data Additional data that the folder can carry
*/
constructor(
readonly name: () => MaybeTranslation,
readonly icon: lazyIcon,
readonly path: SettingsFolderPath,
path: string | SettingsFolderPath,
readonly viewerCreator: lazy<UpdatableSettingsViewer>,
readonly data: T,
) {
this.path = typeof path === "string" ? { folder: path, id: null } : path
this.url =
typeof path === "string" ? `/settings/${encodeURIComponent(path)}` : `/settings/${encodeURIComponent(path.folder)}/${encodeURIComponent(path.id)}`
this.path.id == null
? `/settings/${encodeURIComponent(this.path.folder)}`
: `/settings/${encodeURIComponent(this.path.folder)}/${encodeURIComponent(this.path.id)}`
this._isVisibleHandler = () => true
}
@ -40,11 +54,20 @@ export class SettingsFolder<T> {
return this
}
get folder(): string | null {
return typeof this.path === "string" ? null : this.path.folder
get folder(): string {
return this.path.folder
}
get id(): string | null {
return typeof this.path === "string" ? null : this.path.id
return this.path.id
}
matches(folder: string, id: string | undefined | null): boolean {
// eslint-disable-next-line eqeqeq -- this.id can be null and id can be undefined
return folder === this.folder && id == this.id
}
isSameFolder(anotherFolder: SettingsFolder<unknown>): boolean {
return this.matches(anotherFolder.folder, anotherFolder.id)
}
}

View file

@ -667,14 +667,12 @@ export class SettingsView extends BaseTopLevelView implements TopLevelView<Setti
if (!args.folder) {
this._setUrl(this._userFolders[0].url)
} else if (args.folder || !m.route.get().startsWith("/settings")) {
// TODO: a bit of a hack, instead of requestedPath we should find the folder by :folder and :id params
const requestedPathWithoutHash = requestedPath.split("#")[0]
// ensure that current viewer will be reinitialized
const folder = this._allSettingsFolders().find((folder) => folder.url === requestedPathWithoutHash)
const folder = this._allSettingsFolders().find((folder) => folder.matches(args.folder, args.id))
if (!folder) {
this._setUrl(this._userFolders[0].url)
} else if (this._selectedFolder.path === folder.path) {
} else if (this._selectedFolder.isSameFolder(folder)) {
// folder path has not changed
this._selectedFolder = folder // instance of SettingsFolder might have been changed in membership update, so replace this instance