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 contact, { Employee, EmployeeAccount } from '@anticrm/contact'
import { Account, DocumentQuery, Ref, SortingOrder, Space } from '@anticrm/core' import { Account, DocumentQuery, Ref, SortingOrder, Space } from '@anticrm/core'
import { translate } from '@anticrm/platform' 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 presentation from '../plugin'
import { getClient } from '../utils' import { getClient } from '../utils'
import UserInfo from './UserInfo.svelte' import UserInfo from './UserInfo.svelte'
export let space: Space export let space: Space
export let withAddButton: boolean = false
const client = getClient() const client = getClient()
const dispatch = createEventDispatcher()
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
$: label = hierarchy.getClass(space._class).label $: label = hierarchy.getClass(space._class).label
let spaceClass = '' let spaceClass = ''
@ -77,6 +81,16 @@
</div> </div>
{/if} {/if}
{/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} {#each current as person}
<div class="item fs-title"><UserInfo size={'medium'} value={person} /></div> <div class="item fs-title"><UserInfo size={'medium'} value={person} /></div>
{/each} {/each}

View File

@ -34,6 +34,7 @@
export let placeholder: IntlString = presentation.string.Search export let placeholder: IntlString = presentation.string.Search
export let selectedUsers: Ref<Person>[] = [] export let selectedUsers: Ref<Person>[] = []
export let ignoreUsers: Ref<Person>[] = [] export let ignoreUsers: Ref<Person>[] = []
export let shadows: boolean = true
let search: string = '' let search: string = ''
let objects: Person[] = [] let objects: Person[] = []
@ -72,7 +73,7 @@
}) })
</script> </script>
<div class="selectPopup"> <div class="selectPopup" class:plainContainer={!shadows}>
<div class="header"> <div class="header">
<input bind:this={input} type="text" bind:value={search} placeholder={phTraslate} on:change /> <input bind:this={input} type="text" bind:value={search} placeholder={phTraslate} on:change />
</div> </div>
@ -110,3 +111,13 @@
</div> </div>
</div> </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}", "SharedBy": "Shared by {name} {time}",
"LeaveChannel": "Leave channel", "LeaveChannel": "Leave channel",
"ChannelBrowser": "Channel browser", "ChannelBrowser": "Channel browser",
"SavedItems": "Saved items" "SavedItems": "Saved items",
"AddMembersHeader": "Add members to {channel}:"
} }
} }

View File

@ -56,6 +56,7 @@
"SharedBy": "Доступ открыт {name} {time}", "SharedBy": "Доступ открыт {name} {time}",
"LeaveChannel": "Покинуть канал", "LeaveChannel": "Покинуть канал",
"ChannelBrowser": "Браузер каналов", "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 chunter from '../plugin'
import { classIcon } from '../utils' import { classIcon } from '../utils'
import Header from './Header.svelte' import Header from './Header.svelte'
import Lock from './icons/Lock.svelte'
export let spaceId: Ref<Channel> | undefined export let spaceId: Ref<Channel> | undefined
@ -40,7 +41,7 @@
<div class="ac-header divide full"> <div class="ac-header divide full">
{#if channel} {#if channel}
<Header <Header
icon={classIcon(client, channel._class)} icon={channel.private ? Lock : classIcon(client, channel._class)}
label={channel.name} label={channel.name}
description={channel.topic} description={channel.topic}
on:click={onSpaceEdit} on:click={onSpaceEdit}

View File

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

View File

@ -15,15 +15,18 @@
--> -->
<script lang="ts"> <script lang="ts">
import { ChunterSpace } from '@anticrm/chunter' import { ChunterSpace } from '@anticrm/chunter'
import { EmployeeAccount } from '@anticrm/contact'
import type { Class, Ref } from '@anticrm/core' import type { Class, Ref } from '@anticrm/core'
import type { IntlString } from '@anticrm/platform' import type { IntlString } from '@anticrm/platform'
import { createQuery, getClient, Members } from '@anticrm/presentation' 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 { createEventDispatcher } from 'svelte'
import chunter from '../plugin' import chunter from '../plugin'
import AddMembersPopup from './AddMembersPopup.svelte'
import EditChannelDescriptionTab from './EditChannelDescriptionTab.svelte' import EditChannelDescriptionTab from './EditChannelDescriptionTab.svelte'
import EditChannelSettingsTab from './EditChannelSettingsTab.svelte' import EditChannelSettingsTab from './EditChannelSettingsTab.svelte'
import Lock from './icons/Lock.svelte'
export let _id: Ref<ChunterSpace> export let _id: Ref<ChunterSpace>
export let _class: Ref<Class<ChunterSpace>> export let _class: Ref<Class<ChunterSpace>>
@ -46,6 +49,24 @@
...(_class === chunter.class.Channel ? [chunter.string.Settings] : []) ...(_class === chunter.class.Channel ? [chunter.string.Settings] : [])
] ]
let selectedTabIndex = 0 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> </script>
<div <div
@ -57,7 +78,11 @@
<div class="ac-header short mirror divide"> <div class="ac-header short mirror divide">
<div class="ac-header__wrap-title"> <div class="ac-header__wrap-title">
<div class="ac-header__icon"> <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>
<div class="ac-header__title"><Label label={clazz.label} /></div> <div class="ac-header__title"><Label label={clazz.label} /></div>
</div> </div>
@ -91,7 +116,7 @@
<EditChannelDescriptionTab {channel} on:close /> <EditChannelDescriptionTab {channel} on:close />
</Scroller> </Scroller>
{:else if selectedTabIndex === 1} {:else if selectedTabIndex === 1}
<Members space={channel} /> <Members space={channel} withAddButton={true} on:addMembers={openAddMembersPopup} />
{:else if selectedTabIndex === 2} {:else if selectedTabIndex === 2}
<EditChannelSettingsTab {channel} on:close /> <EditChannelSettingsTab {channel} on:close />
{/if} {/if}

View File

@ -14,9 +14,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Asset } from '@anticrm/platform' 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 label: string
export let description: string | undefined export let description: string | undefined
</script> </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, SharedBy: '' as IntlString,
LeaveChannel: '' as IntlString, LeaveChannel: '' as IntlString,
ChannelBrowser: '' as IntlString, ChannelBrowser: '' as IntlString,
SavedItems: '' as IntlString SavedItems: '' as IntlString,
AddMembersHeader: '' as IntlString
} }
}) })