// // Copyright © 2020, 2021 Anticrm Platform Contributors. // Copyright © 2022 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import { AvatarProvider, AvatarType, ChannelProvider, Contact, contactId, Employee, EmployeeAccount, formatName, getFirstName, getLastName, getName } from '@hcengineering/contact' import { Doc, getCurrentAccount, IdMap, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core' import { createQuery, getClient } from '@hcengineering/presentation' import { TemplateDataProvider } from '@hcengineering/templates' import { DropdownIntlItem, getCurrentLocation, getPanelURI, Location, ResolvedLocation } from '@hcengineering/ui' import view, { Filter } from '@hcengineering/view' import { FilterQuery } from '@hcengineering/view-resources' import { get, writable } from 'svelte/store' import contact from './plugin' export function getChannelProviders (providers: ChannelProvider[]): Map, ChannelProvider> { const map = new Map, ChannelProvider>() for (const provider of providers) { map.set(provider._id, provider) } return map } export function formatDate (dueDateMs: Timestamp): string { return new Date(dueDateMs).toLocaleString('default', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } export async function employeeSort (value: Array>): Promise>> { return value.sort((a, b) => { const employeeId1 = a as Ref | null | undefined const employeeId2 = b as Ref | null | undefined if (employeeId1 == null && employeeId2 != null) { return 1 } if (employeeId1 != null && employeeId2 == null) { return -1 } if (employeeId1 != null && employeeId2 != null) { const employee1 = get(employeeByIdStore).get(employeeId1) const employee2 = get(employeeByIdStore).get(employeeId2) const name1 = employee1 != null ? getName(employee1) : '' const name2 = employee2 != null ? getName(employee2) : '' return name1.localeCompare(name2) } return 0 }) } export async function filterChannelInResult (filter: Filter, onUpdate: () => void): Promise> { const result = await getRefs(filter, onUpdate) return { $in: result } } export async function filterChannelNinResult (filter: Filter, onUpdate: () => void): Promise> { const result = await getRefs(filter, onUpdate) return { $nin: result } } export async function getRefs (filter: Filter, onUpdate: () => void): Promise>> { const lq = FilterQuery.getLiveQuery(filter.index) const client = getClient() const mode = await client.findOne(view.class.FilterMode, { _id: filter.mode }) if (mode === undefined) return [] const promise = new Promise>>((resolve, reject) => { const refresh = lq.query( contact.class.Channel, { provider: { $in: filter.value } }, (refs) => { const result = Array.from(new Set(refs.map((p) => p.attachedTo))) FilterQuery.results.set(filter.index, result) resolve(result) onUpdate() } ) if (!refresh) { resolve(FilterQuery.results.get(filter.index) ?? []) } }) return await promise } export async function getCurrentEmployeeName (): Promise { const me = getCurrentAccount() as EmployeeAccount return formatName(me.name) } export async function getCurrentEmployeeEmail (): Promise { const me = getCurrentAccount() as EmployeeAccount return me.email } export async function getCurrentEmployeePosition (): Promise { const me = getCurrentAccount() as EmployeeAccount const client = getClient() const employee = await client.findOne(contact.class.Employee, { _id: me.employee }) if (employee !== undefined) { return employee.position ?? '' } } export async function getContactName (provider: TemplateDataProvider): Promise { const value = provider.get(contact.class.Contact) as Contact if (value === undefined) return const client = getClient() const hierarchy = client.getHierarchy() if (hierarchy.isDerived(value._class, contact.class.Person)) { return getName(value) } else { return value.name } } export async function getContactLastName (provider: TemplateDataProvider): Promise { const value = provider.get(contact.class.Contact) as Contact if (value === undefined) return const client = getClient() const hierarchy = client.getHierarchy() if (hierarchy.isDerived(value._class, contact.class.Person)) { return getLastName(value.name) } else { return '' } } export async function getContactFirstName (provider: TemplateDataProvider): Promise { const value = provider.get(contact.class.Contact) as Contact if (value === undefined) return const client = getClient() const hierarchy = client.getHierarchy() if (hierarchy.isDerived(value._class, contact.class.Person)) { return getFirstName(value.name) } else { return value.name } } export async function getContactChannel (value: Contact, provider: Ref): Promise { if (value === undefined) return const client = getClient() const res = await client.findOne(contact.class.Channel, { attachedTo: value._id, provider }) return res?.value ?? '' } export async function getContactLink (doc: Doc): Promise { const loc = getCurrentLocation() loc.path.length = 2 loc.fragment = undefined loc.query = undefined loc.path[2] = contactId loc.path[3] = doc._id return loc } export async function resolveLocation (loc: Location): Promise { if (loc.path[2] !== contactId) { return undefined } const id = loc.path[3] as Ref return await generateLocation(loc, id) } async function generateLocation (loc: Location, id: Ref): Promise { const client = getClient() const doc = await client.findOne(contact.class.Contact, { _id: id }) if (doc === undefined) { console.error(`Could not find contact ${id}.`) return undefined } const appComponent = loc.path[0] ?? '' const workspace = loc.path[1] ?? '' return { loc: { path: [appComponent, workspace], fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content') }, shouldNavigate: false, defaultLocation: { path: [appComponent, workspace, contactId], fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content') } } } export const employeeByIdStore = writable>(new Map()) export const employeesStore = writable([]) export const channelProviders = writable([]) function fillStores (): void { const client = getClient() if (client !== undefined) { const query = createQuery(true) query.query(contact.class.Employee, {}, (res) => { employeesStore.set(res) employeeByIdStore.set(toIdMap(res)) }) const providerQuery = createQuery(true) providerQuery.query(contact.class.ChannelProvider, {}, (res) => channelProviders.set(res)) } else { setTimeout(() => fillStores(), 50) } } fillStores() export function getAvatarTypeDropdownItems (hasGravatar: boolean): DropdownIntlItem[] { return [ { id: AvatarType.COLOR, label: contact.string.UseColor }, { id: AvatarType.IMAGE, label: contact.string.UseImage }, ...(hasGravatar ? [ { id: AvatarType.GRAVATAR, label: contact.string.UseGravatar } ] : []) ] } export function getAvatarProviderId (avatar?: string | null): Ref | undefined { if (avatar === null || avatar === undefined || avatar === '') { return } if (!avatar.includes('://')) { return contact.avatarProvider.Image } const [schema] = avatar.split('://') switch (schema) { case AvatarType.GRAVATAR: return contact.avatarProvider.Gravatar case AvatarType.COLOR: return contact.avatarProvider.Color } return contact.avatarProvider.Image }