diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 0bd60779eb..0516087a78 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -14,11 +14,31 @@ // import activity from '@anticrm/activity' -import type { Backlink, Channel, ChunterMessage, Comment, Message, SavedMessages, ThreadMessage } from '@anticrm/chunter' +import type { + Backlink, + Channel, + ChunterMessage, + Comment, + Message, + SavedMessages, + ThreadMessage +} from '@anticrm/chunter' import contact, { Employee } from '@anticrm/contact' import type { Account, Class, Doc, Domain, Ref, Space, Timestamp } from '@anticrm/core' import { IndexKind } from '@anticrm/core' -import { ArrOf, Builder, Collection, Index, Model, Prop, TypeMarkup, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model' +import { + ArrOf, + Builder, + Collection, + Index, + Model, + Prop, + TypeMarkup, + TypeRef, + TypeString, + TypeTimestamp, + UX +} from '@anticrm/model' import attachment from '@anticrm/model-attachment' import core, { TAttachedDoc, TSpace } from '@anticrm/model-core' import view from '@anticrm/model-view' @@ -131,11 +151,16 @@ export function createModel (builder: Builder): void { header: chunter.component.ChannelHeader }) - builder.createDoc(view.class.ViewletDescriptor, core.space.Model, { - label: chunter.string.Chat, - icon: view.icon.Table, - component: chunter.component.ChannelView - }, chunter.viewlet.Chat) + builder.createDoc( + view.class.ViewletDescriptor, + core.space.Model, + { + label: chunter.string.Chat, + icon: view.icon.Table, + component: chunter.component.ChannelView + }, + chunter.viewlet.Chat + ) builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: chunter.class.Message, @@ -225,91 +250,130 @@ export function createModel (builder: Builder): void { } }) - builder.createDoc(workbench.class.Application, core.space.Model, { - label: chunter.string.ApplicationLabelChunter, - icon: chunter.icon.Chunter, - hidden: false, - navigatorModel: { - specials: [ - { - id: 'archive', - component: workbench.component.Archive, - icon: view.icon.Archive, - label: workbench.string.Archive, - position: 'top', - visibleIf: workbench.function.HasArchiveSpaces, - spaceClass: chunter.class.Channel - }, - { - id: 'threads', - label: chunter.string.Threads, - icon: chunter.icon.Thread, - component: chunter.component.Threads, - position: 'top' - }, - { - id: 'savedMessages', - label: chunter.string.SavedMessages, - icon: chunter.icon.Bookmark, - component: chunter.component.SavedMessages - } - ], - spaces: [ - { - label: chunter.string.Channels, - spaceClass: chunter.class.Channel, - addSpaceLabel: chunter.string.CreateChannel, - createComponent: chunter.component.CreateChannel - } - ], - aside: chunter.component.ThreadView - } - }, chunter.app.Chunter) + builder.createDoc( + workbench.class.Application, + core.space.Model, + { + label: chunter.string.ApplicationLabelChunter, + icon: chunter.icon.Chunter, + hidden: false, + navigatorModel: { + specials: [ + { + id: 'spaceBrowser', + component: workbench.component.SpaceBrowser, + icon: workbench.icon.Search, + label: chunter.string.ChannelBrowser, + position: 'top', + spaceClass: chunter.class.Channel, + componentProps: { + _class: chunter.class.Channel, + label: chunter.string.ChannelBrowser, + createItemDialog: chunter.component.CreateChannel, + createItemLabel: chunter.string.CreateChannel + } + }, + { + id: 'archive', + component: workbench.component.Archive, + icon: view.icon.Archive, + label: workbench.string.Archive, + position: 'top', + visibleIf: workbench.function.HasArchiveSpaces, + spaceClass: chunter.class.Channel + }, + { + id: 'threads', + label: chunter.string.Threads, + icon: chunter.icon.Thread, + component: chunter.component.Threads, + position: 'top' + }, + { + id: 'savedMessages', + label: chunter.string.SavedMessages, + icon: chunter.icon.Bookmark, + component: chunter.component.SavedMessages + } + ], + spaces: [ + { + label: chunter.string.Channels, + spaceClass: chunter.class.Channel, + addSpaceLabel: chunter.string.CreateChannel, + createComponent: chunter.component.CreateChannel + } + ], + aside: chunter.component.ThreadView + } + }, + chunter.app.Chunter + ) builder.mixin(chunter.class.Comment, core.class.Class, view.mixin.AttributePresenter, { presenter: chunter.component.CommentPresenter }) - builder.createDoc(activity.class.TxViewlet, core.space.Model, { - objectClass: chunter.class.Comment, - icon: chunter.icon.Chunter, - txClass: core.class.TxCreateDoc, - component: chunter.activity.TxCommentCreate, - label: chunter.string.LeftComment, - display: 'content', - editable: true, - hideOnRemove: true - }, chunter.ids.TxCommentCreate) + builder.createDoc( + activity.class.TxViewlet, + core.space.Model, + { + objectClass: chunter.class.Comment, + icon: chunter.icon.Chunter, + txClass: core.class.TxCreateDoc, + component: chunter.activity.TxCommentCreate, + label: chunter.string.LeftComment, + display: 'content', + editable: true, + hideOnRemove: true + }, + chunter.ids.TxCommentCreate + ) // We need to define this one, to hide default attached object removed case - builder.createDoc(activity.class.TxViewlet, core.space.Model, { - objectClass: chunter.class.Comment, - icon: chunter.icon.Chunter, - txClass: core.class.TxRemoveDoc, - display: 'inline', - hideOnRemove: true - }, chunter.ids.TxCommentRemove) + builder.createDoc( + activity.class.TxViewlet, + core.space.Model, + { + objectClass: chunter.class.Comment, + icon: chunter.icon.Chunter, + txClass: core.class.TxRemoveDoc, + display: 'inline', + hideOnRemove: true + }, + chunter.ids.TxCommentRemove + ) - builder.createDoc(activity.class.TxViewlet, core.space.Model, { - objectClass: chunter.class.Backlink, - icon: chunter.icon.Chunter, - txClass: core.class.TxCreateDoc, - component: chunter.activity.TxBacklinkCreate, - label: chunter.string.MentionedIn, - labelComponent: chunter.activity.TxBacklinkReference, - display: 'emphasized', - editable: false, - hideOnRemove: true - }, chunter.ids.TxCommentCreate) + builder.createDoc( + activity.class.TxViewlet, + core.space.Model, + { + objectClass: chunter.class.Backlink, + icon: chunter.icon.Chunter, + txClass: core.class.TxCreateDoc, + component: chunter.activity.TxBacklinkCreate, + label: chunter.string.MentionedIn, + labelComponent: chunter.activity.TxBacklinkReference, + display: 'emphasized', + editable: false, + hideOnRemove: true + }, + chunter.ids.TxCommentCreate + ) // We need to define this one, to hide default attached object removed case - builder.createDoc(activity.class.TxViewlet, core.space.Model, { - objectClass: chunter.class.Backlink, - icon: chunter.icon.Chunter, - txClass: core.class.TxRemoveDoc, - display: 'inline', - hideOnRemove: true - }, chunter.ids.TxBacklinkRemove) + builder.createDoc( + activity.class.TxViewlet, + core.space.Model, + { + objectClass: chunter.class.Backlink, + icon: chunter.icon.Chunter, + txClass: core.class.TxRemoveDoc, + display: 'inline', + hideOnRemove: true + }, + chunter.ids.TxBacklinkRemove + ) } export { chunterOperation } from './migration' diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index 21d6409395..2b1aef0ec7 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -83,6 +83,20 @@ export function createModel (builder: Builder): void { hidden: false, navigatorModel: { specials: [ + { + id: 'spaceBrowser', + component: workbench.component.SpaceBrowser, + icon: workbench.icon.Search, + label: lead.string.FunnelBrowser, + position: 'top', + spaceClass: chunter.class.Channel, + componentProps: { + _class: lead.class.Funnel, + label: lead.string.FunnelBrowser, + createItemDialog: lead.component.CreateFunnel, + createItemLabel: lead.string.CreateFunnel + } + }, { id: 'customers', label: lead.string.Customers, diff --git a/models/lead/src/plugin.ts b/models/lead/src/plugin.ts index 7e55778fa3..5c5c4fa4e9 100644 --- a/models/lead/src/plugin.ts +++ b/models/lead/src/plugin.ts @@ -30,7 +30,8 @@ export default mergeIds(leadId, lead, { Lead: '' as IntlString, Title: '' as IntlString, Assignee: '' as IntlString, - ManageFunnelStatuses: '' as IntlString + ManageFunnelStatuses: '' as IntlString, + FunnelBrowser: '' as IntlString }, component: { CreateFunnel: '' as AnyComponent, diff --git a/models/workbench/src/plugin.ts b/models/workbench/src/plugin.ts index 1b0f990b7b..105b73209e 100644 --- a/models/workbench/src/plugin.ts +++ b/models/workbench/src/plugin.ts @@ -21,11 +21,13 @@ import workbench, { workbenchId } from '@anticrm/workbench' export default mergeIds(workbenchId, workbench, { component: { ApplicationPresenter: '' as AnyComponent, - Archive: '' as AnyComponent + Archive: '' as AnyComponent, + SpaceBrowser: '' as AnyComponent }, string: { Archive: '' as IntlString, - Application: '' as IntlString + Application: '' as IntlString, + SpaceBrowser: '' as IntlString }, function: { HasArchiveSpaces: '' as Resource<(spaces: Space[]) => boolean> diff --git a/packages/presentation/lang/en.json b/packages/presentation/lang/en.json index b56c8ff544..0104d79040 100644 --- a/packages/presentation/lang/en.json +++ b/packages/presentation/lang/en.json @@ -15,6 +15,11 @@ "Search": "Search...", "Unassigned": "Unassigned", "CreateMore": "Create more", - "NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}" + "NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}", + "InThis": "In this {space}", + "NoMatchesInThis": "No matches in this {space}", + "NoMatchesFound": "No matches found", + "NotInThis": "Not in this {space}", + "Add": "Add" } } diff --git a/packages/presentation/lang/ru.json b/packages/presentation/lang/ru.json index d463e87ac8..6501865233 100644 --- a/packages/presentation/lang/ru.json +++ b/packages/presentation/lang/ru.json @@ -15,6 +15,11 @@ "Search": "Поиск...", "Unassigned": "Не назначен", "CreateMore": "Создать еще", - "NumberMembers": "{count, plural, =0 {нет участников} =1 {1 участник} other {# участника}}" + "NumberMembers": "{count, plural, =0 {нет участников} =1 {1 участник} other {# участника}}", + "InThis": "В этом {space}", + "NoMatchesInThis": "В этом {space} совпадения не обнаружены", + "NoMatchesFound": "Не найдено соответсвий", + "NotInThis": "Не в этом {space}", + "Add": "Добавить" } } diff --git a/packages/presentation/src/components/Members.svelte b/packages/presentation/src/components/Members.svelte index d66cfcc51a..86af0fa747 100644 --- a/packages/presentation/src/components/Members.svelte +++ b/packages/presentation/src/components/Members.svelte @@ -14,23 +14,99 @@ --> -
-
- -
- +
+
+ {#await getUsers(space.members, search) then users} + {@const current = users.filter((p) => members.has(p._id))} + {@const foreign = users.filter((p) => !members.has(p._id))} + {#if isSearch && !foreign.length && !current.length} +
+
+ {:else} + + {#if isSearch} +
+ {#if !current.length} +
+
+ {/if} + {/if} + {#each current as person} +
+ {/each} + {#if foreign.length} +
+
+
+ {#each foreign as person} +
+
+
add(person._id)}>
+
+ {/each} +
+ {/if} + + {/if} + {/await}
diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts index f0045cf6a0..a739eda3cb 100644 --- a/packages/presentation/src/plugin.ts +++ b/packages/presentation/src/plugin.ts @@ -44,7 +44,12 @@ export default plugin(presentationId, { Search: '' as IntlString, Unassigned: '' as IntlString, CreateMore: '' as IntlString, - NumberMembers: '' as IntlString + NumberMembers: '' as IntlString, + InThis: '' as IntlString, + NoMatchesInThis: '' as IntlString, + NoMatchesFound: '' as IntlString, + NotInThis: '' as IntlString, + Add: '' as IntlString }, metadata: { RequiredVersion: '' as Metadata diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index 2fc632919c..6c281c5aab 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -353,6 +353,7 @@ p:last-child { margin-block-end: 0; } .pr-2 { padding-right: .5rem; } .pr-3 { padding-right: .75rem; } .pr-4 { padding-right: 1rem; } +.pr-8 { padding-right: 2rem; } .pr-24 { padding-right: 6rem; } .pt-2 { padding-top: .5rem; } .pt-3 { padding-top: .75rem; } diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json index c3cceb7825..25c87b7565 100644 --- a/plugins/chunter-assets/lang/en.json +++ b/plugins/chunter-assets/lang/en.json @@ -49,6 +49,8 @@ "AddToSaved": "Add to saved", "RemoveFromSaved": "Remove from saved", "EmptySavedHeader": "Add messages to come back to later", - "EmptySavedText": "Tick off your to-dos or save something for another time. Only you can see your saved items, so use them however you like." + "EmptySavedText": "Tick off your to-dos or save something for another time. Only you can see your saved items, so use them however you like.", + "LeaveChannel": "Leave channel", + "ChannelBrowser": "Channel browser" } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json index f369de8009..cc23b0bdbf 100644 --- a/plugins/chunter-assets/lang/ru.json +++ b/plugins/chunter-assets/lang/ru.json @@ -48,6 +48,8 @@ "AddToSaved": "Добавить в сохраненные", "RemoveFromSaved": "Удалить из сохраненных", "EmptySavedHeader": "Добавляйте сообщения и файлы, чтобы вернуться к ним позже", - "EmptySavedText": "Пометьте свои задачи или сохраните что-нибудь на потом. Только вы можете просматривать свои сохраненные объекты, поэтому используйте их как угодно." + "EmptySavedText": "Пометьте свои задачи или сохраните что-нибудь на потом. Только вы можете просматривать свои сохраненные объекты, поэтому используйте их как угодно.", + "LeaveChannel": "Покинуть канал", + "ChannelBrowser": "Браузер каналов" } } \ No newline at end of file diff --git a/plugins/chunter-resources/src/components/EditChannel.svelte b/plugins/chunter-resources/src/components/EditChannel.svelte index 15334f9a5a..bc535d2091 100644 --- a/plugins/chunter-resources/src/components/EditChannel.svelte +++ b/plugins/chunter-resources/src/components/EditChannel.svelte @@ -28,7 +28,7 @@ export let _id: Ref export let _class: Ref> - let channel: Channel + let channel: Channel | undefined const dispatch = createEventDispatcher() @@ -81,14 +81,15 @@ {/each}
- + {#if channel} {#if selectedTabIndex === 0} - + + + {:else if selectedTabIndex === 1} - - + {:else if selectedTabIndex === 2} {/if} - + {/if}
diff --git a/plugins/chunter-resources/src/components/EditChannelDescriptionAttachments.svelte b/plugins/chunter-resources/src/components/EditChannelDescriptionAttachments.svelte index 8059b93f58..8cc386b32b 100644 --- a/plugins/chunter-resources/src/components/EditChannelDescriptionAttachments.svelte +++ b/plugins/chunter-resources/src/components/EditChannelDescriptionAttachments.svelte @@ -96,7 +96,7 @@ diff --git a/plugins/workbench-resources/src/components/navigator/SpacePanel.svelte b/plugins/workbench-resources/src/components/navigator/SpacePanel.svelte index 2e54143e02..3519878ae0 100644 --- a/plugins/workbench-resources/src/components/navigator/SpacePanel.svelte +++ b/plugins/workbench-resources/src/components/navigator/SpacePanel.svelte @@ -13,12 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. --> - -
{ dispatch('close') }}/> +
{ + dispatch('close') + }} +/>
@@ -63,12 +70,25 @@ {/if}
-
{ dispatch('close') }} />
+
+ { + dispatch('close') + }} + /> +
{#each tabs as tab, i} -
{ selected = i }}> +
{ + selected = i + }} + >
{/each} @@ -78,13 +98,21 @@ {#if selected === 0} {#if space} - + {/if} {:else} -
diff --git a/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte b/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte index dae631ca8f..301f02bcfd 100644 --- a/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte +++ b/plugins/workbench-resources/src/components/navigator/SpacesNav.svelte @@ -19,7 +19,17 @@ import { NotificationClientImpl } from '@anticrm/notification-resources' import { getResource } from '@anticrm/platform' import { getClient } from '@anticrm/presentation' - import { Action, AnyComponent, IconAdd, IconEdit, showPanel, showPopup } from '@anticrm/ui' + import { + Action, + AnyComponent, + getCurrentLocation, + IconAdd, + IconEdit, + IconSearch, + navigate, + showPanel, + showPopup + } from '@anticrm/ui' import view from '@anticrm/view' import preference from '@anticrm/preference' import { getActions as getContributedActions } from '@anticrm/view-resources' @@ -35,6 +45,7 @@ export let currentSpace: Ref | undefined export let spaces: Space[] export let currentSpecial: string | undefined + export let hasSpaceBrowser: boolean = false const client = getClient() const dispatch = createEventDispatcher() @@ -46,6 +57,16 @@ } } + const browseSpaces: Action = { + label: plugin.string.BrowseSpaces, + icon: IconSearch, + action: async (_id: Ref, ev?: Event): Promise => { + const loc = getCurrentLocation() + loc.path[2] = 'spaceBrowser' + navigate(loc) + } + } + const editSpace: Action = { label: plugin.string.Open, icon: IconEdit, @@ -65,7 +86,7 @@ } } - async function getEditor (_class: Ref>): Promise { + async function getEditor(_class: Ref>): Promise { const hierarchy = client.getHierarchy() const clazz = hierarchy.getClass(_class) const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor) @@ -73,11 +94,11 @@ return editorMixin.editor } - function selectSpace (id: Ref, spaceSpecial?: string) { + function selectSpace(id: Ref, spaceSpecial?: string) { dispatch('space', { space: id, spaceSpecial }) } - async function getActions (space: Space): Promise { + async function getActions(space: Space): Promise { const result = [editSpace, starSpace] const extraActions = await getContributedActions(client, space, core.class.Space) @@ -100,7 +121,7 @@ $: clazz = hierarchy.getClass(model.spaceClass) $: lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit) - function isChanged (space: Space, lastViews: Map, number>): boolean { + function isChanged(space: Space, lastViews: Map, number>): boolean { const field = lastEditMixin?.lastEditField const lastView = lastViews.get(space._id) if (lastView === undefined || lastView === -1) return false @@ -110,9 +131,13 @@ return lastView < value } + + function getParentActions(): Action[] { + return hasSpaceBrowser ? [browseSpaces, addSpace] : [addSpace] + } - [addSpace]} indent={'ml-2'}> + getParentActions()} indent={'ml-2'}> {#each spaces as space (space._id)} {#if model.specials} getActions(space)}> diff --git a/plugins/workbench-resources/src/index.ts b/plugins/workbench-resources/src/index.ts index 3e8523ef33..820f285158 100644 --- a/plugins/workbench-resources/src/index.ts +++ b/plugins/workbench-resources/src/index.ts @@ -19,9 +19,10 @@ import { Resources } from '@anticrm/platform' import Archive from './components/Archive.svelte' import { Space } from '@anticrm/core' import SpacePanel from './components/navigator/SpacePanel.svelte' +import SpaceBrowser from './components/SpaceBrowser.svelte' -function hasArchiveSpaces (spaces: Space[]): boolean { - return spaces.find(sp => sp.archived) !== undefined +function hasArchiveSpaces(spaces: Space[]): boolean { + return spaces.find((sp) => sp.archived) !== undefined } export default async (): Promise => ({ @@ -29,7 +30,8 @@ export default async (): Promise => ({ WorkbenchApp, ApplicationPresenter, Archive, - SpacePanel + SpacePanel, + SpaceBrowser }, function: { HasArchiveSpaces: hasArchiveSpaces diff --git a/plugins/workbench-resources/src/plugin.ts b/plugins/workbench-resources/src/plugin.ts index 04f1640f33..a150a7e767 100644 --- a/plugins/workbench-resources/src/plugin.ts +++ b/plugins/workbench-resources/src/plugin.ts @@ -29,7 +29,12 @@ export default mergeIds(workbenchId, workbench, { Archived: '' as IntlString, Open: '' as IntlString, General: '' as IntlString, - Members: '' as IntlString + Members: '' as IntlString, + View: '' as IntlString, + Leave: '' as IntlString, + Joined: '' as IntlString, + Join: '' as IntlString, + BrowseSpaces: '' as IntlString }, component: { SpacePanel: '' as AnyComponent diff --git a/plugins/workbench/src/index.ts b/plugins/workbench/src/index.ts index 7266140b29..4e03767b6e 100644 --- a/plugins/workbench/src/index.ts +++ b/plugins/workbench/src/index.ts @@ -107,6 +107,9 @@ export default plugin(workbenchId, { component: { WorkbenchApp: '' as AnyComponent }, + icon: { + Search: '' as Asset + }, metadata: { PlatformTitle: '' as Metadata, ExcludedApplications: '' as Metadata[]>