Update window title (#2138)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
Alex 2022-06-24 14:46:53 +07:00 committed by GitHub
parent 32d2b6ed69
commit 6c3070c85d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 5 deletions

View File

@ -409,6 +409,10 @@ export function createModel (builder: Builder): void {
presenter: tracker.component.IssuePreview presenter: tracker.component.IssuePreview
}) })
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ObjectTitle, {
titleProvider: tracker.function.getIssueTitle
})
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AttributePresenter, { builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AttributePresenter, {
presenter: tracker.component.PriorityPresenter presenter: tracker.component.PriorityPresenter
}) })

View File

@ -40,6 +40,7 @@ import type {
ObjectEditor, ObjectEditor,
ObjectEditorHeader, ObjectEditorHeader,
ObjectFactory, ObjectFactory,
ObjectTitle,
ObjectValidator, ObjectValidator,
PreviewPresenter, PreviewPresenter,
SpaceHeader, SpaceHeader,
@ -158,6 +159,11 @@ export class TObjectFactory extends TClass implements ObjectFactory {
component!: AnyComponent component!: AnyComponent
} }
@Mixin(view.mixin.ObjectTitle, core.class.Class)
export class TObjectTitle extends TClass implements ObjectTitle {
titleProvider!: Resource<<T extends Doc>(client: Client, ref: Ref<T>) => Promise<string>>
}
@Model(view.class.ViewletPreference, preference.class.Preference) @Model(view.class.ViewletPreference, preference.class.Preference)
export class TViewletPreference extends TPreference implements ViewletPreference { export class TViewletPreference extends TPreference implements ViewletPreference {
attachedTo!: Ref<Viewlet> attachedTo!: Ref<Viewlet>
@ -276,6 +282,7 @@ export function createModel (builder: Builder): void {
TActionCategory, TActionCategory,
TObjectValidator, TObjectValidator,
TObjectFactory, TObjectFactory,
TObjectTitle,
TObjectEditorHeader, TObjectEditorHeader,
THTMLPresenter, THTMLPresenter,
TSpaceHeader, TSpaceHeader,

View File

@ -56,7 +56,7 @@ import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.sv
import Views from './components/views/Views.svelte' import Views from './components/views/Views.svelte'
import KanbanView from './components/issues/KanbanView.svelte' import KanbanView from './components/issues/KanbanView.svelte'
import tracker from './plugin' import tracker from './plugin'
import { getIssueId } from './utils' import { getIssueId, getIssueTitle } from './utils'
export async function queryIssue<D extends Issue> ( export async function queryIssue<D extends Issue> (
_class: Ref<Class<D>>, _class: Ref<Class<D>>,
@ -148,5 +148,8 @@ export default async (): Promise<Resources> => ({
}, },
completion: { completion: {
IssueQuery: async (client: Client, query: string) => await queryIssue(tracker.class.Issue, client, query) IssueQuery: async (client: Client, query: string) => await queryIssue(tracker.class.Issue, client, query)
},
function: {
getIssueTitle
} }
}) })

View File

@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import type { IntlString } from '@anticrm/platform' import type { IntlString, Resource } from '@anticrm/platform'
import { mergeIds } from '@anticrm/platform' import { mergeIds } from '@anticrm/platform'
import tracker, { trackerId } from '../../tracker/lib' import tracker, { trackerId } from '../../tracker/lib'
import { AnyComponent } from '@anticrm/ui' import { AnyComponent } from '@anticrm/ui'
import { Client, Doc, Ref } from '@anticrm/core'
export default mergeIds(trackerId, tracker, { export default mergeIds(trackerId, tracker, {
string: { string: {
@ -209,5 +210,8 @@ export default mergeIds(trackerId, tracker, {
Roadmap: '' as AnyComponent, Roadmap: '' as AnyComponent,
TeamProjects: '' as AnyComponent, TeamProjects: '' as AnyComponent,
IssuePreview: '' as AnyComponent IssuePreview: '' as AnyComponent
},
function: {
getIssueTitle: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>
} }
}) })

View File

@ -14,7 +14,7 @@
// //
import contact, { Employee, formatName } from '@anticrm/contact' import contact, { Employee, formatName } from '@anticrm/contact'
import { DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core' import { Doc, DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core'
import { Asset, IntlString, translate } from '@anticrm/platform' import { Asset, IntlString, translate } from '@anticrm/platform'
import { import {
IssuePriority, IssuePriority,
@ -504,3 +504,13 @@ export async function getKanbanStatuses (
} }
return [] return []
} }
export async function getIssueTitle (client: TxOperations, ref: Ref<Doc>): Promise<string> {
const issue = await client.findOne(
tracker.class.Issue,
{ _id: ref as Ref<Issue> },
{ lookup: { space: tracker.class.Team } }
)
if (issue?.$lookup?.space === undefined) throw new Error(`Issue Team not found, _id: ${ref}`)
return getIssueId(issue.$lookup.space, issue)
}

View File

@ -154,6 +154,13 @@ export interface ObjectValidator extends Class<Doc> {
validator: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>> validator: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
} }
/**
* @public
*/
export interface ObjectTitle extends Class<Doc> {
titleProvider: Resource<<T extends Doc>(client: Client, ref: Ref<T>) => Promise<string>>
}
/** /**
* @public * @public
*/ */
@ -384,6 +391,7 @@ const view = plugin(viewId, {
ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>, ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>,
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>, ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
ObjectFactory: '' as Ref<Mixin<ObjectFactory>>, ObjectFactory: '' as Ref<Mixin<ObjectFactory>>,
ObjectTitle: '' as Ref<Mixin<ObjectTitle>>,
SpaceHeader: '' as Ref<Mixin<SpaceHeader>>, SpaceHeader: '' as Ref<Mixin<SpaceHeader>>,
SpaceName: '' as Ref<Mixin<SpaceName>>, SpaceName: '' as Ref<Mixin<SpaceName>>,
IgnoreActions: '' as Ref<Mixin<IgnoreActions>>, IgnoreActions: '' as Ref<Mixin<IgnoreActions>>,

View File

@ -15,10 +15,10 @@
<script lang="ts"> <script lang="ts">
import calendar from '@anticrm/calendar' import calendar from '@anticrm/calendar'
import contact, { Employee, EmployeeAccount } from '@anticrm/contact' import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
import core, { Client, getCurrentAccount, Ref, Space } from '@anticrm/core' import core, { Class, Client, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
import notification, { NotificationStatus } from '@anticrm/notification' import notification, { NotificationStatus } from '@anticrm/notification'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { getMetadata, IntlString } from '@anticrm/platform' import { getMetadata, getResource, IntlString } from '@anticrm/platform'
import { Avatar, createQuery, setClient } from '@anticrm/presentation' import { Avatar, createQuery, setClient } from '@anticrm/presentation'
import { import {
AnyComponent, AnyComponent,
@ -26,6 +26,7 @@
closeTooltip, closeTooltip,
Component, Component,
DatePickerPopup, DatePickerPopup,
fetchMetadataLocalStorage,
getCurrentLocation, getCurrentLocation,
location, location,
Location, Location,
@ -35,6 +36,8 @@
showPopup, showPopup,
TooltipInstance TooltipInstance
} from '@anticrm/ui' } from '@anticrm/ui'
import login from '@anticrm/login'
import view from '@anticrm/view'
import { ActionContext, ActionHandler } from '@anticrm/view-resources' import { ActionContext, ActionHandler } from '@anticrm/view-resources'
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@anticrm/workbench' import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@anticrm/workbench'
import { onDestroy, tick } from 'svelte' import { onDestroy, tick } from 'svelte'
@ -125,9 +128,33 @@
closePopup() closePopup()
await syncLoc(loc) await syncLoc(loc)
await updateWindowTitle(loc)
}) })
) )
async function updateWindowTitle (loc: Location) {
const title = (await getWindowTitle(loc)) ?? getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform'
const ws = fetchMetadataLocalStorage(login.metadata.CurrentWorkspace)
document.title = ws == null ? title : `${ws} - ${title}`
}
async function getWindowTitle (loc: Location) {
if (loc.fragment == null) return
const hierarchy = client.getHierarchy()
const [, _id, _class] = decodeURIComponent(loc.fragment).split('|')
if (_class == null) return
const clazz = hierarchy.getClass(_class as Ref<Class<Doc>>)
if (!hierarchy.hasMixin(clazz, view.mixin.ObjectTitle)) return
const mixin = hierarchy.as(clazz, view.mixin.ObjectTitle)
const titleProvider = await getResource(mixin.titleProvider)
try {
return await titleProvider(client, _id as Ref<Doc>)
} catch (err: any) {
console.error(err)
}
}
async function syncLoc (loc: Location): Promise<void> { async function syncLoc (loc: Location): Promise<void> {
const app = loc.path.length > 1 ? (loc.path[1] as Ref<Application>) : undefined const app = loc.path.length > 1 ? (loc.path[1] as Ref<Application>) : undefined
const space = loc.path.length > 2 ? (loc.path[2] as Ref<Space>) : undefined const space = loc.path.length > 2 ? (loc.path[2] as Ref<Space>) : undefined