/* * This file belongs to Hoist, an application development toolkit * developed by Extremely Heavy Industries (www.xh.io | info@xh.io) * * Copyright © 2026 Extremely Heavy Industries Inc. */ import {SECONDS} from '@xh/hoist/utils/datetime'; import {throwIf} from '@xh/hoist/utils/js'; import {ViewManagerModel} from './ViewManagerModel'; import {JsonBlob} from '@xh/hoist/svc'; import {PlainObject, XH} from '@xh/hoist/core'; /** * Metadata describing {@link View} managed by {@link ViewManagerModel}. */ export class ViewInfo { /** Unique Id */ readonly token: string; /** App-defined type discriminator. */ readonly type: string; /** User-supplied descriptive name. */ readonly name: string; /** Description of the view. */ readonly description: string; /** User owning this view. Null if the view is global.*/ readonly owner: string; /** * True if the owner (which can be the current user) has made this view accessible to all other * users. Note always `false` for global views, to better distinguish the two sharing models. */ readonly isShared: boolean; /** True if this view is global and visible to all users. */ readonly isGlobal: boolean; /** Optional group name used for bucketing this view in display. */ readonly group: string; /** * Original meta-data on views associated JsonBlob. * Not typically used by applications. * @internal */ readonly meta: PlainObject; dateCreated: number; lastUpdated: number; lastUpdatedBy: string; readonly model: ViewManagerModel; constructor(blob: JsonBlob, model: ViewManagerModel) { throwIf(blob.type !== model.type, 'View type does not match ViewManager type.'); this.token = blob.token; this.type = blob.type; this.name = blob.name; this.description = blob.description; this.owner = blob.owner; this.meta = (blob.meta as PlainObject) ?? {}; this.isGlobal = !this.owner; this.group = this.meta.group ?? null; this.isShared = !!(!this.isGlobal && this.meta.isShared); // Round to seconds. See: https://github.com/xh/hoist-core/issues/423 this.dateCreated = Math.round(blob.dateCreated / SECONDS) * SECONDS; this.lastUpdated = Math.round(blob.lastUpdated / SECONDS) * SECONDS; this.lastUpdatedBy = blob.lastUpdatedBy; this.model = model; } get isOwned(): boolean { return this.owner === XH.getUsername(); } get isEditable(): boolean { return this.isOwned || (this.isGlobal && this.model.manageGlobal); } get isCurrentView(): boolean { return this.token === this.model.view.token; } /** * True if this view should appear on the users easy access menu. * * This value is computed with the user persisted state for the view. * * If the user has not set pinning state for the view, global views and * owned views are pinned by default. */ get isPinned(): boolean { return this.isUserPinned ?? (this.isGlobal || this.isOwned); } /** * The user indicated pin state for this view, or null if user has not indicated a preference. */ get isUserPinned(): boolean | null { return this.model.isUserPinned(this); } get typedName(): string { return `${this.model.typeDisplayName} "${this.name}"`; } }