UBER-343 Multiple emails per contact (#3327)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-06-02 13:00:18 +06:00 committed by GitHub
parent 8ce6d005df
commit da2f5f3ee4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 93 deletions

View File

@ -16,7 +16,7 @@
<script lang="ts"> <script lang="ts">
import type { Channel, ChannelProvider } from '@hcengineering/contact' import type { Channel, ChannelProvider } from '@hcengineering/contact'
import contact from '@hcengineering/contact' import contact from '@hcengineering/contact'
import type { AttachedData, Doc, Ref } from '@hcengineering/core' import { AttachedData, Doc, Ref, toIdMap } from '@hcengineering/core'
import notification, { DocUpdates } from '@hcengineering/notification' import notification, { DocUpdates } from '@hcengineering/notification'
import { Asset, IntlString, getResource } from '@hcengineering/platform' import { Asset, IntlString, getResource } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation' import presentation from '@hcengineering/presentation'
@ -36,7 +36,7 @@
import { invokeAction } from '@hcengineering/view-resources' import { invokeAction } from '@hcengineering/view-resources'
import { createEventDispatcher, tick } from 'svelte' import { createEventDispatcher, tick } from 'svelte'
import { Writable, writable } from 'svelte/store' import { Writable, writable } from 'svelte/store'
import { channelProviders, getChannelProviders } from '../utils' import { channelProviders } from '../utils'
import ChannelEditor from './ChannelEditor.svelte' import ChannelEditor from './ChannelEditor.svelte'
export let value: AttachedData<Channel>[] | Channel | null export let value: AttachedData<Channel>[] | Channel | null
@ -106,8 +106,9 @@
displayItems = [] displayItems = []
return return
} }
const result: Item[] = [] const result: Item[] = []
const map = getChannelProviders(channelProviders) const map = toIdMap(channelProviders)
if (Array.isArray(value)) { if (Array.isArray(value)) {
for (const item of value) { for (const item of value) {
const provider = getProvider(item, map, docUpdates) const provider = getProvider(item, map, docUpdates)
@ -139,27 +140,22 @@
const focusManager = getFocusManager() const focusManager = getFocusManager()
const updateMenu = (_displayItems: Item[], providers: ChannelProvider[]): void => { const updateMenu = (_displayItems: Item[], providers: ChannelProvider[]): void => {
actions = [] actions = providers.map((pr) => {
providers.forEach((pr) => { return {
if (_displayItems.filter((it) => it.provider === pr._id).length === 0) { icon: pr.icon ?? contact.icon.SocialEdit,
actions.push({ label: pr.label,
icon: pr.icon ?? contact.icon.SocialEdit, action: async () => {
label: pr.label, const provider = getProvider({ provider: pr._id, value: '' }, toIdMap(providers), $docUpdates)
action: async () => { if (provider !== undefined) {
const provider = getProvider({ provider: pr._id, value: '' }, getChannelProviders(providers), $docUpdates) displayItems = [..._displayItems, provider]
if (provider !== undefined) { if (focusIndex !== -1) {
if (_displayItems.filter((it) => it.provider === pr._id).length === 0) { await tick()
displayItems = [..._displayItems, provider] focusManager?.setFocusPos(focusIndex + displayItems.length)
if (focusIndex !== -1) { await tick()
await tick() editChannel(btns[displayItems.length - 1], displayItems.length - 1, provider)
focusManager?.setFocusPos(focusIndex + displayItems.length)
await tick()
editChannel(btns[displayItems.length - 1], displayItems.length - 1, provider)
}
}
} }
} }
}) }
} }
}) })
} }
@ -170,7 +166,6 @@
} }
const saveItems = (): void => { const saveItems = (): void => {
value = filterUndefined(displayItems) value = filterUndefined(displayItems)
dispatch('change', value)
updateMenu(displayItems, $channelProviders) updateMenu(displayItems, $channelProviders)
} }
@ -182,6 +177,12 @@
}) })
} }
function remove (n: number) {
const removed = displayItems[n]
displayItems = dropItem(n)
dispatch('remove', removed.channel)
}
const editChannel = (el: HTMLElement, n: number, item: Item): void => { const editChannel = (el: HTMLElement, n: number, item: Item): void => {
if (opened !== n) { if (opened !== n) {
opened = n opened = n
@ -195,7 +196,9 @@
}, },
el, el,
(result) => { (result) => {
if (result === undefined && item.value === '') displayItems = dropItem(n) if (result === undefined && item.value === '') {
remove(n)
}
if (result === 'open') { if (result === 'open') {
if (item.action) { if (item.action) {
const doc = item.channel as Channel const doc = item.channel as Channel
@ -205,12 +208,14 @@
} }
} else if (result != null) { } else if (result != null) {
if (result === '') { if (result === '') {
displayItems = dropItem(n) remove(n)
} else { } else {
item.value = result item.value = result
if (displayItems.find((it) => item.value === it.value) === undefined) { if (displayItems[n] === undefined) {
displayItems = [...displayItems, item] displayItems = [...displayItems, item]
} }
item.channel.value = item.value
dispatch('save', item.channel)
} }
saveItems() saveItems()
focusManager?.setFocusPos(focusIndex + 1 + n) focusManager?.setFocusPos(focusIndex + 1 + n)
@ -220,11 +225,12 @@
(result) => { (result) => {
if (result != null) { if (result != null) {
if (result === '') { if (result === '') {
displayItems = dropItem(n) remove(n)
} else { } else {
item.value = result item.value = result
saveItems()
dispatch('save', item.channel)
} }
saveItems()
} }
} }
) )

View File

@ -14,7 +14,7 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import type { Class, Doc, Ref, TxResult } from '@hcengineering/core' import type { AttachedData, Class, Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import { ButtonKind, ButtonSize, closeTooltip } from '@hcengineering/ui' import { ButtonKind, ButtonSize, closeTooltip } from '@hcengineering/ui'
@ -38,13 +38,6 @@
let channels: Channel[] = [] let channels: Channel[] = []
function findValue (provider: Ref<ChannelProvider>): Channel | undefined {
for (let i = 0; i < channels.length; i++) {
if (channels[i].provider === provider) return channels[i]
}
return undefined
}
const query = createQuery() const query = createQuery()
$: attachedTo && $: attachedTo &&
query.query( query.query(
@ -59,52 +52,25 @@
const client = getClient() const client = getClient()
async function save (newValues: Channel[]): Promise<void> { async function remove (value: Channel | AttachedData<Channel>): Promise<void> {
const currentProviders = new Set(channels.map((p) => p.provider)) if (!editable) return
const promises: Array<Promise<TxResult>> = [] if ('_id' in value) {
for (const value of newValues) { await client.remove(value)
const oldChannel = findValue(value.provider)
if (oldChannel === undefined) {
if (value.value.length === 0) continue
promises.push(
client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
value: value.value,
provider: value.provider
})
)
} else {
currentProviders.delete(value.provider)
if (value.value === oldChannel.value) continue
promises.push(
client.updateCollection(
oldChannel._class,
oldChannel.space,
oldChannel._id,
oldChannel.attachedTo,
oldChannel.attachedToClass,
oldChannel.collection,
{
value: value.value
}
)
)
}
} }
for (const value of currentProviders) { }
const oldChannel = findValue(value)
if (oldChannel === undefined) continue async function saveHandler (value: Channel | AttachedData<Channel>): Promise<void> {
promises.push( if (!editable) return
client.removeCollection( if ('_id' in value) {
oldChannel._class, await client.update(value, {
oldChannel.space, value: value.value
oldChannel._id, })
oldChannel.attachedTo, } else {
oldChannel.attachedToClass, await client.addCollection(contact.class.Channel, contact.space.Contacts, attachedTo, attachedClass, 'channels', {
oldChannel.collection value: value.value,
) provider: value.provider
) })
} }
Promise.all(promises)
} }
function _open (ev: any) { function _open (ev: any) {
@ -127,8 +93,11 @@
{restricted} {restricted}
{shape} {shape}
{focusIndex} {focusIndex}
on:change={(e) => { on:remove={(e) => {
if (editable) save(e.detail) remove(e.detail)
}}
on:save={(e) => {
saveHandler(e.detail)
}} }}
on:open={_open} on:open={_open}
/> />

View File

@ -15,14 +15,14 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Channel, ChannelProvider } from '@hcengineering/contact' import type { Channel, ChannelProvider } from '@hcengineering/contact'
import type { AttachedData, Doc, Ref } from '@hcengineering/core' import { AttachedData, Doc, Ref, toIdMap } from '@hcengineering/core'
import notification, { DocUpdates } from '@hcengineering/notification' import notification, { DocUpdates } from '@hcengineering/notification'
import { Asset, IntlString, getResource } from '@hcengineering/platform' import { Asset, IntlString, getResource } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui' import type { AnyComponent } from '@hcengineering/ui'
import { Button } from '@hcengineering/ui' import { Button } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { Writable, writable } from 'svelte/store' import { Writable, writable } from 'svelte/store'
import { channelProviders, getChannelProviders } from '../utils' import { channelProviders } from '../utils'
import ChannelsPopup from './ChannelsPopup.svelte' import ChannelsPopup from './ChannelsPopup.svelte'
export let value: AttachedData<Channel>[] | Channel | null export let value: AttachedData<Channel>[] | Channel | null
@ -81,7 +81,7 @@
return return
} }
const result: any[] = [] const result: any[] = []
const map = getChannelProviders(channels) const map = toIdMap(channels)
if (Array.isArray(value)) { if (Array.isArray(value)) {
for (const item of value) { for (const item of value) {
const provider = getProvider(item, map, docUpdates) const provider = getProvider(item, map, docUpdates)

View File

@ -36,14 +36,6 @@ import { FilterQuery } from '@hcengineering/view-resources'
import { get, writable } from 'svelte/store' import { get, writable } from 'svelte/store'
import contact from './plugin' import contact from './plugin'
export function getChannelProviders (providers: ChannelProvider[]): Map<Ref<ChannelProvider>, ChannelProvider> {
const map = new Map<Ref<ChannelProvider>, ChannelProvider>()
for (const provider of providers) {
map.set(provider._id, provider)
}
return map
}
export function formatDate (dueDateMs: Timestamp): string { export function formatDate (dueDateMs: Timestamp): string {
return new Date(dueDateMs).toLocaleString('default', { return new Date(dueDateMs).toLocaleString('default', {
month: 'short', month: 'short',