mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
TSK-997 В Activity Feed таланта писать событие об отправленном письме (#2917)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
b699096e22
commit
cab96e795d
@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { ActivityFilter, DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import type { ActivityFilter, DisplayTx, ExtraActivityComponent, TxViewlet } from '@hcengineering/activity'
|
||||
import activity from './plugin'
|
||||
import core, { Class, Doc, DocumentQuery, DOMAIN_MODEL, Ref, Tx } from '@hcengineering/core'
|
||||
import { Builder, Model } from '@hcengineering/model'
|
||||
import { TDoc } from '@hcengineering/model-core'
|
||||
import { Builder, Mixin, Model } from '@hcengineering/model'
|
||||
import { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
|
||||
@ -42,8 +42,13 @@ export class TActivityFilter extends TDoc implements ActivityFilter {
|
||||
filter!: Resource<(tx: DisplayTx, _class?: Ref<Doc>) => boolean>
|
||||
}
|
||||
|
||||
@Mixin(activity.mixin.ExtraActivityComponent, core.class.Class)
|
||||
export class TExtraActivityComponent extends TClass implements ExtraActivityComponent {
|
||||
component!: AnyComponent
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TTxViewlet, TActivityFilter)
|
||||
builder.createModel(TTxViewlet, TActivityFilter, TExtraActivityComponent)
|
||||
|
||||
builder.createDoc(activity.class.ActivityFilter, core.space.Model, {
|
||||
label: activity.string.Attributes,
|
||||
|
@ -36,6 +36,7 @@
|
||||
"@hcengineering/core": "^0.6.21",
|
||||
"@hcengineering/ui": "^0.6.3",
|
||||
"@hcengineering/platform": "^0.6.8",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/contact": "^0.6.11",
|
||||
"@hcengineering/notification": "^0.6.8",
|
||||
"@hcengineering/contact-resources": "^0.6.0",
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
Status,
|
||||
ContactsTab
|
||||
} from '@hcengineering/contact'
|
||||
import activity from '@hcengineering/activity'
|
||||
import { Class, DateRangeMode, Domain, DOMAIN_MODEL, IndexKind, Ref, Timestamp } from '@hcengineering/core'
|
||||
import {
|
||||
Builder,
|
||||
@ -722,6 +723,10 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
contact.templateField.ContactLastName
|
||||
)
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, activity.mixin.ExtraActivityComponent, {
|
||||
component: contact.component.ActivityChannelMessage
|
||||
})
|
||||
}
|
||||
|
||||
export { contactOperation } from './migration'
|
||||
|
@ -49,7 +49,8 @@ export default mergeIds(contactId, contact, {
|
||||
CreateEmployee: '' as AnyComponent,
|
||||
AccountArrayEditor: '' as AnyComponent,
|
||||
ChannelFilter: '' as AnyComponent,
|
||||
MergeEmployee: '' as AnyComponent
|
||||
MergeEmployee: '' as AnyComponent,
|
||||
ActivityChannelMessage: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Persons: '' as IntlString,
|
||||
|
@ -150,12 +150,26 @@ export function createModel (builder: Builder): void {
|
||||
gmail.integrationType.Gmail
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: gmail.class.Message,
|
||||
icon: contact.icon.Email,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
label: gmail.string.HaveWrittenEmail,
|
||||
labelComponent: gmail.activity.TxWriteMessage,
|
||||
display: 'inline'
|
||||
},
|
||||
gmail.ids.TxSharedCreate
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
activity.class.TxViewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass: gmail.class.SharedMessages,
|
||||
icon: contact.icon.Telegram,
|
||||
icon: contact.icon.Email,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
component: gmail.activity.TxSharedCreate,
|
||||
label: gmail.string.SharedMessages,
|
||||
|
@ -36,7 +36,6 @@ export default mergeIds(gmailId, gmail, {
|
||||
Message: '' as IntlString,
|
||||
Messages: '' as IntlString,
|
||||
Incoming: '' as IntlString,
|
||||
Email: '' as IntlString,
|
||||
Status: '' as IntlString,
|
||||
EmailPlaceholder: '' as IntlString
|
||||
},
|
||||
@ -44,7 +43,8 @@ export default mergeIds(gmailId, gmail, {
|
||||
TxSharedCreate: '' as Ref<TxViewlet>
|
||||
},
|
||||
activity: {
|
||||
TxSharedCreate: '' as AnyComponent
|
||||
TxSharedCreate: '' as AnyComponent,
|
||||
TxWriteMessage: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
HasEmail: '' as Resource<(doc?: Doc | Doc[] | undefined) => Promise<boolean>>
|
||||
|
@ -139,28 +139,20 @@
|
||||
<div class="h-2 min-h-2 max-h-2" />
|
||||
</svelte:fragment>
|
||||
|
||||
{#if withoutActivity}
|
||||
{#if $deviceInfo.isMobile}
|
||||
<div class="popupPanel-body__mobile-content clear-mins">
|
||||
<slot />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="popupPanel-body__main-content py-8 clear-mins" class:max={useMaxWidth}>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
{:else if $deviceInfo.isMobile}
|
||||
{#if $deviceInfo.isMobile}
|
||||
<div class="popupPanel-body__mobile-content clear-mins" class:max={useMaxWidth}>
|
||||
<Component is={activity.component.Activity} props={{ object, integrate: true, showCommenInput: !withoutInput }}>
|
||||
<slot />
|
||||
</Component>
|
||||
<slot />
|
||||
{#if !withoutActivity}
|
||||
<Component is={activity.component.Activity} props={{ object, showCommenInput: !withoutInput }} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-8 clear-mins" class:max={useMaxWidth}>
|
||||
<Component is={activity.component.Activity} props={{ object, integrate: true, showCommenInput: !withoutInput }}>
|
||||
<slot />
|
||||
</Component>
|
||||
<slot />
|
||||
{#if !withoutActivity}
|
||||
<Component is={activity.component.Activity} props={{ object, showCommenInput: !withoutInput }} />
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
{/if}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DisplayTx } from '@hcengineering/activity'
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
AttachedDoc,
|
||||
@ -43,37 +44,6 @@ function isEqualOps (op1: any, op2: any): boolean {
|
||||
return o1 === o2
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction being displayed.
|
||||
* @public
|
||||
*/
|
||||
export interface DisplayTx {
|
||||
// Source tx
|
||||
tx: TxCUD<Doc>
|
||||
|
||||
// A set of collapsed transactions.
|
||||
txes: DisplayTx[]
|
||||
txDocIds?: Set<Ref<Doc>>
|
||||
|
||||
// type check for createTx
|
||||
createTx?: TxCreateDoc<Doc>
|
||||
|
||||
// Type check for updateTx
|
||||
updateTx?: TxUpdateDoc<Doc>
|
||||
|
||||
// Type check for updateTx
|
||||
mixinTx?: TxMixin<Doc, Doc>
|
||||
|
||||
// Document in case it is required.
|
||||
doc?: Doc
|
||||
|
||||
updated: boolean
|
||||
mixin: boolean
|
||||
removed: boolean
|
||||
|
||||
collectionAttribute?: Attribute<Collection<AttachedDoc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
@ -13,35 +13,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import activity, { ActivityFilter, TxViewlet } from '@hcengineering/activity'
|
||||
import activity, { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import core, { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import notification, { LastView } from '@hcengineering/notification'
|
||||
import { getResource, IntlString } from '@hcengineering/platform'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
ActionIcon,
|
||||
Component,
|
||||
eventToHTMLElement,
|
||||
Grid,
|
||||
Icon,
|
||||
IconActivity,
|
||||
Label,
|
||||
Scroller,
|
||||
showPopup,
|
||||
Spinner
|
||||
} from '@hcengineering/ui'
|
||||
import { Component, Grid, Label, Spinner } from '@hcengineering/ui'
|
||||
import { Writable } from 'svelte/store'
|
||||
import { ActivityKey, activityKey, DisplayTx, newActivity } from '../activity'
|
||||
import activityPlg from '../plugin'
|
||||
import { ActivityKey, activityKey, newActivity } from '../activity'
|
||||
import { filterCollectionTxes } from '../utils'
|
||||
import FilterPopup from './FilterPopup.svelte'
|
||||
import IconClose from './icons/Close.svelte'
|
||||
import IconFilter from './icons/Filter.svelte'
|
||||
import ActivityFilter from './ActivityFilter.svelte'
|
||||
import TxView from './TxView.svelte'
|
||||
|
||||
export let object: Doc
|
||||
export let integrate: boolean = false
|
||||
export let showCommenInput: boolean = true
|
||||
export let transparent: boolean = false
|
||||
|
||||
@ -50,23 +35,6 @@
|
||||
const client = getClient()
|
||||
const attrs = client.getHierarchy().getAllAttributes(object._class)
|
||||
|
||||
let labels: IntlString[] = []
|
||||
const filters: ActivityFilter[] = []
|
||||
const saved = localStorage.getItem('activity-filter')
|
||||
let selectedFilter: Ref<Doc>[] | 'All' =
|
||||
saved !== null && saved !== undefined ? (JSON.parse(saved) as Ref<Doc>[] | 'All') : 'All'
|
||||
$: localStorage.setItem('activity-filter', JSON.stringify(selectedFilter))
|
||||
client.findAll(activity.class.ActivityFilter, {}).then((res) => {
|
||||
res.map((it) => filters.push(it))
|
||||
if (saved !== null && saved !== undefined) {
|
||||
const temp: Ref<Doc>[] | 'All' = JSON.parse(saved)
|
||||
if (temp !== 'All' && Array.isArray(temp)) {
|
||||
selectedFilter = temp.filter((it) => filters.findIndex((f) => it === f._id) > -1)
|
||||
if ((selectedFilter as Ref<Doc>[]).length === 0) selectedFilter = 'All'
|
||||
} else selectedFilter = 'All'
|
||||
}
|
||||
})
|
||||
|
||||
const activityQuery = newActivity(client, attrs)
|
||||
getResource(notification.function.GetNotificationClient).then((res) => {
|
||||
lastViews = res().getLastViews()
|
||||
@ -97,6 +65,7 @@
|
||||
activityQuery.update(
|
||||
object,
|
||||
(result) => {
|
||||
console.log('query txes update')
|
||||
txes = filterCollectionTxes(result)
|
||||
|
||||
if (txes.length > 0) {
|
||||
@ -110,6 +79,8 @@
|
||||
|
||||
$: if (editableMap) updateTxes(object)
|
||||
|
||||
let filtered: DisplayTx[] = []
|
||||
|
||||
$: newTxPos = newTx(filtered, $lastViews)
|
||||
|
||||
function newTx (txes: DisplayTx[], lastViews: LastView | undefined): number {
|
||||
@ -121,136 +92,35 @@
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
const handleOptions = (ev: MouseEvent) => {
|
||||
showPopup(
|
||||
FilterPopup,
|
||||
{ selectedFilter, filters },
|
||||
eventToHTMLElement(ev),
|
||||
() => {},
|
||||
(res) => {
|
||||
if (res === undefined) return
|
||||
if (res.action === 'select') selectedFilter = res.value as Ref<Doc>[] | 'All'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let filterActions: ((tx: DisplayTx, _class?: Ref<Doc>) => boolean)[] = [] // Enabled filters
|
||||
const updateFiltered = () => (filtered = txes.filter((it) => filterActions.some((f) => f(it, object._class))))
|
||||
async function updateFilterActions (fls: ActivityFilter[], selected: Ref<Doc>[] | 'All'): Promise<void> {
|
||||
if (selected === 'All' || !Array.isArray(selected)) filterActions = [() => true]
|
||||
else {
|
||||
const tf = fls.filter((filter) => (selected as Ref<Doc>[]).includes(filter._id))
|
||||
filterActions = []
|
||||
labels = []
|
||||
tf.forEach((filter) => {
|
||||
labels.push(filter.label)
|
||||
getResource(filter.filter).then((res) => filterActions.push(res))
|
||||
})
|
||||
}
|
||||
setTimeout(() => updateFiltered(), 0)
|
||||
}
|
||||
$: updateFilterActions(filters, selectedFilter)
|
||||
$: filtered = txes.filter((it) => filterActions.some((f) => f(it, object._class)))
|
||||
</script>
|
||||
|
||||
{#if !integrate || transparent}
|
||||
<!-- OLD TRANSPARENT -->
|
||||
{#if transparent !== undefined && !transparent}
|
||||
<div class="ac-header short mirror-tool highlight">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="flex-center icon"><IconActivity size={'small'} /></div>
|
||||
<span class="ac-header__title flex-row-center">
|
||||
<Label label={activity.string.Activity} />
|
||||
{#if loading}
|
||||
<div class="ml-1">
|
||||
<Spinner size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-col flex-grow min-h-0" class:background-accent-bg-color={!transparent}>
|
||||
<Scroller>
|
||||
<div class="p-10 select-text" id={activity.string.Activity}>
|
||||
{#if filtered}
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
{#each filtered as tx, i}
|
||||
<TxView
|
||||
{tx}
|
||||
{viewlets}
|
||||
isNew={newTxPos >= i && newTxPos !== -1}
|
||||
isNextNew={newTxPos > i && newTxPos !== -1}
|
||||
/>
|
||||
{/each}
|
||||
</Grid>
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object }} />
|
||||
<div class="antiSection-header high mt-9" class:invisible={transparent}>
|
||||
<span class="antiSection-header__title flex-row-center">
|
||||
<Label label={activity.string.Activity} />
|
||||
{#if loading}
|
||||
<div class="ml-1">
|
||||
<Spinner size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<!-- MODERN -->
|
||||
<slot />
|
||||
<!-- <div class="antiDivider" style:margin={'1rem -1.5rem'} /> -->
|
||||
<div class="antiSection-header high mt-9">
|
||||
<span class="antiSection-header__title flex-row-center">
|
||||
<Label label={activity.string.Activity} />
|
||||
{#if loading}
|
||||
<div class="ml-1">
|
||||
<Spinner size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
{#if selectedFilter === 'All'}
|
||||
<div class="antiSection-header__tag highlight">
|
||||
<Label label={activityPlg.string.All} />
|
||||
</div>
|
||||
{:else}
|
||||
{#each labels as label}
|
||||
<div class="antiSection-header__tag overflow-label">
|
||||
<Label {label} />
|
||||
<div class="tag-icon">
|
||||
<Icon icon={IconClose} size={'small'} />
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<ActivityFilter {txes} {object} on:update={(e) => (filtered = e.detail)} />
|
||||
</div>
|
||||
<div class="p-activity select-text" id={activity.string.Activity}>
|
||||
{#if filtered}
|
||||
<Grid column={1} rowGap={0.75}>
|
||||
{#each filtered as tx, i}
|
||||
<TxView {tx} {viewlets} isNew={newTxPos >= i && newTxPos !== -1} isNextNew={newTxPos > i && newTxPos !== -1} />
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="w-4 min-w-4 max-w-4" />
|
||||
<ActionIcon icon={IconFilter} size={'medium'} action={handleOptions} />
|
||||
</div>
|
||||
<div class="p-activity select-text" id={activity.string.Activity}>
|
||||
{#if filtered}
|
||||
<Grid column={1} rowGap={0.75}>
|
||||
{#each filtered as tx, i}
|
||||
<TxView
|
||||
{tx}
|
||||
{viewlets}
|
||||
isNew={newTxPos >= i && newTxPos !== -1}
|
||||
isNextNew={newTxPos > i && newTxPos !== -1}
|
||||
/>
|
||||
{/each}
|
||||
</Grid>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object }} />
|
||||
</div>
|
||||
</Grid>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showCommenInput}
|
||||
<div class="ref-input">
|
||||
<Component is={chunter.component.CommentInput} props={{ object }} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.icon {
|
||||
margin-left: 1rem;
|
||||
height: 2rem;
|
||||
color: var(--caption-color);
|
||||
}
|
||||
.ref-input {
|
||||
flex-shrink: 0;
|
||||
margin-top: 1.75rem;
|
||||
@ -259,6 +129,9 @@
|
||||
.p-activity {
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global(.grid .msgactivity-container.showIcon:last-child::after) {
|
||||
content: none;
|
||||
|
130
plugins/activity-resources/src/components/ActivityFilter.svelte
Normal file
130
plugins/activity-resources/src/components/ActivityFilter.svelte
Normal file
@ -0,0 +1,130 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 activity, { ActivityFilter, DisplayTx } from '@hcengineering/activity'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ActionIcon, AnyComponent, Icon, Label, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import activityPlg from '../plugin'
|
||||
import FilterPopup from './FilterPopup.svelte'
|
||||
import IconClose from './icons/Close.svelte'
|
||||
import IconFilter from './icons/Filter.svelte'
|
||||
|
||||
export let txes: DisplayTx[]
|
||||
export let object: Doc
|
||||
|
||||
let filtered: DisplayTx[]
|
||||
const client = getClient()
|
||||
let filters: ActivityFilter[] = []
|
||||
const saved = localStorage.getItem('activity-filter')
|
||||
let selectedFilter: Ref<Doc>[] | 'All' =
|
||||
saved !== null && saved !== undefined ? (JSON.parse(saved) as Ref<Doc>[] | 'All') : 'All'
|
||||
$: localStorage.setItem('activity-filter', JSON.stringify(selectedFilter))
|
||||
client.findAll(activity.class.ActivityFilter, {}).then((res) => {
|
||||
filters = res
|
||||
if (saved !== null && saved !== undefined) {
|
||||
const temp: Ref<Doc>[] | 'All' = JSON.parse(saved)
|
||||
if (temp !== 'All' && Array.isArray(temp)) {
|
||||
selectedFilter = temp.filter((it) => filters.findIndex((f) => it === f._id) > -1)
|
||||
if ((selectedFilter as Ref<Doc>[]).length === 0) {
|
||||
selectedFilter = 'All'
|
||||
}
|
||||
} else {
|
||||
selectedFilter = 'All'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function getAdditionalComponent (_class: Ref<Class<Doc>>): AnyComponent | undefined {
|
||||
const hierarchy = client.getHierarchy()
|
||||
const mixin = hierarchy.classHierarchyMixin(_class, activity.mixin.ExtraActivityComponent)
|
||||
if (mixin !== undefined) {
|
||||
return mixin.component
|
||||
}
|
||||
}
|
||||
|
||||
let extraComponent = getAdditionalComponent(object._class)
|
||||
$: extraComponent = getAdditionalComponent(object._class)
|
||||
|
||||
const handleOptions = (ev: MouseEvent) => {
|
||||
showPopup(
|
||||
FilterPopup,
|
||||
{ selectedFilter, filters },
|
||||
eventToHTMLElement(ev),
|
||||
() => {},
|
||||
(res) => {
|
||||
if (res === undefined) return
|
||||
if (res.action === 'select') selectedFilter = res.value as Ref<Doc>[] | 'All'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let labels: IntlString[] = []
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function updateFilterActions (
|
||||
txes: DisplayTx[],
|
||||
filters: ActivityFilter[],
|
||||
selected: Ref<Doc>[] | 'All'
|
||||
): Promise<void> {
|
||||
if (selected === 'All') {
|
||||
filtered = txes
|
||||
if (extraComponent === undefined) {
|
||||
dispatch('update', filtered)
|
||||
}
|
||||
} else {
|
||||
const selectedFilters = filters.filter((filter) => selected.includes(filter._id))
|
||||
const filterActions: ((tx: DisplayTx, _class?: Ref<Doc>) => boolean)[] = []
|
||||
labels = []
|
||||
for (const filter of selectedFilters) {
|
||||
labels.push(filter.label)
|
||||
const fltr = await getResource(filter.filter)
|
||||
filterActions.push(fltr)
|
||||
}
|
||||
filtered = txes.filter((it) => filterActions.some((f) => f(it, object._class)))
|
||||
if (extraComponent === undefined) {
|
||||
dispatch('update', filtered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: updateFilterActions(txes, filters, selectedFilter)
|
||||
</script>
|
||||
|
||||
{#if selectedFilter === 'All'}
|
||||
<div class="antiSection-header__tag highlight">
|
||||
<Label label={activityPlg.string.All} />
|
||||
</div>
|
||||
{:else}
|
||||
{#each labels as label}
|
||||
<div class="antiSection-header__tag overflow-label">
|
||||
<Label {label} />
|
||||
<div class="tag-icon">
|
||||
<Icon icon={IconClose} size={'small'} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="w-4 min-w-4 max-w-4" />
|
||||
<ActionIcon icon={IconFilter} size={'medium'} action={handleOptions} />
|
||||
{#if extraComponent}
|
||||
{#await getResource(extraComponent) then comp}
|
||||
{#if comp}
|
||||
<svelte:component this={comp} {filtered} {object} on:update />
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { TxViewlet } from '@hcengineering/activity'
|
||||
import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, Doc, getCurrentAccount, Ref, Class } from '@hcengineering/core'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
@ -34,7 +34,7 @@
|
||||
import attachment from '@hcengineering/attachment'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { Menu, ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import { ActivityKey, DisplayTx } from '../activity'
|
||||
import { ActivityKey } from '../activity'
|
||||
import activity from '../plugin'
|
||||
import { getValue, TxDisplayViewlet, updateViewlet } from '../utils'
|
||||
import TxViewTx from './TxViewTx.svelte'
|
||||
@ -206,9 +206,9 @@
|
||||
<span class="lower">
|
||||
<Label label={viewlet.label} params={viewlet.labelParams ?? {}} />
|
||||
</span>
|
||||
{#if viewlet.labelComponent}
|
||||
<Component is={viewlet.labelComponent} {props} />
|
||||
{/if}
|
||||
{/if}
|
||||
{#if viewlet && viewlet.labelComponent}
|
||||
<Component is={viewlet.labelComponent} {props} />
|
||||
{/if}
|
||||
|
||||
{#if viewlet === undefined && model.length > 0 && tx.updateTx}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import core, { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { Component, IconAdd, IconDelete } from '@hcengineering/ui'
|
||||
import { DisplayTx } from '../activity'
|
||||
import { getDTxProps, TxDisplayViewlet } from '../utils'
|
||||
import { DisplayTx } from '@hcengineering/activity'
|
||||
|
||||
export let tx: DisplayTx
|
||||
export let viewlet: TxDisplayViewlet
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { TxViewlet } from '@hcengineering/activity'
|
||||
import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import core, {
|
||||
AttachedDoc,
|
||||
Class,
|
||||
@ -17,7 +17,7 @@ import { Asset, IntlString, translate } from '@hcengineering/platform'
|
||||
import { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { buildModel, getObjectPresenter } from '@hcengineering/view-resources'
|
||||
import { ActivityKey, activityKey, DisplayTx } from './activity'
|
||||
import { ActivityKey, activityKey } from './activity'
|
||||
import activity from './plugin'
|
||||
|
||||
const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
|
||||
|
@ -100,6 +100,13 @@ export interface ActivityFilter extends Doc {
|
||||
filter: Resource<(tx: DisplayTx, _class?: Ref<Doc>) => boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ExtraActivityComponent extends Class<Doc> {
|
||||
component: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -121,6 +128,9 @@ export default plugin(activityId, {
|
||||
From: '' as IntlString,
|
||||
Removed: '' as IntlString
|
||||
},
|
||||
mixin: {
|
||||
ExtraActivityComponent: '' as Ref<Class<ExtraActivityComponent>>
|
||||
},
|
||||
class: {
|
||||
TxViewlet: '' as Ref<Class<TxViewlet>>,
|
||||
ActivityFilter: '' as Ref<Class<ActivityFilter>>
|
||||
|
@ -45,6 +45,7 @@
|
||||
"@hcengineering/contact-resources": "^0.6.0",
|
||||
"@hcengineering/notification": "^0.6.8",
|
||||
"@hcengineering/notification-resources": "^0.6.0",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/attachment": "^0.6.1",
|
||||
"@hcengineering/attachment-resources": "^0.6.0",
|
||||
"@hcengineering/view-resources": "^0.6.0",
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { DisplayTx } from '@hcengineering/activity'
|
||||
import chunter, {
|
||||
Backlink,
|
||||
Channel,
|
||||
@ -22,16 +23,12 @@ import chunter, {
|
||||
Message,
|
||||
ThreadMessage
|
||||
} from '@hcengineering/chunter'
|
||||
import core, { Data, Doc, DocumentQuery, getCurrentAccount, Ref, RelatedDocument, Space } from '@hcengineering/core'
|
||||
import core, { Data, Doc, DocumentQuery, Ref, RelatedDocument, Space, getCurrentAccount } from '@hcengineering/core'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { IntlString, Resources, translate } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
import { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { MessageBox, getClient } from '@hcengineering/presentation'
|
||||
import { location, navigate, showPopup } from '@hcengineering/ui'
|
||||
import TxBacklinkCreate from './components/activity/TxBacklinkCreate.svelte'
|
||||
import TxBacklinkReference from './components/activity/TxBacklinkReference.svelte'
|
||||
import TxCommentCreate from './components/activity/TxCommentCreate.svelte'
|
||||
import TxMessageCreate from './components/activity/TxMessageCreate.svelte'
|
||||
import ChannelHeader from './components/ChannelHeader.svelte'
|
||||
import ChannelPresenter from './components/ChannelPresenter.svelte'
|
||||
import ChannelView from './components/ChannelView.svelte'
|
||||
@ -41,25 +38,28 @@ import CommentInput from './components/CommentInput.svelte'
|
||||
import CommentPopup from './components/CommentPopup.svelte'
|
||||
import CommentPresenter from './components/CommentPresenter.svelte'
|
||||
import CommentsPresenter from './components/CommentsPresenter.svelte'
|
||||
import MessagePresenter from './components/MessagePresenter.svelte'
|
||||
import ThreadParentPresenter from './components/ThreadParentPresenter.svelte'
|
||||
import ThreadViewPanel from './components/ThreadViewPanel.svelte'
|
||||
import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte'
|
||||
import CreateChannel from './components/CreateChannel.svelte'
|
||||
import CreateDirectMessage from './components/CreateDirectMessage.svelte'
|
||||
import DirectMessagePresenter from './components/DirectMessagePresenter.svelte'
|
||||
import DmHeader from './components/DmHeader.svelte'
|
||||
import DmPresenter from './components/DmPresenter.svelte'
|
||||
import EditChannel from './components/EditChannel.svelte'
|
||||
import DirectMessagePresenter from './components/DirectMessagePresenter.svelte'
|
||||
import MessagePresenter from './components/MessagePresenter.svelte'
|
||||
import SavedMessages from './components/SavedMessages.svelte'
|
||||
import Threads from './components/Threads.svelte'
|
||||
import ThreadParentPresenter from './components/ThreadParentPresenter.svelte'
|
||||
import ThreadView from './components/ThreadView.svelte'
|
||||
import ThreadViewPanel from './components/ThreadViewPanel.svelte'
|
||||
import Threads from './components/Threads.svelte'
|
||||
import TxBacklinkCreate from './components/activity/TxBacklinkCreate.svelte'
|
||||
import TxBacklinkReference from './components/activity/TxBacklinkReference.svelte'
|
||||
import TxCommentCreate from './components/activity/TxCommentCreate.svelte'
|
||||
import TxMessageCreate from './components/activity/TxMessageCreate.svelte'
|
||||
|
||||
import { get, writable } from 'svelte/store'
|
||||
import { DisplayTx } from '../../activity/lib'
|
||||
import { updateBacklinksList } from './backlinks'
|
||||
import { getDmName, getTitle, getLink, resolveLocation } from './utils'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { get, writable } from 'svelte/store'
|
||||
import { updateBacklinksList } from './backlinks'
|
||||
import { getDmName, getLink, getTitle, resolveLocation } from './utils'
|
||||
|
||||
export { default as Header } from './components/Header.svelte'
|
||||
export { classIcon } from './utils'
|
||||
|
@ -37,6 +37,8 @@
|
||||
"@hcengineering/contact": "^0.6.11",
|
||||
"@hcengineering/ui": "^0.6.3",
|
||||
"@hcengineering/setting": "^0.6.3",
|
||||
"@hcengineering/activity": "^0.6.0",
|
||||
"@hcengineering/activity-resources": "^0.6.1",
|
||||
"@hcengineering/presentation": "^0.6.2",
|
||||
"@hcengineering/notification": "^0.6.8",
|
||||
"@hcengineering/core": "^0.6.21",
|
||||
|
@ -0,0 +1,64 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { DisplayTx } from '@hcengineering/activity'
|
||||
import core, { AttachedDoc, Doc, TxCUD, TxCollectionCUD, TxProcessor } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import contact from '../../plugin'
|
||||
import { Channel, Contact } from '@hcengineering/contact'
|
||||
import { newDisplayTx } from '@hcengineering/activity-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let object: Contact
|
||||
export let filtered: DisplayTx[]
|
||||
|
||||
let channels: Channel[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(
|
||||
contact.class.Channel,
|
||||
{
|
||||
attachedTo: object._id,
|
||||
provider: contact.channelProvider.Email
|
||||
},
|
||||
(res) => {
|
||||
channels = res
|
||||
}
|
||||
)
|
||||
|
||||
const txQuery = createQuery()
|
||||
$: txQuery.query(
|
||||
core.class.TxCollectionCUD,
|
||||
{
|
||||
objectId: { $in: channels.map((p) => p._id) },
|
||||
'tx._class': core.class.TxCreateDoc,
|
||||
'tx.attributes.incoming': false
|
||||
},
|
||||
(res) => {
|
||||
const newTxes = createDisplayTxes(res)
|
||||
const result = filtered.concat(newTxes).sort((a, b) => a.tx.modifiedOn - b.tx.modifiedOn)
|
||||
dispatch('update', result)
|
||||
}
|
||||
)
|
||||
|
||||
function createDisplayTxes (txes: TxCollectionCUD<Doc, AttachedDoc>[]): DisplayTx[] {
|
||||
return txes.map((p) => newDisplayTx(TxProcessor.extractTx(p) as TxCUD<Doc>, hierarchy))
|
||||
}
|
||||
</script>
|
@ -17,11 +17,12 @@
|
||||
import { Channel, Contact, Employee, getGravatarUrl, getName } from '@hcengineering/contact'
|
||||
import { Class, Client, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import { getResource, IntlString, Resources } from '@hcengineering/platform'
|
||||
import { getClient, getFileUrl, MessageBox, ObjectSearchResult } from '@hcengineering/presentation'
|
||||
import { IntlString, Resources, getResource } from '@hcengineering/platform'
|
||||
import { MessageBox, ObjectSearchResult, getClient, getFileUrl } from '@hcengineering/presentation'
|
||||
import { AnyComponent, AnySvelteComponent, showPopup } from '@hcengineering/ui'
|
||||
import AccountArrayEditor from './components/AccountArrayEditor.svelte'
|
||||
import AccountBox from './components/AccountBox.svelte'
|
||||
import AssigneeBox from './components/AssigneeBox.svelte'
|
||||
import Avatar from './components/Avatar.svelte'
|
||||
import ChannelFilter from './components/ChannelFilter.svelte'
|
||||
import Channels from './components/Channels.svelte'
|
||||
@ -29,6 +30,7 @@ import ChannelsDropdown from './components/ChannelsDropdown.svelte'
|
||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||
import ChannelsView from './components/ChannelsView.svelte'
|
||||
import CombineAvatars from './components/CombineAvatars.svelte'
|
||||
import ContactArrayEditor from './components/ContactArrayEditor.svelte'
|
||||
import ContactPresenter from './components/ContactPresenter.svelte'
|
||||
import ContactRefPresenter from './components/ContactRefPresenter.svelte'
|
||||
@ -37,19 +39,19 @@ import ContactsTabs from './components/ContactsTabs.svelte'
|
||||
import CreateEmployee from './components/CreateEmployee.svelte'
|
||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||
import CreatePerson from './components/CreatePerson.svelte'
|
||||
import EditableAvatar from './components/EditableAvatar.svelte'
|
||||
import EditEmployee from './components/EditEmployee.svelte'
|
||||
import EditMember from './components/EditMember.svelte'
|
||||
import EditOrganization from './components/EditOrganization.svelte'
|
||||
import EditPerson from './components/EditPerson.svelte'
|
||||
import EditableAvatar from './components/EditableAvatar.svelte'
|
||||
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
|
||||
import EmployeeAccountRefPresenter from './components/EmployeeAccountRefPresenter.svelte'
|
||||
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
|
||||
import EmployeeBox from './components/EmployeeBox.svelte'
|
||||
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
|
||||
import EmployeeEditor from './components/EmployeeEditor.svelte'
|
||||
import EmployeePresenter from './components/EmployeePresenter.svelte'
|
||||
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
||||
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
|
||||
import MemberPresenter from './components/MemberPresenter.svelte'
|
||||
import Members from './components/Members.svelte'
|
||||
import MembersPresenter from './components/MembersPresenter.svelte'
|
||||
@ -60,14 +62,13 @@ import PersonEditor from './components/PersonEditor.svelte'
|
||||
import PersonPresenter from './components/PersonPresenter.svelte'
|
||||
import PersonRefPresenter from './components/PersonRefPresenter.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
import UserInfo from './components/UserInfo.svelte'
|
||||
import UserBox from './components/UserBox.svelte'
|
||||
import AssigneeBox from './components/AssigneeBox.svelte'
|
||||
import UsersPopup from './components/UsersPopup.svelte'
|
||||
import EmployeeBox from './components/EmployeeBox.svelte'
|
||||
import UserBoxList from './components/UserBoxList.svelte'
|
||||
import SpaceMembers from './components/SpaceMembers.svelte'
|
||||
import CombineAvatars from './components/CombineAvatars.svelte'
|
||||
import UserBox from './components/UserBox.svelte'
|
||||
import UserBoxList from './components/UserBoxList.svelte'
|
||||
import UserInfo from './components/UserInfo.svelte'
|
||||
import UsersPopup from './components/UsersPopup.svelte'
|
||||
import ActivityChannelMessage from './components/activity/ActivityChannelMessage.svelte'
|
||||
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
|
||||
import IconMembers from './components/icons/Members.svelte'
|
||||
|
||||
import contact from './plugin'
|
||||
@ -258,7 +259,8 @@ export default async (): Promise<Resources> => ({
|
||||
ChannelFilter,
|
||||
MergeEmployee,
|
||||
Avatar,
|
||||
UserBoxList
|
||||
UserBoxList,
|
||||
ActivityChannelMessage
|
||||
},
|
||||
completion: {
|
||||
EmployeeQuery: async (
|
||||
|
@ -32,6 +32,7 @@
|
||||
"EmailPlaceholder": "john.appleseed@apple.com",
|
||||
"WriteEmail": "Write Email",
|
||||
"Shared": "Shared",
|
||||
"AvailableTo": "Available to"
|
||||
"AvailableTo": "Available to",
|
||||
"HaveWrittenEmail": "have written an"
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
"EmailPlaceholder": "john.appleseed@apple.com",
|
||||
"WriteEmail": "Написать Email",
|
||||
"Shared": "Публичный",
|
||||
"AvailableTo": "Доступен для"
|
||||
"AvailableTo": "Доступен для",
|
||||
"HaveWrittenEmail": "написал(а)"
|
||||
}
|
||||
}
|
@ -14,13 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, {
|
||||
Channel,
|
||||
Contact,
|
||||
Employee,
|
||||
EmployeeAccount,
|
||||
getName as getContactName
|
||||
} from '@hcengineering/contact'
|
||||
import contact, { Channel, Contact, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, Ref, SortingOrder, toIdMap } from '@hcengineering/core'
|
||||
import { Message, SharedMessage } from '@hcengineering/gmail'
|
||||
@ -28,17 +22,15 @@
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import plugin, { Button, Icon, IconShare, Label, Scroller } from '@hcengineering/ui'
|
||||
import gmail from '../plugin'
|
||||
import IconInbox from './icons/Inbox.svelte'
|
||||
import { convertMessages } from '../utils'
|
||||
import Messages from './Messages.svelte'
|
||||
import IconInbox from './icons/Inbox.svelte'
|
||||
|
||||
export let object: Contact
|
||||
export let channel: Channel
|
||||
export let newMessage: boolean
|
||||
export let enabled: boolean
|
||||
|
||||
const EMAIL_REGEX =
|
||||
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
|
||||
|
||||
let messages: Message[] = []
|
||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||
let selected: Set<Ref<SharedMessage>> = new Set<Ref<SharedMessage>>()
|
||||
@ -76,7 +68,7 @@
|
||||
object._class,
|
||||
'gmailSharedMessages',
|
||||
{
|
||||
messages: convertMessages(selectedMessages, accounts, $employeeByIdStore)
|
||||
messages: convertMessages(object, channel, selectedMessages, accounts, $employeeByIdStore)
|
||||
}
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
@ -88,39 +80,6 @@
|
||||
selected.clear()
|
||||
selected = selected
|
||||
}
|
||||
|
||||
function convertMessages (
|
||||
messages: Message[],
|
||||
accounts: IdMap<EmployeeAccount>,
|
||||
employees: IdMap<Employee>
|
||||
): SharedMessage[] {
|
||||
return messages.map((m) => {
|
||||
return {
|
||||
...m,
|
||||
_id: m._id as string as Ref<SharedMessage>,
|
||||
sender: getName(m, accounts, employees, true),
|
||||
receiver: getName(m, accounts, employees, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getName (
|
||||
message: Message,
|
||||
accounts: IdMap<EmployeeAccount>,
|
||||
employees: IdMap<Employee>,
|
||||
sender: boolean
|
||||
): string {
|
||||
if (message.incoming === sender) {
|
||||
return `${getContactName(object)} (${channel.value})`
|
||||
} else {
|
||||
const account = accounts.get(message.modifiedBy as Ref<EmployeeAccount>)
|
||||
const emp = account ? employees.get(account?.employee) : undefined
|
||||
const value = message.incoming ? message.to : message.from
|
||||
const email = value.match(EMAIL_REGEX)
|
||||
const emailVal = email?.[0] ?? value
|
||||
return emp ? `${getContactName(emp)} (${emailVal})` : emailVal
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="popupPanel-body__main-header bottom-divider p-2">
|
||||
@ -167,7 +126,7 @@
|
||||
<div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
|
||||
{#if messages && messages.length > 0}
|
||||
<Messages
|
||||
messages={convertMessages(messages, accounts, $employeeByIdStore)}
|
||||
messages={convertMessages(object, channel, messages, accounts, $employeeByIdStore)}
|
||||
{selectable}
|
||||
bind:selected
|
||||
on:select
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
|
||||
import { Class, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { SharedMessage } from '@hcengineering/gmail'
|
||||
import { Message, SharedMessage } from '@hcengineering/gmail'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
@ -30,14 +30,17 @@
|
||||
import FullMessage from './FullMessage.svelte'
|
||||
import IntegrationSelector from './IntegrationSelector.svelte'
|
||||
import NewMessage from './NewMessage.svelte'
|
||||
import { convertMessage } from '../utils'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
|
||||
export let _id: Ref<Contact>
|
||||
export let _class: Ref<Class<Contact>>
|
||||
export let message: Message | undefined = undefined
|
||||
|
||||
let object: Contact
|
||||
let currentMessage: SharedMessage | undefined = undefined
|
||||
|
||||
let newMessage: boolean = false
|
||||
let currentMessage: SharedMessage | undefined = undefined
|
||||
let channel: Channel | undefined = undefined
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
let integrations: Integration[] = []
|
||||
@ -97,6 +100,11 @@
|
||||
integrations = res.filter((p) => p.createdBy === me || p.shared?.includes(me))
|
||||
selectedIntegration = integrations.find((p) => p.createdBy === me) ?? integrations[0]
|
||||
})
|
||||
|
||||
$: message &&
|
||||
channel &&
|
||||
object &&
|
||||
convertMessage(object, channel, message, $employeeByIdStore).then((p) => (currentMessage = p))
|
||||
</script>
|
||||
|
||||
{#if channel && object}
|
||||
|
@ -0,0 +1,33 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { Message } from '@hcengineering/gmail'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label, showPopup } from '@hcengineering/ui'
|
||||
import gmail from '../../plugin'
|
||||
import Main from '../Main.svelte'
|
||||
|
||||
export let value: Message
|
||||
|
||||
async function click () {
|
||||
const client = getClient()
|
||||
const channel = await client.findOne(value.attachedToClass, { _id: value.attachedTo })
|
||||
if (channel !== undefined) {
|
||||
showPopup(Main, { _id: channel.attachedTo, _class: channel.attachedToClass, message: value }, 'float')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<span class="over-underline" on:click={click}><Label label={gmail.string.Email} /></span>
|
@ -18,6 +18,7 @@ import { concatLink } from '@hcengineering/core'
|
||||
import { getMetadata, Resources } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import TxSharedCreate from './components/activity/TxSharedCreate.svelte'
|
||||
import TxWriteMessage from './components/activity/TxWriteMessage.svelte'
|
||||
import Configure from './components/Configure.svelte'
|
||||
import Connect from './components/Connect.svelte'
|
||||
import IconGmail from './components/icons/GmailColor.svelte'
|
||||
@ -35,7 +36,8 @@ export default async (): Promise<Resources> => ({
|
||||
Configure
|
||||
},
|
||||
activity: {
|
||||
TxSharedCreate
|
||||
TxSharedCreate,
|
||||
TxWriteMessage
|
||||
},
|
||||
function: {
|
||||
HasEmail: checkHasEmail
|
||||
|
@ -41,6 +41,8 @@ export default mergeIds(gmailId, gmail, {
|
||||
CopyPlaceholder: '' as IntlString,
|
||||
WriteEmail: '' as IntlString,
|
||||
Shared: '' as IntlString,
|
||||
AvailableTo: '' as IntlString
|
||||
AvailableTo: '' as IntlString,
|
||||
Email: '' as IntlString,
|
||||
HaveWrittenEmail: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -1,5 +1,6 @@
|
||||
import contact from '@hcengineering/contact'
|
||||
import { Doc } from '@hcengineering/core'
|
||||
import contact, { Channel, Contact, Employee, EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
|
||||
import { Doc, IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { Message, SharedMessage } from '@hcengineering/gmail'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export function getTime (time: number): string {
|
||||
@ -56,3 +57,59 @@ export async function checkHasEmail (doc: Doc | Doc[] | undefined): Promise<bool
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const EMAIL_REGEX =
|
||||
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
|
||||
|
||||
export function convertMessages (
|
||||
object: Contact,
|
||||
channel: Channel,
|
||||
messages: Message[],
|
||||
accounts: IdMap<EmployeeAccount>,
|
||||
employees: IdMap<Employee>
|
||||
): SharedMessage[] {
|
||||
return messages.map((m) => {
|
||||
return {
|
||||
...m,
|
||||
_id: m._id as string as Ref<SharedMessage>,
|
||||
sender: getName(object, channel, m, accounts, employees, true),
|
||||
receiver: getName(object, channel, m, accounts, employees, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function convertMessage (
|
||||
object: Contact,
|
||||
channel: Channel,
|
||||
message: Message,
|
||||
employees: IdMap<Employee>
|
||||
): Promise<SharedMessage> {
|
||||
const client = getClient()
|
||||
const accounts = toIdMap(await client.findAll(contact.class.EmployeeAccount, {}))
|
||||
return {
|
||||
...message,
|
||||
_id: message._id as string as Ref<SharedMessage>,
|
||||
sender: getName(object, channel, message, accounts, employees, true),
|
||||
receiver: getName(object, channel, message, accounts, employees, false)
|
||||
}
|
||||
}
|
||||
|
||||
export function getName (
|
||||
object: Contact,
|
||||
channel: Channel,
|
||||
message: Message,
|
||||
accounts: IdMap<EmployeeAccount>,
|
||||
employees: IdMap<Employee>,
|
||||
sender: boolean
|
||||
): string {
|
||||
if (message.incoming === sender) {
|
||||
return `${getContactName(object)} (${channel.value})`
|
||||
} else {
|
||||
const account = accounts.get(message.modifiedBy as Ref<EmployeeAccount>)
|
||||
const emp = account != null ? employees.get(account?.employee) : undefined
|
||||
const value = message.incoming ? message.to : message.from
|
||||
const email = value.match(EMAIL_REGEX)
|
||||
const emailVal = email?.[0] ?? value
|
||||
return emp != null ? `${getContactName(emp)} (${emailVal})` : emailVal
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import {
|
||||
ActivityKey,
|
||||
DisplayTx,
|
||||
getValue,
|
||||
newDisplayTx,
|
||||
TxDisplayViewlet,
|
||||
|
@ -13,11 +13,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey, DisplayTx, getValue, newDisplayTx, updateViewlet } from '@hcengineering/activity-resources'
|
||||
import { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey, getValue, newDisplayTx, updateViewlet } from '@hcengineering/activity-resources'
|
||||
import activity from '@hcengineering/activity-resources/src/plugin'
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, Ref, Tx } from '@hcengineering/core'
|
||||
import core, { AnyAttribute, Doc, Ref, Tx, TxCUD } from '@hcengineering/core'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
@ -37,7 +37,7 @@
|
||||
let modelIcon: Asset | undefined = undefined
|
||||
|
||||
$: if (tx._id !== ptx?.tx._id) {
|
||||
ptx = newDisplayTx(tx, client.getHierarchy())
|
||||
ptx = newDisplayTx(tx as TxCUD<Doc>, client.getHierarchy())
|
||||
if (tx.modifiedBy !== employee?._id) {
|
||||
employee = undefined
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user