From 6739ce302df928dbe309442cfe20c0b1c17c40a4 Mon Sep 17 00:00:00 2001 From: budaeva <47611627+budaeva@users.noreply.github.com> Date: Tue, 31 May 2022 20:01:24 +0700 Subject: [PATCH] Messages search (#1935) Signed-off-by: budaeva <irina.budaeva@xored.com> --- models/chunter/src/index.ts | 12 ++ models/chunter/src/plugin.ts | 13 +- plugins/chunter-assets/lang/en.json | 5 +- plugins/chunter-assets/lang/ru.json | 5 +- .../src/components/CreateDirectMessage.svelte | 1 - .../src/components/DmHeader.svelte | 18 ++- .../src/components/Header.svelte | 18 ++- .../src/components/MessagesBrowser.svelte | 128 ++++++++++++++++++ .../src/components/SavedMessages.svelte | 30 ++-- plugins/chunter-resources/src/index.ts | 14 +- plugins/chunter-resources/src/plugin.ts | 3 +- plugins/chunter-resources/src/utils.ts | 17 ++- plugins/view-resources/src/index.ts | 2 + 13 files changed, 233 insertions(+), 33 deletions(-) create mode 100644 plugins/chunter-resources/src/components/MessagesBrowser.svelte diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 605de85b48..67fcfb919e 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -93,6 +93,7 @@ export class TChunterMessage extends TAttachedDoc implements ChunterMessage { } @Model(chunter.class.ThreadMessage, chunter.class.ChunterMessage) +@UX(chunter.string.ThreadMessage) export class TThreadMessage extends TChunterMessage implements ThreadMessage { declare attachedTo: Ref<Message> @@ -100,6 +101,7 @@ export class TThreadMessage extends TChunterMessage implements ThreadMessage { } @Model(chunter.class.Message, chunter.class.ChunterMessage) +@UX(chunter.string.Message) export class TMessage extends TChunterMessage implements Message { declare attachedTo: Ref<Space> @@ -347,6 +349,12 @@ export function createModel (builder: Builder): void { componentProps: { requestedSpaceClasses: [chunter.class.Channel, chunter.class.DirectMessage] } + }, + { + id: 'messagesBrowser', + label: chunter.string.MessagesBrowser, + component: chunter.component.MessagesBrowser, + visibleIf: chunter.function.MessageBrowserVisible } ], spaces: [ @@ -437,6 +445,10 @@ export function createModel (builder: Builder): void { }, chunter.ids.TxBacklinkRemove ) + + builder.mixin(chunter.class.ChunterMessage, core.class.Class, view.mixin.ClassFilters, { + filters: ['space', 'modifiedOn', 'createBy', '_class'] + }) } export { chunterOperation } from './migration' diff --git a/models/chunter/src/plugin.ts b/models/chunter/src/plugin.ts index 3eaf6ec341..1ec84e2a10 100644 --- a/models/chunter/src/plugin.ts +++ b/models/chunter/src/plugin.ts @@ -16,8 +16,8 @@ import type { TxViewlet } from '@anticrm/activity' import { Channel, chunterId } from '@anticrm/chunter' import chunter from '@anticrm/chunter-resources/src/plugin' -import type { Ref } from '@anticrm/core' -import type { IntlString } from '@anticrm/platform' +import type { Ref, Space } from '@anticrm/core' +import type { IntlString, Resource } from '@anticrm/platform' import { mergeIds } from '@anticrm/platform' import type { AnyComponent } from '@anticrm/ui' import type { Action, ActionCategory, ViewAction, ViewletDescriptor } from '@anticrm/view' @@ -29,7 +29,8 @@ export default mergeIds(chunterId, chunter, { DmPresenter: '' as AnyComponent, Threads: '' as AnyComponent, ThreadView: '' as AnyComponent, - SavedMessages: '' as AnyComponent + SavedMessages: '' as AnyComponent, + MessagesBrowser: '' as AnyComponent }, action: { MarkCommentUnread: '' as Ref<Action>, @@ -63,7 +64,8 @@ export default mergeIds(chunterId, chunter, { MarkUnread: '' as IntlString, LastMessage: '' as IntlString, PinnedMessages: '' as IntlString, - SavedMessages: '' as IntlString + SavedMessages: '' as IntlString, + ThreadMessage: '' as IntlString }, viewlet: { Chat: '' as Ref<ViewletDescriptor> @@ -82,5 +84,8 @@ export default mergeIds(chunterId, chunter, { space: { General: '' as Ref<Channel>, Random: '' as Ref<Channel> + }, + function: { + MessageBrowserVisible: '' as Resource<(spaces: Space[]) => boolean> } }) diff --git a/plugins/chunter-assets/lang/en.json b/plugins/chunter-assets/lang/en.json index 967517f736..72459fe2a7 100644 --- a/plugins/chunter-assets/lang/en.json +++ b/plugins/chunter-assets/lang/en.json @@ -59,6 +59,9 @@ "ChannelBrowser": "Channel browser", "SavedItems": "Saved items", "AddMembersHeader": "Add members to {channel}:", - "ConvertToPrivate": "Convert to private channel" + "ConvertToPrivate": "Convert to private channel", + "MessagesBrowser": "Messages browser", + "CreateBy": "Create by", + "ThreadMessage": "Thread message" } } \ No newline at end of file diff --git a/plugins/chunter-assets/lang/ru.json b/plugins/chunter-assets/lang/ru.json index f710feffe1..af3fb5ba0b 100644 --- a/plugins/chunter-assets/lang/ru.json +++ b/plugins/chunter-assets/lang/ru.json @@ -58,6 +58,9 @@ "ChannelBrowser": "Браузер каналов", "SavedItems": "Сохраненные объекты", "AddMembersHeader": "Добавить пользователей в {channel}:", - "ConvertToPrivate": "Конвертировать в закрытый канал" + "ConvertToPrivate": "Конвертировать в закрытый канал", + "MessagesBrowser": "Браузер сообщений", + "CreateBy": "Создано пользователем", + "ThreadMessage": "Сообщение в обсуждении" } } \ No newline at end of file diff --git a/plugins/chunter-resources/src/components/CreateDirectMessage.svelte b/plugins/chunter-resources/src/components/CreateDirectMessage.svelte index d31283e432..56911203c5 100644 --- a/plugins/chunter-resources/src/components/CreateDirectMessage.svelte +++ b/plugins/chunter-resources/src/components/CreateDirectMessage.svelte @@ -43,7 +43,6 @@ mode: 'space', space: dm._id }) - return } } diff --git a/plugins/chunter-resources/src/components/DmHeader.svelte b/plugins/chunter-resources/src/components/DmHeader.svelte index 0762627328..5ebec0d0e0 100644 --- a/plugins/chunter-resources/src/components/DmHeader.svelte +++ b/plugins/chunter-resources/src/components/DmHeader.svelte @@ -18,12 +18,16 @@ import { getCurrentAccount } from '@anticrm/core' import { createQuery, getClient, CombineAvatars } from '@anticrm/presentation' import contact, { EmployeeAccount } from '@anticrm/contact' - import { showPanel } from '@anticrm/ui' + import { getCurrentLocation, navigate, SearchEdit, showPanel } from '@anticrm/ui' import chunter from '../plugin' import { getDmName } from '../utils' + import { userSearch } from '../index' export let spaceId: Ref<DirectMessage> | undefined + let userSearch_: string = '' + userSearch.subscribe((v) => (userSearch_ = v)) + const client = getClient() const query = createQuery() const myAccId = getCurrentAccount()._id @@ -62,6 +66,18 @@ {/await} {/await} {/if} + <SearchEdit + value={userSearch_} + on:change={(ev) => { + userSearch.set(ev.detail) + + if (ev.detail !== '') { + const loc = getCurrentLocation() + loc.path[2] = 'messagesBrowser' + navigate(loc) + } + }} + /> </div> <style lang="scss"> diff --git a/plugins/chunter-resources/src/components/Header.svelte b/plugins/chunter-resources/src/components/Header.svelte index e5b7c990fc..b708fe9654 100644 --- a/plugins/chunter-resources/src/components/Header.svelte +++ b/plugins/chunter-resources/src/components/Header.svelte @@ -14,11 +14,15 @@ --> <script lang="ts"> import type { Asset } from '@anticrm/platform' - import { AnySvelteComponent, Icon } from '@anticrm/ui' + import { AnySvelteComponent, getCurrentLocation, Icon, navigate, SearchEdit } from '@anticrm/ui' + import { userSearch } from '../index' export let icon: Asset | AnySvelteComponent | undefined export let label: string export let description: string | undefined + + let userSearch_: string + userSearch.subscribe((v) => (userSearch_ = v)) </script> <div class="ac-header__wrap-description"> @@ -28,6 +32,18 @@ </div> {#if description}<span class="ac-header__description">{description}</span>{/if} </div> +<SearchEdit + value={userSearch_} + on:change={(ev) => { + userSearch.set(ev.detail) + + if (ev.detail !== '') { + const loc = getCurrentLocation() + loc.path[2] = 'messagesBrowser' + navigate(loc) + } + }} +/> <style lang="scss"> .ac-header__wrap-title:hover { diff --git a/plugins/chunter-resources/src/components/MessagesBrowser.svelte b/plugins/chunter-resources/src/components/MessagesBrowser.svelte new file mode 100644 index 0000000000..30a3a77d71 --- /dev/null +++ b/plugins/chunter-resources/src/components/MessagesBrowser.svelte @@ -0,0 +1,128 @@ +<script lang="ts"> + import attachment, { Attachment } from '@anticrm/attachment' + import chunter, { ChunterMessage } from '@anticrm/chunter' + import contact, { Employee } from '@anticrm/contact' + import core, { DocumentQuery, Ref, SortingOrder } from '@anticrm/core' + import { createQuery, getClient } from '@anticrm/presentation' + import { Label, Scroller, SearchEdit } from '@anticrm/ui' + import type { Filter } from '@anticrm/view' + import { FilterBar, FilterButton } from '@anticrm/view-resources' + import MessageComponent from './Message.svelte' + import { userSearch } from '../index' + import plugin from '../plugin' + import { openMessageFromSpecial } from '../utils' + + let userSearch_: string = '' + userSearch.subscribe((v) => (userSearch_ = v)) + + let searchQuery: DocumentQuery<ChunterMessage> = { $search: userSearch_ } + + let filters: Filter[] = [] + + function updateSearchQuery (search: string): void { + searchQuery = { $search: search } + } + + $: updateSearchQuery(userSearch_) + + const client = getClient() + const _class = chunter.class.ChunterMessage + let messages: ChunterMessage[] = [] + + let resultQuery: DocumentQuery<ChunterMessage> = { ...searchQuery } + + async function updateMessages (resultQuery: DocumentQuery<ChunterMessage>) { + messages = await client.findAll( + _class, + { + ...resultQuery + }, + { + sort: { createOn: SortingOrder.Descending }, + limit: 100, + lookup: { + _id: { attachments: attachment.class.Attachment }, + createBy: core.class.Account + } + } + ) + } + + $: updateMessages(resultQuery) + + let employees: Map<Ref<Employee>, Employee> = new Map<Ref<Employee>, Employee>() + const employeeQuery = createQuery() + + employeeQuery.query( + contact.class.Employee, + {}, + (res) => + (employees = new Map( + res.map((r) => { + return [r._id, r] + }) + )), + { + lookup: { _id: { statuses: contact.class.Status } } + } + ) + + const pinnedQuery = createQuery() + const pinnedIds: Ref<ChunterMessage>[] = [] + + pinnedQuery.query( + chunter.class.Channel, + {}, + (res) => { + res.forEach((ch) => { + if (ch.pinned) { + pinnedIds.push(...ch.pinned) + } + }) + }, + {} + ) + let savedMessagesIds: Ref<ChunterMessage>[] = [] + let savedAttachmentsIds: Ref<Attachment>[] = [] + + const savedMessagesQuery = createQuery() + const savedAttachmentsQuery = createQuery() + + savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => { + savedMessagesIds = res.map((r) => r.attachedTo) + }) + + savedAttachmentsQuery.query(attachment.class.SavedAttachments, {}, (res) => { + savedAttachmentsIds = res.map((r) => r.attachedTo) + }) +</script> + +<div class="ac-header full divide"> + <div class="ac-header__wrap-title"> + <span class="ac-header__title"><Label label={plugin.string.MessagesBrowser} /></span> + </div> + <div class="ml-4"><FilterButton {_class} bind:filters /></div> + <SearchEdit + value={userSearch_} + on:change={(ev) => { + userSearch.set(ev.detail) + updateSearchQuery(userSearch_) + updateMessages(resultQuery) + }} + /> +</div> +<FilterBar {_class} query={searchQuery} bind:filters on:change={(e) => (resultQuery = e.detail)} /> +<Scroller> + {#each messages as message} + <div on:click={() => openMessageFromSpecial(message)}> + <MessageComponent + {message} + {employees} + on:openThread + isPinned={pinnedIds.includes(message._id)} + isSaved={savedMessagesIds.includes(message._id)} + {savedAttachmentsIds} + /> + </div> + {/each} +</Scroller> diff --git a/plugins/chunter-resources/src/components/SavedMessages.svelte b/plugins/chunter-resources/src/components/SavedMessages.svelte index 043acb9956..63c5d68969 100644 --- a/plugins/chunter-resources/src/components/SavedMessages.svelte +++ b/plugins/chunter-resources/src/components/SavedMessages.svelte @@ -4,12 +4,12 @@ import { ChunterMessage } from '@anticrm/chunter' import core, { Ref, WithLookup } from '@anticrm/core' import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact' - import { getCurrentLocation, Label, navigate, Scroller } from '@anticrm/ui' + import { Label, Scroller } from '@anticrm/ui' import AttachmentPreview from '@anticrm/attachment-resources/src/components/AttachmentPreview.svelte' import Bookmark from './icons/Bookmark.svelte' import Message from './Message.svelte' import chunter from '../plugin' - import { getTime } from '../utils' + import { getTime, openMessageFromSpecial } from '../utils' const client = getClient() let savedMessagesIds: Ref<ChunterMessage>[] = [] @@ -83,29 +83,19 @@ chunter.class.Channel, {}, (res) => { - res.forEach((ch) => pinnedIds.concat(ch?.pinned ?? [])) + res.forEach((ch) => { + if (ch.pinned) { + pinnedIds.push(...ch.pinned) + } + }) }, - { limit: 1 } + {} ) - function openMessage (message: ChunterMessage) { - const loc = getCurrentLocation() - - if (message.attachedToClass === chunter.class.ChunterSpace) { - loc.path.length = 3 - loc.path[2] = message.attachedTo - } else if (message.attachedToClass === chunter.class.Message) { - loc.path.length = 4 - loc.path[2] = message.space - loc.path[3] = message.attachedTo - } - navigate(loc) - } - async function openAttachment (att: Attachment) { const messageId: Ref<ChunterMessage> = att.attachedTo as Ref<ChunterMessage> await client.findOne(chunter.class.ChunterMessage, { _id: messageId }).then((res) => { - if (res !== undefined) openMessage(res) + if (res !== undefined) openMessageFromSpecial(res) }) } @@ -125,7 +115,7 @@ <Scroller> {#if savedMessages.length > 0 || savedAttachments.length > 0} {#each savedMessages as message} - <div on:click={() => openMessage(message)}> + <div on:click={() => openMessageFromSpecial(message)}> <Message {message} {employees} diff --git a/plugins/chunter-resources/src/index.ts b/plugins/chunter-resources/src/index.ts index 5851773855..6f11faec41 100644 --- a/plugins/chunter-resources/src/index.ts +++ b/plugins/chunter-resources/src/index.ts @@ -13,7 +13,7 @@ // limitations under the License. // -import core from '@anticrm/core' +import core, { Space } from '@anticrm/core' import chunter, { ChunterSpace, Channel, ChunterMessage, Message, ThreadMessage, DirectMessage } from '@anticrm/chunter' import { NotificationClientImpl } from '@anticrm/notification-resources' import { Resources } from '@anticrm/platform' @@ -34,12 +34,14 @@ import CommentsPresenter from './components/CommentsPresenter.svelte' import CreateChannel from './components/CreateChannel.svelte' import CreateDirectMessage from './components/CreateDirectMessage.svelte' import EditChannel from './components/EditChannel.svelte' +import MessagesBrowser from './components/MessagesBrowser.svelte' import ThreadView from './components/ThreadView.svelte' import Threads from './components/Threads.svelte' import SavedMessages from './components/SavedMessages.svelte' import ConvertDmToPrivateChannelModal from './components/ConvertDmToPrivateChannel.svelte' import { getDmName } from './utils' +import { writable } from 'svelte/store' export { default as Header } from './components/Header.svelte' export { classIcon } from './utils' @@ -160,6 +162,12 @@ export async function DeleteMessageFromSaved (message: ChunterMessage): Promise< } } +export const userSearch = writable('') + +export function messageBrowserVisible (spaces: Space[]): boolean { + return false +} + export default async (): Promise<Resources> => ({ component: { CommentInput, @@ -173,12 +181,14 @@ export default async (): Promise<Resources> => ({ ChannelPresenter, DmPresenter, EditChannel, + MessagesBrowser, Threads, ThreadView, SavedMessages }, function: { - GetDmName: getDmName + GetDmName: getDmName, + MessageBrowserVisible: messageBrowserVisible }, activity: { TxCommentCreate, diff --git a/plugins/chunter-resources/src/plugin.ts b/plugins/chunter-resources/src/plugin.ts index f3569d72ad..c08b390f4c 100644 --- a/plugins/chunter-resources/src/plugin.ts +++ b/plugins/chunter-resources/src/plugin.ts @@ -81,6 +81,7 @@ export default mergeIds(chunterId, chunter, { LeaveChannel: '' as IntlString, ChannelBrowser: '' as IntlString, SavedItems: '' as IntlString, - AddMembersHeader: '' as IntlString + AddMembersHeader: '' as IntlString, + MessagesBrowser: '' as IntlString } }) diff --git a/plugins/chunter-resources/src/utils.ts b/plugins/chunter-resources/src/utils.ts index 46e651c454..41e306d3ff 100644 --- a/plugins/chunter-resources/src/utils.ts +++ b/plugins/chunter-resources/src/utils.ts @@ -1,7 +1,8 @@ +import { ChunterMessage } from '@anticrm/chunter' import contact, { EmployeeAccount, formatName } from '@anticrm/contact' import { Account, Class, Client, Obj, Ref, Space, getCurrentAccount, Timestamp } from '@anticrm/core' import { Asset } from '@anticrm/platform' -import { getCurrentLocation, locationToUrl } from '@anticrm/ui' +import { getCurrentLocation, locationToUrl, navigate } from '@anticrm/ui' import chunter from './plugin' @@ -69,3 +70,17 @@ export function getDay (time: Timestamp): Timestamp { date.setHours(0, 0, 0, 0) return date.getTime() } + +export function openMessageFromSpecial (message: ChunterMessage): void { + const loc = getCurrentLocation() + + if (message.attachedToClass === chunter.class.ChunterSpace) { + loc.path.length = 3 + loc.path[2] = message.attachedTo + } else if (message.attachedToClass === chunter.class.Message) { + loc.path.length = 4 + loc.path[2] = message.space + loc.path[3] = message.attachedTo + } + navigate(loc) +} diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index 921d3f7e29..0352c81fc0 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -45,6 +45,7 @@ import ValueFilter from './components/filter/ValueFilter.svelte' import ObjectFilter from './components/filter/ObjectFilter.svelte' import TimestampFilter from './components/filter/TimestampFilter.svelte' import ClassPresenter from './components/ClassPresenter.svelte' +import FilterBar from './components/filter/FilterBar.svelte' import EditBoxPopup from './components/EditBoxPopup.svelte' import BooleanTruePresenter from './components/BooleanTruePresenter.svelte' import EnumEditor from './components/EnumEditor.svelte' @@ -75,6 +76,7 @@ export { SpacePresenter, UpDownNavigator, ViewletSetting, + FilterBar, ClassAttributeBar }