Messages search (#1935)

Signed-off-by: budaeva <irina.budaeva@xored.com>
This commit is contained in:
budaeva 2022-05-31 20:01:24 +07:00 committed by GitHub
parent 5a99d0dd93
commit 6739ce302d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 233 additions and 33 deletions

View File

@ -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'

View File

@ -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>
}
})

View File

@ -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"
}
}

View File

@ -58,6 +58,9 @@
"ChannelBrowser": "Браузер каналов",
"SavedItems": "Сохраненные объекты",
"AddMembersHeader": "Добавить пользователей в {channel}:",
"ConvertToPrivate": "Конвертировать в закрытый канал"
"ConvertToPrivate": "Конвертировать в закрытый канал",
"MessagesBrowser": "Браузер сообщений",
"CreateBy": "Создано пользователем",
"ThreadMessage": "Сообщение в обсуждении"
}
}

View File

@ -43,7 +43,6 @@
mode: 'space',
space: dm._id
})
return
}
}

View File

@ -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">

View File

@ -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 {

View File

@ -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>

View File

@ -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}

View File

@ -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,

View File

@ -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
}
})

View File

@ -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)
}

View File

@ -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
}