Chunter: private channel & add channel members ui (#1524) (#1589)

Signed-off-by: budaeva <irina.budaeva@xored.com>
This commit is contained in:
budaeva 2022-04-29 15:22:47 +07:00 committed by GitHub
parent eb9526f015
commit 21f0928e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 189 additions and 13 deletions

View File

@ -16,13 +16,17 @@
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
import { Account, DocumentQuery, Ref, SortingOrder, Space } from '@anticrm/core'
import { translate } from '@anticrm/platform'
import { Label, Scroller, SearchEdit } from '@anticrm/ui'
import { IconAdd, Label, Scroller, SearchEdit } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import presentation from '../plugin'
import { getClient } from '../utils'
import UserInfo from './UserInfo.svelte'
export let space: Space
export let withAddButton: boolean = false
const client = getClient()
const dispatch = createEventDispatcher()
const hierarchy = client.getHierarchy()
$: label = hierarchy.getClass(space._class).label
let spaceClass = ''
@ -77,6 +81,16 @@
</div>
{/if}
{/if}
{#if !isSearch && withAddButton}
<div class="item fs-title">
<div class="flex-row-center" on:click={() => dispatch('addMembers')}>
<div class="flex-center ml-1 mr-1"><IconAdd size={'large'} /></div>
<div class="flex-col ml-2 min-w-0 content-accent-color">
<Label label={presentation.string.Add} />
</div>
</div>
</div>
{/if}
{#each current as person}
<div class="item fs-title"><UserInfo size={'medium'} value={person} /></div>
{/each}

View File

@ -34,6 +34,7 @@
export let placeholder: IntlString = presentation.string.Search
export let selectedUsers: Ref<Person>[] = []
export let ignoreUsers: Ref<Person>[] = []
export let shadows: boolean = true
let search: string = ''
let objects: Person[] = []
@ -72,7 +73,7 @@
})
</script>
<div class="selectPopup">
<div class="selectPopup" class:plainContainer={!shadows}>
<div class="header">
<input bind:this={input} type="text" bind:value={search} placeholder={phTraslate} on:change />
</div>
@ -110,3 +111,13 @@
</div>
</div>
</div>
<style lang="scss">
.plainContainer {
color: var(--caption-color);
background-color: var(--body-color);
border: 1px solid var(--button-border-color);
border-radius: .25rem;
box-shadow: none;
}
</style>

View File

@ -57,6 +57,7 @@
"SharedBy": "Shared by {name} {time}",
"LeaveChannel": "Leave channel",
"ChannelBrowser": "Channel browser",
"SavedItems": "Saved items"
"SavedItems": "Saved items",
"AddMembersHeader": "Add members to {channel}:"
}
}

View File

@ -56,6 +56,7 @@
"SharedBy": "Доступ открыт {name} {time}",
"LeaveChannel": "Покинуть канал",
"ChannelBrowser": "Браузер каналов",
"SavedItems": "Сохраненные объекты"
"SavedItems": "Сохраненные объекты",
"AddMembersHeader": "Добавить пользователей в {channel}:"
}
}

View File

@ -0,0 +1,107 @@
<script lang="ts">
import { Channel } from '@anticrm/chunter'
import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
import core, { Ref } from '@anticrm/core'
import presentation, { getClient, UsersPopup } from '@anticrm/presentation'
import { Label, Button, ActionIcon, IconClose } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import chunter from '../plugin'
export let channel: Channel
const dispatch = createEventDispatcher()
const client = getClient()
let membersToAdd: EmployeeAccount[] = []
let channelMembers: Ref<Employee>[] = []
client.findAll(core.class.Account, { _id: { $in: channel.members } }).then((res) => {
channelMembers = res
.filter((e) => e._class === contact.class.EmployeeAccount)
.map((e) => (e as EmployeeAccount).employee)
})
async function changeMembersToAdd (employees: Ref<Employee>[]) {
if (employees) {
await client.findAll(contact.class.EmployeeAccount, { employee: { $in: employees } }).then((res) => {
if (res) {
membersToAdd = res
}
})
}
}
$: selectedEmployees = membersToAdd.map((e) => e.employee)
function removeMember (_id: Ref<EmployeeAccount>) {
membersToAdd = membersToAdd.filter((m) => m._id !== _id)
}
</script>
<div class="antiPopup antiPopup-withHeader">
<div class="ap-header flex-between header">
<div class="ap-caption">
<Label label={chunter.string.AddMembersHeader} params={{ channel: channel.name }} />
</div>
<div class="tool">
<ActionIcon
icon={IconClose}
size={'small'}
action={() => {
dispatch('close')
}}
/>
</div>
</div>
{#if membersToAdd.length}
<div class="flex-row-top flex-wrap ml-6 mr-6 mt-4">
{#each membersToAdd as m}
<div class=" mr-2 p-1 item">
{formatName(m.name)}
<div class="tool">
<ActionIcon
icon={IconClose}
size={'small'}
action={() => {
removeMember(m._id)
}}
/>
</div>
</div>
{/each}
</div>
{/if}
<div class="ml-8 mr-8 mb-6 mt-4">
<UsersPopup
selected={undefined}
_class={contact.class.Employee}
multiSelect={true}
allowDeselect={true}
selectedUsers={selectedEmployees}
ignoreUsers={channelMembers}
shadows={false}
on:update={(ev) => changeMembersToAdd(ev.detail)}
/>
</div>
<Button
on:click={() => {
dispatch(
'update',
membersToAdd.map((m) => m._id)
)
dispatch('close')
}}
label={presentation.string.Add}
/>
</div>
<style lang="scss">
.header {
flex-direction: row;
}
.item {
display: flex;
flex-direction: row;
width: fit-content;
background-color: var(--popup-bg-hover);
}
</style>

View File

@ -20,6 +20,7 @@
import chunter from '../plugin'
import { classIcon } from '../utils'
import Header from './Header.svelte'
import Lock from './icons/Lock.svelte'
export let spaceId: Ref<Channel> | undefined
@ -40,7 +41,7 @@
<div class="ac-header divide full">
{#if channel}
<Header
icon={classIcon(client, channel._class)}
icon={channel.private ? Lock : classIcon(client, channel._class)}
label={channel.name}
description={channel.topic}
on:click={onSpaceEdit}

View File

@ -23,6 +23,7 @@
const dispatch = createEventDispatcher()
let isPrivate: boolean = false
let name: string = ''
export function canClose (): boolean {
return name === ''
@ -33,7 +34,7 @@
client.createDoc(chunter.class.Channel, core.space.Space, {
name,
description: '',
private: false,
private: isPrivate,
archived: false,
members: [getCurrentAccount()._id]
})
@ -57,6 +58,10 @@
maxWidth={'16rem'}
focus
/>
<ToggleWithLabel label={chunter.string.MakePrivate} description={chunter.string.MakePrivateDescription} />
<ToggleWithLabel
label={chunter.string.MakePrivate}
description={chunter.string.MakePrivateDescription}
bind:on={isPrivate}
/>
</Grid>
</SpaceCreateCard>

View File

@ -15,15 +15,18 @@
-->
<script lang="ts">
import { ChunterSpace } from '@anticrm/chunter'
import { EmployeeAccount } from '@anticrm/contact'
import type { Class, Ref } from '@anticrm/core'
import type { IntlString } from '@anticrm/platform'
import { createQuery, getClient, Members } from '@anticrm/presentation'
import { Icon, IconClose, Label, ActionIcon, Scroller } from '@anticrm/ui'
import { ActionIcon, Icon, IconClose, Label, Scroller, showPopup } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import chunter from '../plugin'
import AddMembersPopup from './AddMembersPopup.svelte'
import EditChannelDescriptionTab from './EditChannelDescriptionTab.svelte'
import EditChannelSettingsTab from './EditChannelSettingsTab.svelte'
import Lock from './icons/Lock.svelte'
export let _id: Ref<ChunterSpace>
export let _class: Ref<Class<ChunterSpace>>
@ -46,6 +49,24 @@
...(_class === chunter.class.Channel ? [chunter.string.Settings] : [])
]
let selectedTabIndex = 0
function openAddMembersPopup () {
showPopup(
AddMembersPopup,
{ channel },
undefined,
() => {},
async (membersIds: Ref<EmployeeAccount>[]) => {
if (membersIds) {
membersIds
.filter((m: Ref<EmployeeAccount>) => !channel.members.includes(m))
.forEach(async (m) => {
await client.update(channel, { $push: { members: m } })
})
}
}
)
}
</script>
<div
@ -57,7 +78,11 @@
<div class="ac-header short mirror divide">
<div class="ac-header__wrap-title">
<div class="ac-header__icon">
{#if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
{#if channel}
{#if channel.private}
<Lock size={'medium'} />
{:else if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
{/if}
</div>
<div class="ac-header__title"><Label label={clazz.label} /></div>
</div>
@ -91,7 +116,7 @@
<EditChannelDescriptionTab {channel} on:close />
</Scroller>
{:else if selectedTabIndex === 1}
<Members space={channel} />
<Members space={channel} withAddButton={true} on:addMembers={openAddMembersPopup} />
{:else if selectedTabIndex === 2}
<EditChannelSettingsTab {channel} on:close />
{/if}

View File

@ -14,9 +14,9 @@
-->
<script lang="ts">
import type { Asset } from '@anticrm/platform'
import { Icon } from '@anticrm/ui'
import { AnySvelteComponent, Icon } from '@anticrm/ui'
export let icon: Asset | undefined
export let icon: Asset | AnySvelteComponent | undefined
export let label: string
export let description: string | undefined
</script>

View File

@ -0,0 +1,10 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="M13,6.9h-1.1V4.8c0-2.2-1.8-3.9-3.9-3.9c-2.2,0-3.9,1.8-3.9,3.9v2.1H3c-0.9,0-1.6,0.7-1.6,1.6v5.3 c0,0.9,0.7,1.6,1.6,1.6h10c0.9,0,1.6-0.7,1.6-1.6V8.5C14.6,7.6,13.9,6.9,13,6.9z M5.3,4.8c0-1.5,1.2-2.7,2.7-2.7s2.7,1.2,2.7,2.7 v2.1H5.3V4.8z M13.4,13.8c0,0.2-0.2,0.4-0.4,0.4H3c-0.2,0-0.4-0.2-0.4-0.4V8.5c0-0.2,0.2-0.4,0.4-0.4h10c0.2,0,0.4,0.2,0.4,0.4V13.8 z"
/>
</svg>

View File

@ -80,6 +80,7 @@ export default mergeIds(chunterId, chunter, {
SharedBy: '' as IntlString,
LeaveChannel: '' as IntlString,
ChannelBrowser: '' as IntlString,
SavedItems: '' as IntlString
SavedItems: '' as IntlString,
AddMembersHeader: '' as IntlString
}
})