mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
Chat and sidebar fixes (#6813)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
030bbb589b
commit
304beeae31
@ -46,8 +46,8 @@ export function defineActions (builder: Builder): void {
|
||||
|
||||
createAction(builder, {
|
||||
action: chunter.actionImpl.StartConversation,
|
||||
label: chunter.string.StartConversation,
|
||||
icon: chunter.icon.Thread,
|
||||
label: chunter.string.Message,
|
||||
icon: view.icon.Bubble,
|
||||
input: 'focus',
|
||||
category: chunter.category.Chunter,
|
||||
target: contact.mixin.Employee,
|
||||
|
@ -21,6 +21,7 @@ import core from '@hcengineering/model-core'
|
||||
import view from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import { WidgetType } from '@hcengineering/workbench'
|
||||
import contact from '@hcengineering/contact'
|
||||
|
||||
import chunter from './plugin'
|
||||
import { defineActions } from './actions'
|
||||
@ -303,6 +304,16 @@ export function createModel (builder: Builder): void {
|
||||
disabled: [{ _class: 1 }, { space: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { createdOn: -1 }]
|
||||
})
|
||||
|
||||
// Extensions
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: contact.extension.EmployeePopupActions,
|
||||
component: chunter.component.DirectMessageButton
|
||||
})
|
||||
builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, {
|
||||
extension: activity.extension.ActivityEmployeePresenter,
|
||||
component: chunter.component.EmployeePresenter
|
||||
})
|
||||
|
||||
defineActions(builder)
|
||||
defineNotifications(builder)
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ export default mergeIds(chunterId, chunter, {
|
||||
ChatMessageNotificationLabel: '' as AnyComponent,
|
||||
ThreadNotificationPresenter: '' as AnyComponent,
|
||||
JoinChannelNotificationPresenter: '' as AnyComponent,
|
||||
WorkbenchTabExtension: '' as AnyComponent
|
||||
WorkbenchTabExtension: '' as AnyComponent,
|
||||
DirectMessageButton: '' as AnyComponent,
|
||||
EmployeePresenter: '' as AnyComponent
|
||||
},
|
||||
action: {
|
||||
MarkCommentUnread: '' as Ref<Action>,
|
||||
|
@ -185,8 +185,7 @@ export function createModel (builder: Builder): void {
|
||||
label: love.string.MeetingRoom,
|
||||
type: WidgetType.Flexible,
|
||||
icon: love.icon.Cam,
|
||||
component: love.component.VideoWidget,
|
||||
size: 'medium'
|
||||
component: love.component.VideoWidget
|
||||
},
|
||||
love.ids.VideoWidget
|
||||
)
|
||||
|
@ -316,6 +316,7 @@ input.search {
|
||||
border-radius: 50%;
|
||||
}
|
||||
&:not(.small-gap, .large-gap) { margin-right: .375rem; }
|
||||
&.no-gap { margin-right: 0; }
|
||||
&.small-gap { margin-right: .25rem; }
|
||||
&.large-gap { margin-right: .5rem; }
|
||||
&.flow:last-child { margin-right: 0; }
|
||||
|
@ -270,6 +270,9 @@
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
|
||||
&.relative {
|
||||
position: relative;
|
||||
}
|
||||
&.xx-small,
|
||||
&.inline,
|
||||
&.tiny,
|
||||
|
@ -107,9 +107,9 @@
|
||||
on:keydown
|
||||
>
|
||||
{#if loading}
|
||||
<div class="icon"><Spinner size={'small'} /></div>
|
||||
<div class="icon no-gap"><Spinner size={'small'} /></div>
|
||||
{:else if icon}
|
||||
<div class="icon"><Icon {icon} {iconProps} size={actualIconSize} /></div>
|
||||
<div class="icon no-gap"><Icon {icon} {iconProps} size={actualIconSize} /></div>
|
||||
{/if}
|
||||
{#if label}<span><Label {label} params={labelParams} /></span>{/if}
|
||||
{#if title}<span>{title}</span>{/if}
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let lazy = false
|
||||
export let minHeight: string | null = null
|
||||
export let highlightIndex: number | undefined = undefined
|
||||
export let items: any[] = []
|
||||
export let getKey: (index: number) => string = (index) => index.toString()
|
||||
|
||||
const refs: HTMLElement[] = []
|
||||
@ -65,6 +66,8 @@
|
||||
r?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
}
|
||||
}
|
||||
|
||||
$: array = items.length > 0 ? items : Array(count)
|
||||
</script>
|
||||
|
||||
{#if count}
|
||||
@ -75,7 +78,7 @@
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
>
|
||||
{#each Array(count) as _, row (getKey(row))}
|
||||
{#each array as _, row (getKey(row))}
|
||||
{#if lazy}
|
||||
<div style="min-height: {minHeight}">
|
||||
<Lazy>
|
||||
@ -145,6 +148,6 @@
|
||||
.list-container {
|
||||
min-width: 0;
|
||||
// border-radius: 0.25rem;
|
||||
user-select: none;
|
||||
//user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -81,6 +81,7 @@
|
||||
<style lang="scss">
|
||||
.container {
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
@ -161,7 +161,7 @@
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-row-center left-items" style:-webkit-app-region={'no-drag'}>
|
||||
<div class="flex-row-center left-items flex-gap-0-5" style:-webkit-app-region={'no-drag'}>
|
||||
<RootBarExtension position="left" />
|
||||
</div>
|
||||
<div
|
||||
@ -216,7 +216,7 @@
|
||||
min-height: var(--status-bar-height);
|
||||
height: var(--status-bar-height);
|
||||
// min-width: 600px;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 150%;
|
||||
background-color: var(--theme-statusbar-color);
|
||||
// border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
|
@ -14,15 +14,9 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { getClient, LiteMessageViewer } from '@hcengineering/presentation'
|
||||
import { ComponentExtensions, getClient, LiteMessageViewer } from '@hcengineering/presentation'
|
||||
import { Person, type PersonAccount } from '@hcengineering/contact'
|
||||
import {
|
||||
Avatar,
|
||||
EmployeePresenter,
|
||||
personAccountByIdStore,
|
||||
personByIdStore,
|
||||
SystemAvatar
|
||||
} from '@hcengineering/contact-resources'
|
||||
import { Avatar, personAccountByIdStore, personByIdStore, SystemAvatar } from '@hcengineering/contact-resources'
|
||||
import core, { Account, Doc, Ref, Timestamp, type WithLookup } from '@hcengineering/core'
|
||||
import { Icon, Label, resizeObserver, TimeSince, tooltip } from '@hcengineering/ui'
|
||||
import { Asset, getEmbeddedLabel, IntlString } from '@hcengineering/platform'
|
||||
@ -131,7 +125,7 @@
|
||||
/>
|
||||
</DocNavLink>
|
||||
{:else if person}
|
||||
<EmployeePresenter value={person} shouldShowAvatar={false} compact showStatus={false} />
|
||||
<ComponentExtensions extension={activity.extension.ActivityEmployeePresenter} props={{ person }} />
|
||||
{:else}
|
||||
<Label label={core.string.System} />
|
||||
{/if}
|
||||
|
@ -19,9 +19,9 @@
|
||||
ActivityMessageViewType
|
||||
} from '@hcengineering/activity'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { Avatar, EmployeePresenter, SystemAvatar } from '@hcengineering/contact-resources'
|
||||
import { Avatar, SystemAvatar } from '@hcengineering/contact-resources'
|
||||
import core, { Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ComponentExtensions, getClient } from '@hcengineering/presentation'
|
||||
import { Action, Icon, Label } from '@hcengineering/ui'
|
||||
import { getActions, restrictionStore, showMenu } from '@hcengineering/view-resources'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
@ -209,7 +209,7 @@
|
||||
<div class="header clear-mins">
|
||||
{#if person}
|
||||
<div class="username">
|
||||
<EmployeePresenter value={person} shouldShowAvatar={false} compact />
|
||||
<ComponentExtensions extension={activity.extension.ActivityEmployeePresenter} props={{ person }} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="strong">
|
||||
|
@ -30,7 +30,7 @@ import {
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import type { AnyComponent, ComponentExtensionId } from '@hcengineering/ui'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -334,6 +334,9 @@ export default plugin(activityId, {
|
||||
AllFilter: '' as Ref<ActivityMessagesFilter>,
|
||||
MentionNotification: '' as Ref<Doc>
|
||||
},
|
||||
extension: {
|
||||
ActivityEmployeePresenter: '' as ComponentExtensionId
|
||||
},
|
||||
function: {
|
||||
ShouldScrollToActivity: '' as Resource<() => boolean>
|
||||
},
|
||||
|
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import contact, { Person, Employee } from '@hcengineering/contact'
|
||||
import { EmployeePresenter } from '@hcengineering/contact-resources'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { getCurrentLocation, location, Location } from '@hcengineering/ui'
|
||||
import { decodeObjectURI } from '@hcengineering/view'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { chunterId } from '@hcengineering/chunter'
|
||||
import { notificationId } from '@hcengineering/notification'
|
||||
|
||||
import { createDirect } from '../utils'
|
||||
import { openChannel } from '../navigation'
|
||||
import chunter from '../plugin'
|
||||
|
||||
export let person: Person | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
function canNavigateToDirect (location: Location, person: Person | undefined): boolean {
|
||||
const app = location.path[2]
|
||||
if (app !== chunterId && app !== notificationId) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (person === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return hierarchy.hasMixin(person, contact.mixin.Employee) && (person as Employee).active
|
||||
}
|
||||
|
||||
async function openEmployeeDirect (): Promise<void> {
|
||||
if (person === undefined) return
|
||||
|
||||
const dm = await createDirect([person._id as Ref<Employee>])
|
||||
if (dm === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
const [_id] = decodeObjectURI(loc.path[3]) ?? []
|
||||
|
||||
if (_id === dm) {
|
||||
return
|
||||
}
|
||||
|
||||
openChannel(dm, chunter.class.DirectMessage, undefined, true)
|
||||
}
|
||||
</script>
|
||||
|
||||
<EmployeePresenter
|
||||
value={person}
|
||||
shouldShowAvatar={false}
|
||||
compact
|
||||
onEmployeeEdit={canNavigateToDirect($location, person) ? openEmployeeDirect : undefined}
|
||||
/>
|
@ -0,0 +1,49 @@
|
||||
<!--
|
||||
// Copyright © 2024 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { ModernButton, getCurrentLocation } from '@hcengineering/ui'
|
||||
import view, { decodeObjectURI } from '@hcengineering/view'
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
|
||||
import chunter from '../plugin'
|
||||
import { createDirect } from '../utils'
|
||||
import { openChannelInSidebar } from '../navigation'
|
||||
|
||||
export let employee: Employee
|
||||
|
||||
async function openDirect (): Promise<void> {
|
||||
const dm = await createDirect([employee._id])
|
||||
if (dm === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
const [_id] = decodeObjectURI(loc.path[3]) ?? []
|
||||
|
||||
if (_id === dm) {
|
||||
return
|
||||
}
|
||||
|
||||
await openChannelInSidebar(dm, chunter.class.DirectMessage, undefined, undefined, true)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModernButton
|
||||
label={chunter.string.Message}
|
||||
icon={view.icon.Bubble}
|
||||
size="small"
|
||||
iconSize="small"
|
||||
on:click={openDirect}
|
||||
/>
|
@ -60,6 +60,7 @@
|
||||
flex-shrink: 0;
|
||||
margin: 0.25rem 0;
|
||||
height: 1.875rem;
|
||||
font-size: 0.75rem;
|
||||
|
||||
&:not(:first-child)::after {
|
||||
position: absolute;
|
||||
|
@ -102,7 +102,7 @@
|
||||
<div class="antiHSpacer" />
|
||||
{:else if secondaryNotifyMarker}
|
||||
<div class="antiHSpacer" />
|
||||
<NotifyMarker count={0} kind="secondary" size="x-small" />
|
||||
<NotifyMarker count={0} kind="simple" />
|
||||
<div class="antiHSpacer" />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
@ -53,6 +53,8 @@ import ThreadViewPanel from './components/threads/ThreadViewPanel.svelte'
|
||||
import ChatWidget from './components/ChatWidget.svelte'
|
||||
import ChatWidgetTab from './components/ChatWidgetTab.svelte'
|
||||
import WorkbenchTabExtension from './components/WorkbenchTabExtension.svelte'
|
||||
import DirectMessageButton from './components/DirectMessageButton.svelte'
|
||||
import EmployeePresenter from './components/ChunterEmployeePresenter.svelte'
|
||||
|
||||
import {
|
||||
chunterSpaceLinkFragmentProvider,
|
||||
@ -181,7 +183,9 @@ export default async (): Promise<Resources> => ({
|
||||
JoinChannelNotificationPresenter,
|
||||
ChatWidget,
|
||||
ChatWidgetTab,
|
||||
WorkbenchTabExtension
|
||||
WorkbenchTabExtension,
|
||||
DirectMessageButton,
|
||||
EmployeePresenter
|
||||
},
|
||||
activity: {
|
||||
ChannelCreatedMessage,
|
||||
|
@ -31,7 +31,12 @@ import { getChannelName, isThreadMessage } from './utils'
|
||||
import chunter from './plugin'
|
||||
import { threadMessagesStore } from './stores'
|
||||
|
||||
export function openChannel (_id: string, _class: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>): void {
|
||||
export function openChannel (
|
||||
_id: string,
|
||||
_class: Ref<Class<Doc>>,
|
||||
thread?: Ref<ActivityMessage>,
|
||||
forceApplication = false
|
||||
): void {
|
||||
const loc = getCurrentLocation()
|
||||
const id = encodeObjectURI(_id, _class)
|
||||
|
||||
@ -39,6 +44,10 @@ export function openChannel (_id: string, _class: Ref<Class<Doc>>, thread?: Ref<
|
||||
return
|
||||
}
|
||||
|
||||
if (forceApplication) {
|
||||
loc.path[2] = chunterId
|
||||
}
|
||||
|
||||
loc.path[3] = id
|
||||
loc.query = { ...loc.query, message: null }
|
||||
|
||||
@ -317,7 +326,12 @@ export async function closeThreadInSidebarChannel (widget: Widget, tab: ChatWidg
|
||||
}, 100)
|
||||
}
|
||||
|
||||
export async function openThreadInSidebar (_id: Ref<ActivityMessage>, msg?: ActivityMessage, doc?: Doc): Promise<void> {
|
||||
export async function openThreadInSidebar (
|
||||
_id: Ref<ActivityMessage>,
|
||||
msg?: ActivityMessage,
|
||||
doc?: Doc,
|
||||
selectedMessageId?: Ref<ActivityMessage>
|
||||
): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
const widget = client.getModel().findAllSync(workbench.class.Widget, { _id: chunter.ids.ChatWidget })[0]
|
||||
@ -362,6 +376,7 @@ export async function openThreadInSidebar (_id: Ref<ActivityMessage>, msg?: Acti
|
||||
_id: object?._id,
|
||||
_class: object?._class,
|
||||
thread: message._id,
|
||||
selectedMessageId,
|
||||
channelName: name
|
||||
}
|
||||
}
|
||||
@ -436,7 +451,7 @@ export async function locationDataResolver (loc: Location): Promise<LocationData
|
||||
const linkProviders = client.getModel().findAllSync(view.mixin.LinkIdProvider, {})
|
||||
const _id: Ref<Doc> | undefined = await parseLinkId(linkProviders, id, _class)
|
||||
|
||||
const object = await client.findOne(_class, { _id })
|
||||
const object = hierarchy.hasClass(_class) ? await client.findOne(_class, { _id }) : undefined
|
||||
if (object === undefined) return { name: await translate(chunter.string.Chat, {}, get(languageStore)) }
|
||||
|
||||
const titleIntl = client.getHierarchy().getClass(object._class).label
|
||||
|
@ -251,7 +251,9 @@ export default plugin(chunterId, {
|
||||
},
|
||||
function: {
|
||||
CanTranslateMessage: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
OpenThreadInSidebar: '' as Resource<(_id: Ref<ActivityMessage>, msg?: ActivityMessage, doc?: Doc) => Promise<void>>,
|
||||
OpenThreadInSidebar: '' as Resource<
|
||||
(_id: Ref<ActivityMessage>, msg?: ActivityMessage, doc?: Doc, selectedId?: Ref<ActivityMessage>) => Promise<void>
|
||||
>,
|
||||
OpenChannelInSidebar: '' as Resource<
|
||||
(
|
||||
_id: Ref<Doc>,
|
||||
|
@ -102,6 +102,7 @@
|
||||
"People": "People",
|
||||
"For": "For",
|
||||
"SelectUsers": "Select users",
|
||||
"AddGuest": "Add guest"
|
||||
"AddGuest": "Add guest",
|
||||
"ViewProfile": "View profile"
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +102,7 @@
|
||||
"People": "Personas",
|
||||
"For": "Para",
|
||||
"SelectUsers": "Seleccionar usuarios",
|
||||
"AddGuest": "Añadir invitado"
|
||||
"AddGuest": "Añadir invitado",
|
||||
"ViewProfile": "Ver perfil"
|
||||
}
|
||||
}
|
@ -102,6 +102,7 @@
|
||||
"People": "Personnes",
|
||||
"For": "Pour",
|
||||
"SelectUsers": "Sélectionner des utilisateurs",
|
||||
"AddGuest": "Ajouter un invité"
|
||||
"AddGuest": "Ajouter un invité",
|
||||
"ViewProfile": "Voir le profil"
|
||||
}
|
||||
}
|
@ -102,6 +102,7 @@
|
||||
"People": "Pessoas",
|
||||
"For": "Para",
|
||||
"SelectUsers": "Selecionar utilizadores",
|
||||
"AddGuest": "Adicionar convidado"
|
||||
"AddGuest": "Adicionar convidado",
|
||||
"ViewProfile": "Ver perfil"
|
||||
}
|
||||
}
|
@ -102,6 +102,7 @@
|
||||
"People": "Люди",
|
||||
"For": "Для",
|
||||
"SelectUsers": "Выберите пользователей",
|
||||
"AddGuest": "Добавить гостя"
|
||||
"AddGuest": "Добавить гостя",
|
||||
"ViewProfile": "Посмотреть профиль"
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +102,7 @@
|
||||
"People": "人员",
|
||||
"For": "为",
|
||||
"SelectUsers": "选择用户",
|
||||
"AddGuest": "添加访客"
|
||||
"AddGuest": "添加访客",
|
||||
"ViewProfile": "查看资料"
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@
|
||||
import { Employee, Person } from '@hcengineering/contact'
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import ui, { IconSize } from '@hcengineering/ui'
|
||||
import ui, { IconSize, LabelAndProps } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip, employeeByIdStore, personByIdStore } from '..'
|
||||
import PersonPresenter from '../components/PersonPresenter.svelte'
|
||||
import contact from '../plugin'
|
||||
import EmployeePreviewPopup from './EmployeePreviewPopup.svelte'
|
||||
|
||||
export let value: Ref<Person> | WithLookup<Person> | null | undefined
|
||||
export let showPopup: boolean = true
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let shouldShowName: boolean = true
|
||||
@ -24,16 +26,26 @@
|
||||
export let compact: boolean = false
|
||||
export let showStatus: boolean = false
|
||||
|
||||
$: employeeValue = typeof value === 'string' ? $personByIdStore.get(value) : value
|
||||
$: employeeValue = typeof value === 'string' ? ($personByIdStore.get(value) as Employee) : (value as Employee)
|
||||
|
||||
$: active =
|
||||
employeeValue !== undefined ? $employeeByIdStore.get(employeeValue?._id as Ref<Employee>)?.active ?? false : false
|
||||
$: active = employeeValue !== undefined ? $employeeByIdStore.get(employeeValue?._id)?.active ?? false : false
|
||||
|
||||
function getPreviewPopup (active: boolean, value: Employee | undefined): LabelAndProps | undefined {
|
||||
if (!active || value === undefined || !showPopup) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
component: EmployeePreviewPopup,
|
||||
props: { employeeId: value._id }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PersonPresenter
|
||||
value={employeeValue}
|
||||
{tooltipLabels}
|
||||
onEdit={onEmployeeEdit}
|
||||
customTooltip={getPreviewPopup(active, employeeValue)}
|
||||
{shouldShowAvatar}
|
||||
{shouldShowName}
|
||||
{avatarSize}
|
||||
|
@ -1,103 +1,150 @@
|
||||
<script lang="ts">
|
||||
import { Employee, PersonAccount, getName, Status } from '@hcengineering/contact'
|
||||
import { getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import Avatar from './Avatar.svelte'
|
||||
import { Button, Label, resizeObserver, showPopup } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { ModernButton, navigate, resizeObserver } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '@hcengineering/view'
|
||||
import { getObjectLinkFragment } from '@hcengineering/view-resources'
|
||||
import { ComponentExtensions, getClient } from '@hcengineering/presentation'
|
||||
|
||||
import contact from '../plugin'
|
||||
import { employeeByIdStore } from '../utils'
|
||||
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
|
||||
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
|
||||
import Edit from './icons/Edit.svelte'
|
||||
import Avatar from './Avatar.svelte'
|
||||
import { employeeByIdStore, personAccountByPersonId, statusByUserStore } from '../utils'
|
||||
import { EmployeePresenter } from '../index'
|
||||
|
||||
export let employeeId: Ref<Employee>
|
||||
|
||||
const client = getClient()
|
||||
const me = (getCurrentAccount() as PersonAccount).person
|
||||
$: editable = employeeId === me
|
||||
|
||||
const statusesQuery = createQuery()
|
||||
let status: Status | undefined = undefined
|
||||
$: employee = $employeeByIdStore.get(employeeId) as Employee
|
||||
statusesQuery.query(contact.class.Status, { attachedTo: employeeId }, (res) => {
|
||||
status = res[0]
|
||||
})
|
||||
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function onEdit () {
|
||||
showPopup(
|
||||
EmployeeSetStatusPopup,
|
||||
{
|
||||
currentStatus: status
|
||||
},
|
||||
undefined,
|
||||
() => {},
|
||||
(newStatus: Status) => {
|
||||
if (status && newStatus) {
|
||||
client.updateDoc(contact.class.Status, status.space, status._id, { ...newStatus })
|
||||
} else if (status && !newStatus) {
|
||||
client.removeDoc(contact.class.Status, status.space, status._id)
|
||||
} else {
|
||||
client.addCollection(contact.class.Status, employee.space, employeeId, contact.mixin.Employee, 'statuses', {
|
||||
name: newStatus.name,
|
||||
dueDate: newStatus.dueDate
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
dispatch('close')
|
||||
let employee: Employee | undefined = undefined
|
||||
|
||||
$: employee = $employeeByIdStore.get(employeeId)
|
||||
$: accounts = $personAccountByPersonId.get(employeeId) ?? []
|
||||
$: isOnline = accounts.some((account) => $statusByUserStore.get(account._id)?.online === true)
|
||||
|
||||
// const statusesQuery = createQuery()
|
||||
// let editable = false
|
||||
// let status: Status | undefined = undefined
|
||||
// $: editable = employeeId === me
|
||||
// statusesQuery.query(contact.class.Status, { attachedTo: employeeId }, (res) => {
|
||||
// status = res[0]
|
||||
// })
|
||||
|
||||
// function setStatus (): void {
|
||||
// if (!employee) return
|
||||
// showPopup(
|
||||
// EmployeeSetStatusPopup,
|
||||
// {
|
||||
// currentStatus: status
|
||||
// },
|
||||
// undefined,
|
||||
// () => {},
|
||||
// async (newStatus: Status) => {
|
||||
// if (status && newStatus) {
|
||||
// await client.updateDoc(contact.class.Status, status.space, status._id, { ...newStatus })
|
||||
// } else if (status && !newStatus) {
|
||||
// await client.removeDoc(contact.class.Status, status.space, status._id)
|
||||
// } else {
|
||||
// await client.addCollection(contact.class.Status, employee.space, employeeId, contact.mixin.Employee, 'statuses', {
|
||||
// name: newStatus.name,
|
||||
// dueDate: newStatus.dueDate
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// dispatch('close')
|
||||
// }
|
||||
|
||||
async function viewProfile (): Promise<void> {
|
||||
if (employee === undefined) return
|
||||
const panelComponent = hierarchy.classHierarchyMixin(employee._class as Ref<Class<Doc>>, view.mixin.ObjectPanel)
|
||||
const comp = panelComponent?.component ?? view.component.EditDoc
|
||||
const loc = await getObjectLinkFragment(hierarchy, employee, {}, comp)
|
||||
navigate(loc)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="antiPopup p-4 flex-col"
|
||||
class="root flex-col"
|
||||
use:resizeObserver={() => {
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
>
|
||||
{#if employee}
|
||||
<div class="flex-col-center pb-2">
|
||||
<Avatar size={'x-large'} person={employee} name={employee.name} />
|
||||
<div class="flex-presenter flex-gap-2 p-2">
|
||||
<Avatar size="large" person={employee} name={employee.name} />
|
||||
<span class="username">
|
||||
<EmployeePresenter value={employee} shouldShowAvatar={false} showPopup={false} compact />
|
||||
</span>
|
||||
<span class="hulyAvatar-statusMarker small relative mt-0-5" class:online={isOnline} class:offline={!isOnline} />
|
||||
</div>
|
||||
<div class="pb-2">{getName(client.getHierarchy(), employee)}</div>
|
||||
<DocNavLink object={employee}>
|
||||
<Label label={contact.string.ViewFullProfile} />
|
||||
</DocNavLink>
|
||||
{#if status}
|
||||
<div class="pb-2">
|
||||
<Label label={contact.string.Status} />
|
||||
<div class="flex-row-stretch statusContainer">
|
||||
<div class="pr-2">
|
||||
<EmployeeStatusPresenter {employee} withTooltip={false} />
|
||||
</div>
|
||||
{#if editable}
|
||||
<div class="setStatusButton">
|
||||
<Button icon={Edit} title={contact.string.SetStatus} on:click={onEdit} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if editable}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="flex-row-stretch over-underline pb-2" on:click={onEdit}>
|
||||
<Label label={contact.string.SetStatus} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="separator" />
|
||||
<div class="flex-presenter flex-gap-2 p-2">
|
||||
<ComponentExtensions extension={contact.extension.EmployeePopupActions} props={{ employee }} />
|
||||
<ModernButton
|
||||
label={contact.string.ViewProfile}
|
||||
icon={contact.icon.Person}
|
||||
size="small"
|
||||
iconSize="small"
|
||||
on:click={viewProfile}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!--{#if status}-->
|
||||
<!-- <div class="pb-2">-->
|
||||
<!-- <Label label={contact.string.Status} />-->
|
||||
<!-- <div class="flex-row-stretch statusContainer">-->
|
||||
<!-- <div class="pr-2">-->
|
||||
<!-- <EmployeeStatusPresenter {employee} withTooltip={false} />-->
|
||||
<!-- </div>-->
|
||||
<!-- {#if editable}-->
|
||||
<!-- <div class="setStatusButton">-->
|
||||
<!-- <Button icon={Edit} title={contact.string.SetStatus} on:click={setStatus} />-->
|
||||
<!-- </div>-->
|
||||
<!-- {/if}-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!--{:else if editable}-->
|
||||
<!-- <!– svelte-ignore a11y-click-events-have-key-events –>-->
|
||||
<!-- <!– svelte-ignore a11y-no-static-element-interactions –>-->
|
||||
<!-- <div class="flex-row-stretch over-underline pb-2" on:click={setStatus}>-->
|
||||
<!-- <Label label={contact.string.SetStatus} />-->
|
||||
<!-- </div>-->
|
||||
<!--{/if}-->
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.statusContainer {
|
||||
.setStatusButton {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .setStatusButton {
|
||||
opacity: 1;
|
||||
}
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
max-width: 30rem;
|
||||
background: var(--theme-popup-color);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.separator {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: var(--global-ui-BorderColor);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
//.statusContainer {
|
||||
// .setStatusButton {
|
||||
// opacity: 0;
|
||||
// }
|
||||
//
|
||||
// &:hover .setStatusButton {
|
||||
// opacity: 1;
|
||||
// }
|
||||
//}
|
||||
</style>
|
||||
|
@ -34,6 +34,7 @@
|
||||
export let defaultName: IntlString | undefined = ui.string.NotSelected
|
||||
export let statusLabel: IntlString | undefined = undefined
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let customTooltip: LabelAndProps | undefined = undefined
|
||||
export let avatarSize: IconSize = 'x-small'
|
||||
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
|
||||
// export let element: HTMLElement | undefined = undefined
|
||||
@ -81,7 +82,7 @@
|
||||
|
||||
{#if value || shouldShowPlaceholder}
|
||||
<PersonContent
|
||||
showTooltip={getTooltip(tooltipLabels, personValue)}
|
||||
showTooltip={customTooltip ?? getTooltip(tooltipLabels, personValue)}
|
||||
value={personValue}
|
||||
{inline}
|
||||
{onEdit}
|
||||
|
@ -37,6 +37,7 @@ import core, {
|
||||
type Doc,
|
||||
type DocumentQuery,
|
||||
getCurrentAccount,
|
||||
groupByArray,
|
||||
type Hierarchy,
|
||||
type IdMap,
|
||||
matchQuery,
|
||||
@ -310,6 +311,10 @@ export const personIdByAccountId = derived(personAccountByIdStore, (vals) => {
|
||||
return new Map<Ref<PersonAccount>, Ref<Person>>(Array.from(vals.values()).map((it) => [it._id, it.person]))
|
||||
})
|
||||
|
||||
export const personAccountByPersonId = derived(personAccountByIdStore, (vals) => {
|
||||
return groupByArray(Array.from(vals.values()), (it) => it.person)
|
||||
})
|
||||
|
||||
export const statusByUserStore = writable<Map<Ref<Account>, UserStatus>>(new Map())
|
||||
|
||||
export const personByIdStore = derived([personAccountPersonByIdStore, employeeByIdStore], (vals) => {
|
||||
|
@ -31,7 +31,7 @@ import {
|
||||
import type { Asset, Metadata, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { IntlString, plugin } from '@hcengineering/platform'
|
||||
import { TemplateField, TemplateFieldCategory } from '@hcengineering/templates'
|
||||
import type { AnyComponent, ColorDefinition, ResolvedLocation, Location } from '@hcengineering/ui'
|
||||
import type { AnyComponent, ColorDefinition, ResolvedLocation, Location, ComponentExtensionId } from '@hcengineering/ui'
|
||||
import { Action, FilterMode, Viewlet } from '@hcengineering/view'
|
||||
|
||||
/**
|
||||
@ -301,7 +301,8 @@ export const contactPlugin = plugin(contactId, {
|
||||
Members: '' as IntlString,
|
||||
Contacts: '' as IntlString,
|
||||
Employees: '' as IntlString,
|
||||
Persons: '' as IntlString
|
||||
Persons: '' as IntlString,
|
||||
ViewProfile: '' as IntlString
|
||||
},
|
||||
viewlet: {
|
||||
TableMember: '' as Ref<Viewlet>,
|
||||
@ -332,6 +333,9 @@ export const contactPlugin = plugin(contactId, {
|
||||
},
|
||||
ids: {
|
||||
MentionCommonNotificationType: '' as Ref<Doc>
|
||||
},
|
||||
extension: {
|
||||
EmployeePopupActions: '' as ComponentExtensionId
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -127,7 +127,8 @@ function getDocumentLinkId (doc: Document): string {
|
||||
return `${slug}---${doc._id}`
|
||||
}
|
||||
|
||||
function parseDocumentId (shortLink: string): Ref<ControlledDocument> | undefined {
|
||||
function parseDocumentId (shortLink?: string): Ref<ControlledDocument> | undefined {
|
||||
if (shortLink === undefined) return undefined
|
||||
const parts = shortLink.split('---')
|
||||
if (parts.length > 1) {
|
||||
return parts[parts.length - 1] as Ref<ControlledDocument>
|
||||
|
@ -361,7 +361,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
width: 15rem;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
user-select: none;
|
||||
|
||||
&:not(.isDock) {
|
||||
|
@ -20,12 +20,12 @@
|
||||
import love from '../plugin'
|
||||
import VideoPopup from './VideoPopup.svelte'
|
||||
|
||||
export let widgetState: WidgetState
|
||||
export let widgetState: WidgetState | undefined
|
||||
|
||||
let room: Ref<TypeRoom> | undefined = undefined
|
||||
$: room = widgetState.data?.room
|
||||
$: room = widgetState?.data?.room
|
||||
|
||||
$: if (widgetState.data?.room === undefined) {
|
||||
$: if (widgetState?.data?.room === undefined) {
|
||||
closeWidget(love.ids.VideoWidget)
|
||||
}
|
||||
</script>
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let count: number = 0
|
||||
export let kind: 'primary' | 'secondary' | 'simple' = 'primary'
|
||||
export let kind: 'primary' | 'simple' = 'primary'
|
||||
export let size: 'xx-small' | 'x-small' | 'small' | 'medium' = 'small'
|
||||
|
||||
const maxNumber = 9
|
||||
@ -30,10 +30,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if kind === 'secondary'}
|
||||
<div class="notifyMarker {size} {kind}" />
|
||||
{/if}
|
||||
|
||||
{#if kind === 'simple'}
|
||||
<div class="notifyMarker {size} {kind}" />
|
||||
{/if}
|
||||
@ -53,10 +49,6 @@
|
||||
color: var(--global-on-accent-TextColor);
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background-color: var(--global-subtle-BackgroundColor);
|
||||
}
|
||||
|
||||
&.xx-small {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
|
@ -192,7 +192,7 @@
|
||||
|
||||
if (thread !== undefined) {
|
||||
const fn = await getResource(chunter.function.OpenThreadInSidebar)
|
||||
void fn(thread)
|
||||
void fn(thread, undefined, undefined, selectedMessageId)
|
||||
}
|
||||
|
||||
if (selectedMessageId !== undefined) {
|
||||
|
@ -107,6 +107,7 @@
|
||||
bind:this={list}
|
||||
bind:selection={listSelection}
|
||||
count={displayData.length}
|
||||
items={displayData}
|
||||
highlightIndex={displayData.findIndex(([context]) => context === selectedContext)}
|
||||
noScroll
|
||||
minHeight="5.625rem"
|
||||
|
@ -33,9 +33,7 @@
|
||||
preferences = res
|
||||
})
|
||||
|
||||
$: widgetId = $sidebarStore.widget
|
||||
$: widget = widgets.find((it) => it._id === widgetId)
|
||||
$: size = $sidebarStore.variant === SidebarVariant.MINI ? 'mini' : widget?.size
|
||||
$: size = $sidebarStore.variant === SidebarVariant.MINI ? 'mini' : undefined
|
||||
|
||||
function txListener (tx: Tx): void {
|
||||
if (tx._class === workbench.class.TxSidebarEvent) {
|
||||
@ -79,17 +77,5 @@
|
||||
min-width: 3.5rem !important;
|
||||
max-width: 3.5rem !important;
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
width: 10rem !important;
|
||||
min-width: 10rem !important;
|
||||
max-width: 10rem !important;
|
||||
}
|
||||
|
||||
&.size-medium {
|
||||
width: 20rem !important;
|
||||
min-width: 20rem !important;
|
||||
max-width: 20rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -71,7 +71,6 @@ export interface Widget extends Doc {
|
||||
label: IntlString
|
||||
icon: Asset
|
||||
type: WidgetType
|
||||
size?: 'small' | 'medium'
|
||||
|
||||
component: AnyComponent
|
||||
tabComponent?: AnyComponent
|
||||
|
@ -76,9 +76,7 @@ export class DocumentsPage extends CalendarPage {
|
||||
|
||||
async changeSpaceInCreateDocumentForm (space: string): Promise<void> {
|
||||
await this.changeSpaceButton.click()
|
||||
await this.page
|
||||
.locator(`div.list-container.flex-col.flex-grow.svelte-15na0wa >> text=${space}`)
|
||||
.click({ force: true })
|
||||
await this.page.locator(`div.selectPopup >> div.list-container >> text=${space}`).click({ force: true })
|
||||
}
|
||||
|
||||
async createTemplate (title: string, description: string, category: string, spaceName: string): Promise<void> {
|
||||
|
Loading…
Reference in New Issue
Block a user