Chunter: saved attachments (#1515)

Signed-off-by: budaeva <irina.budaeva@xored.com>
This commit is contained in:
budaeva 2022-04-25 20:44:43 +07:00 committed by GitHub
parent 25bc560b8b
commit e5c64849ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 344 additions and 93 deletions

View File

@ -33,6 +33,7 @@
"@anticrm/platform": "~0.6.5",
"@anticrm/model-core": "~0.6.0",
"@anticrm/model-view": "~0.6.0",
"@anticrm/activity": "~0.6.0"
"@anticrm/activity": "~0.6.0",
"@anticrm/model-preference": "~0.6.0"
}
}

View File

@ -14,11 +14,12 @@
//
import activity from '@anticrm/activity'
import type { Attachment, Photo } from '@anticrm/attachment'
import { Domain, IndexKind } from '@anticrm/core'
import { Builder, Index, Model, Prop, TypeString, TypeTimestamp, UX } from '@anticrm/model'
import type { Attachment, Photo, SavedAttachments } from '@anticrm/attachment'
import { Domain, IndexKind, Ref } from '@anticrm/core'
import { Builder, Index, Model, Prop, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model'
import core, { TAttachedDoc } from '@anticrm/model-core'
import view from '@anticrm/model-view'
import preference, { TPreference } from '@anticrm/model-preference'
import attachment from './plugin'
export { attachmentOperation } from './migration'
@ -50,8 +51,14 @@ export class TAttachment extends TAttachedDoc implements Attachment {
@UX(attachment.string.Photo)
export class TPhoto extends TAttachment implements Photo {}
@Model(attachment.class.SavedAttachments, preference.class.Preference)
export class TSavedAttachments extends TPreference implements SavedAttachments {
@Prop(TypeRef(attachment.class.Attachment), attachment.string.SavedAttachments)
attachedTo!: Ref<Attachment>
}
export function createModel (builder: Builder): void {
builder.createModel(TAttachment, TPhoto)
builder.createModel(TAttachment, TPhoto, TSavedAttachments)
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.AttributePresenter, {
presenter: attachment.component.AttachmentPresenter

View File

@ -33,7 +33,8 @@ export default mergeIds(attachmentId, attachment, {
Size: '' as IntlString,
Type: '' as IntlString,
Photo: '' as IntlString,
Date: '' as IntlString
Date: '' as IntlString,
SavedAttachments: '' as IntlString
},
ids: {
TxAttachmentCreate: '' as Ref<TxViewlet>

View File

@ -16,9 +16,9 @@
import activity from '@anticrm/activity'
import type {
Backlink,
ChunterSpace,
Channel,
ChunterMessage,
ChunterSpace,
Comment,
Message,
SavedMessages,

View File

@ -36,6 +36,8 @@
"FileBrowserTypeFilterImages": "Images",
"FileBrowserTypeFilterAudio": "Audio",
"FileBrowserTypeFilterVideos": "Videos",
"FileBrowserTypeFilterPDFs": "PDFs"
"FileBrowserTypeFilterPDFs": "PDFs",
"AddAttachmentToSaved": "Add attachment to saved",
"RemoveAttachmentFromSaved": "Remove attachment from saved"
}
}

View File

@ -36,6 +36,8 @@
"FileBrowserTypeFilterImages": "Изображения",
"FileBrowserTypeFilterAudio": "Звук",
"FileBrowserTypeFilterVideos": "Видео",
"FileBrowserTypeFilterPDFs": "PDF-файлы"
"FileBrowserTypeFilterPDFs": "PDF-файлы",
"AddAttachmentToSaved": "Добавить вложение в сохраненные",
"RemoveAttachmentFromSaved": "Удалить вложение из сохраненных"
}
}

View File

@ -43,6 +43,7 @@
"@anticrm/panel": "~0.6.0",
"@anticrm/text-editor": "~0.6.0",
"@anticrm/login": "~0.6.1",
"filesize": "^8.0.3"
"filesize": "^8.0.3",
"@anticrm/preference": "~0.6.0"
}
}

View File

@ -14,16 +14,18 @@
-->
<script lang="ts">
import { Attachment } from '@anticrm/attachment'
import { Ref } from '@anticrm/core'
import AttachmentPreview from './AttachmentPreview.svelte'
export let attachments: Attachment[] = []
export let savedAttachmentsIds: Ref<Attachment>[] = []
</script>
{#if attachments.length}
<div class="container">
{#each attachments as attachment}
<div class="item">
<AttachmentPreview value={attachment} />
<AttachmentPreview value={attachment} isSaved={savedAttachmentsIds?.includes(attachment._id) ?? false} />
</div>
{/each}
</div>

View File

@ -13,43 +13,116 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import type { Attachment } from '@anticrm/attachment'
import { getResource } from '@anticrm/platform'
import { getFileUrl, PDFViewer } from '@anticrm/presentation'
import { showPopup, closeTooltip, ActionIcon, IconMoreH, Menu } from '@anticrm/ui'
import { Action } from '@anticrm/view'
import { getType } from '../utils'
import { showPopup, closeTooltip } from '@anticrm/ui'
import AttachmentPresenter from './AttachmentPresenter.svelte'
import AudioPlayer from './AudioPlayer.svelte'
import attachment from '../plugin'
export let value: Attachment
export let isSaved: boolean = false
$: type = getType(value.type)
$: saveAttachmentAction = isSaved
? ({
label: attachment.string.RemoveAttachmentFromSaved,
action: attachment.actionImpl.DeleteAttachmentFromSaved
} as Action)
: ({
label: attachment.string.AddAttachmentToSaved,
action: attachment.actionImpl.AddAttachmentToSaved
} as Action)
const showMenu = (ev: Event) => {
showPopup(
Menu,
{
actions: [
{
label: saveAttachmentAction.label,
icon: saveAttachmentAction.icon,
action: async (evt: MouseEvent) => {
const impl = await getResource(saveAttachmentAction.action)
await impl(value, evt)
}
}
]
},
ev.target as HTMLElement
)
}
</script>
<div class="flex-row-center">
{#if type === 'image'}
<div class='content flex-center cursor-pointer' on:click={() => {
closeTooltip()
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
}}>
<div
class="content flex-center buttonContainer cursor-pointer"
on:click={() => {
closeTooltip()
showPopup(PDFViewer, { file: value.file, name: value.name, contentType: value.type }, 'right')
}}
>
<img src={getFileUrl(value.file)} alt={value.name} />
<div class="more">
<ActionIcon
icon={IconMoreH}
size={'small'}
action={(e) => {
showMenu(e)
}}
/>
</div>
</div>
{:else if type === 'audio'}
<AudioPlayer {value} />
<div class="buttonContainer">
<AudioPlayer {value} />
<div class="more">
<ActionIcon
icon={IconMoreH}
size={'small'}
action={(e) => {
showMenu(e)
}}
/>
</div>
</div>
{:else if type === 'video'}
<div class='content flex-center'>
<div class="content buttonContainer flex-center">
<video controls>
<source src={getFileUrl(value.file)} type={value.type}>
<source src={getFileUrl(value.file)} type={value.type} />
<track kind="captions" label={value.name} />
<div class='container'>
<div class="container">
<AttachmentPresenter {value} />
</div>
</video>
<div class="more">
<ActionIcon
icon={IconMoreH}
size={'small'}
action={(e) => {
showMenu(e)
}}
/>
</div>
</div>
{:else}
<div class='container'>
<div class="flex container buttonContainer">
<AttachmentPresenter {value} />
<div class="more">
<ActionIcon
icon={IconMoreH}
size={'small'}
action={(e) => {
showMenu(e)
}}
/>
</div>
</div>
{/if}
</div>
@ -59,14 +132,29 @@
background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-color);
border-radius: 0.75rem;
padding: 0.5rem
padding: 0.5rem;
}
.buttonContainer {
align-items: flex-start;
.more {
margin-left: 0.5rem;
visibility: hidden;
}
}
.buttonContainer:hover {
.more {
visibility: visible;
}
}
.content {
max-width: 20rem;
max-height: 20rem;
img, video {
img,
video {
max-width: 20rem;
max-height: 20rem;
border-radius: 0.75rem;

View File

@ -26,6 +26,9 @@ import FileBrowser from './components/FileBrowser.svelte'
import Photos from './components/Photos.svelte'
import { Resources } from '@anticrm/platform'
import { uploadFile, deleteFile } from './utils'
import attachment, { Attachment } from '@anticrm/attachment'
import preference from '@anticrm/preference'
import { getClient } from '@anticrm/presentation'
export {
AddAttachment,
@ -38,6 +41,23 @@ export {
AttachmentDocList
}
export async function AddAttachmentToSaved(attach: Attachment): Promise<void> {
const client = getClient()
await client.createDoc(attachment.class.SavedAttachments, preference.space.Preference, {
attachedTo: attach._id
})
}
export async function DeleteAttachmentFromSaved(attach: Attachment): Promise<void> {
const client = getClient()
const current = await client.findOne(attachment.class.SavedAttachments, { attachedTo: attach._id })
if (current !== undefined) {
await client.remove(current)
}
}
export default async (): Promise<Resources> => ({
component: {
AttachmentsPresenter,
@ -52,5 +72,9 @@ export default async (): Promise<Resources> => ({
helper: {
UploadFile: uploadFile,
DeleteFile: deleteFile
},
actionImpl: {
AddAttachmentToSaved,
DeleteAttachmentFromSaved
}
})

View File

@ -18,6 +18,7 @@ import { mergeIds } from '@anticrm/platform'
import type { IntlString } from '@anticrm/platform'
import attachment, { attachmentId } from '@anticrm/attachment'
import { ViewAction } from '@anticrm/view'
export default mergeIds(attachmentId, attachment, {
string: {
@ -46,6 +47,12 @@ export default mergeIds(attachmentId, attachment, {
FileBrowserTypeFilterImages: '' as IntlString,
FileBrowserTypeFilterAudio: '' as IntlString,
FileBrowserTypeFilterVideos: '' as IntlString,
FileBrowserTypeFilterPDFs: '' as IntlString
FileBrowserTypeFilterPDFs: '' as IntlString,
AddAttachmentToSaved: '' as IntlString,
RemoveAttachmentFromSaved: '' as IntlString
},
actionImpl: {
AddAttachmentToSaved: '' as ViewAction,
DeleteAttachmentFromSaved: '' as ViewAction
}
})

View File

@ -28,6 +28,7 @@
"dependencies": {
"@anticrm/platform": "~0.6.5",
"@anticrm/ui": "~0.6.0",
"@anticrm/core": "~0.6.16"
"@anticrm/core": "~0.6.16",
"@anticrm/preference": "~0.6.0"
}
}

View File

@ -17,6 +17,7 @@
import type { AttachedDoc, Class, Doc, Ref, Space } from '@anticrm/core'
import type { Asset, Plugin } from '@anticrm/platform'
import { IntlString, plugin, Resource } from '@anticrm/platform'
import type { Preference } from '@anticrm/preference'
import { AnyComponent } from '@anticrm/ui'
/**
@ -35,6 +36,13 @@ export interface Attachment extends AttachedDoc {
*/
export interface Photo extends Attachment {}
/**
* @public
*/
export interface SavedAttachments extends Preference {
attachedTo: Ref<Attachment>
}
/**
* @public
*/
@ -52,7 +60,8 @@ export default plugin(attachmentId, {
},
class: {
Attachment: '' as Ref<Class<Attachment>>,
Photo: '' as Ref<Class<Photo>>
Photo: '' as Ref<Class<Photo>>,
SavedAttachments: '' as Ref<Class<SavedAttachments>>
},
helper: {
UploadFile: '' as Resource<(file: File, opts?: { space: Ref<Space>, attachedTo: Ref<Doc> }) => Promise<string>>,

View File

@ -55,6 +55,7 @@
"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.",
"SharedBy": "Shared by {name} {time}",
"LeaveChannel": "Leave channel",
"ChannelBrowser": "Channel browser"
}

View File

@ -54,6 +54,7 @@
"RemoveFromSaved": "Удалить из сохраненных",
"EmptySavedHeader": "Добавляйте сообщения и файлы, чтобы вернуться к ним позже",
"EmptySavedText": "Пометьте свои задачи или сохраните что-нибудь на потом. Только вы можете просматривать свои сохраненные объекты, поэтому используйте их как угодно.",
"SharedBy": "Доступ открыт {name} {time}",
"LeaveChannel": "Покинуть канал",
"ChannelBrowser": "Браузер каналов"
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import attachment from '@anticrm/attachment'
import attachment, { Attachment } from '@anticrm/attachment'
import type { ChunterMessage, Message } from '@anticrm/chunter'
import contact, { Employee } from '@anticrm/contact'
import core, { Doc, Ref, Space, WithLookup } from '@anticrm/core'
@ -26,7 +26,8 @@
export let space: Ref<Space> | undefined
export let pinnedIds: Ref<ChunterMessage>[]
export let savedIds: Ref<ChunterMessage>[]
export let savedMessagesIds: Ref<ChunterMessage>[]
export let savedAttachmentsIds: Ref<Attachment>[]
let div: HTMLDivElement | undefined
let autoscroll: boolean = false
@ -119,7 +120,8 @@
{employees}
on:openThread
isPinned={pinnedIds.includes(message._id)}
isSaved={savedIds.includes(message._id)}
isSaved={savedMessagesIds.includes(message._id)}
{savedAttachmentsIds}
/>
{/each}
{/if}

View File

@ -13,6 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import attachment, { Attachment } from '@anticrm/attachment'
import { AttachmentRefInput } from '@anticrm/attachment-resources'
import { ChunterMessage, Message, ChunterSpace } from '@anticrm/chunter'
import { generateId, getCurrentAccount, Ref, Space, TxFactory } from '@anticrm/core'
@ -99,10 +100,16 @@
{ limit: 1 }
)
const preferenceQuery = createQuery()
let savedIds: Ref<ChunterMessage>[] = []
preferenceQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedIds = res.map((r) => r.attachedTo)
const savedMessagesQuery = createQuery()
let savedMessagesIds: Ref<ChunterMessage>[] = []
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedMessagesIds = res.map((r) => r.attachedTo)
})
const savedAttachmentsQuery = createQuery()
let savedAttachmentsIds: Ref<Attachment>[] = []
savedAttachmentsQuery.query(attachment.class.SavedAttachments, {}, (res) => {
savedAttachmentsIds = res.map((r) => r.attachedTo)
})
</script>
@ -113,7 +120,8 @@
openThread(e.detail)
}}
{pinnedIds}
{savedIds}
{savedMessagesIds}
{savedAttachmentsIds}
/>
<div class="reference">
<AttachmentRefInput {space} {_class} objectId={_id} on:message={onMessage} />

View File

@ -25,7 +25,7 @@
import { Action } from '@anticrm/view'
import { getActions } from '@anticrm/view-resources'
import { createEventDispatcher } from 'svelte'
import { AddToSaved, DeleteFromSaved, UnpinMessage } from '../index'
import { AddMessageToSaved, DeleteMessageFromSaved, UnpinMessage } from '../index'
import chunter from '../plugin'
import { getTime } from '../utils'
// import Share from './icons/Share.svelte'
@ -37,6 +37,7 @@
export let message: WithLookup<ChunterMessage>
export let employees: Map<Ref<Employee>, Employee>
export let savedAttachmentsIds: Ref<Attachment>[]
export let thread: boolean = false
export let isPinned: boolean = false
export let isSaved: boolean = false
@ -86,7 +87,7 @@
action: async () => {
;(await client.findAll(chunter.class.ThreadMessage, { attachedTo: message._id as Ref<Message> })).forEach((c) => {
UnpinMessage(c)
DeleteFromSaved(c)
DeleteMessageFromSaved(c)
})
UnpinMessage(message)
await client.removeDoc(message._class, message.space, message._id)
@ -108,7 +109,7 @@
...actions.map((a) => ({
label: a.label,
icon: a.icon,
action: async (ctx:any, evt: MouseEvent) => {
action: async (ctx: any, evt: MouseEvent) => {
const impl = await getResource(a.action)
await impl(message, evt)
}
@ -145,8 +146,8 @@
}
function addToSaved () {
if (isSaved) DeleteFromSaved(message)
else AddToSaved(message)
if (isSaved) DeleteMessageFromSaved(message)
else AddMessageToSaved(message)
}
$: parentMessage = message as Message
@ -183,7 +184,11 @@
</div>
{:else}
<div class="text"><MessageViewer message={message.content} /></div>
{#if message.attachments}<div class="attachments"><AttachmentList {attachments} /></div>{/if}
{#if message.attachments}
<div class="attachments">
<AttachmentList {attachments} {savedAttachmentsIds} />
</div>
{/if}
{/if}
{#if reactions || (!thread && hasReplies)}
<div class="footer flex-col">

View File

@ -1,27 +1,63 @@
<script lang="ts">
import { createQuery } from '@anticrm/presentation'
import attachment, { Attachment } from '@anticrm/attachment'
import { createQuery, getClient } from '@anticrm/presentation'
import { ChunterMessage } from '@anticrm/chunter'
import { Ref } from '@anticrm/core'
import Message from './Message.svelte'
import contact, { Employee } from '@anticrm/contact'
import { getCurrentLocation, Label, navigate } from '@anticrm/ui'
import core, { Ref, WithLookup } from '@anticrm/core'
import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
import { getCurrentLocation, Label, navigate, 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'
let savedIds: Ref<ChunterMessage>[] = []
let savedMessages: ChunterMessage[] = []
const client = getClient()
let savedMessagesIds: Ref<ChunterMessage>[] = []
let savedMessages: WithLookup<ChunterMessage>[] = []
let savedAttachmentsIds: Ref<Attachment>[] = []
let savedAttachments: WithLookup<Attachment>[] = []
const messagesQuery = createQuery()
const preferenceQuery = createQuery()
const attachmentsQuery = createQuery()
const savedMessagesQuery = createQuery()
const savedAttachmentsQuery = createQuery()
preferenceQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedIds = res.map((r) => r.attachedTo)
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedMessagesIds = res.map((r) => r.attachedTo)
})
$: savedIds &&
messagesQuery.query(chunter.class.ChunterMessage, { _id: { $in: savedIds } }, (res) => {
savedMessages = res
})
savedAttachmentsQuery.query(attachment.class.SavedAttachments, {}, (res) => {
savedAttachmentsIds = res.map((r) => r.attachedTo)
})
$: savedMessagesIds &&
messagesQuery.query(
chunter.class.ChunterMessage,
{ _id: { $in: savedMessagesIds } },
(res) => {
savedMessages = res
},
{
lookup: {
_id: { attachments: attachment.class.Attachment },
createBy: core.class.Account
}
}
)
$: savedAttachmentsIds &&
attachmentsQuery.query(
attachment.class.Attachment,
{ _id: { $in: savedAttachmentsIds } },
(res) => {
savedAttachments = res
},
{
lookup: {
modifiedBy: core.class.Account
}
}
)
let employees: Map<Ref<Employee>, Employee> = new Map<Ref<Employee>, Employee>()
const employeeQuery = createQuery()
@ -52,7 +88,7 @@
function openMessage (message: ChunterMessage) {
const loc = getCurrentLocation()
if (message.attachedToClass === chunter.class.Channel) {
if (message.attachedToClass === chunter.class.ChunterSpace) {
loc.path.length = 3
loc.path[2] = message.attachedTo
} else if (message.attachedToClass === chunter.class.Message) {
@ -62,32 +98,57 @@
}
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)
})
}
function getName (a: WithLookup<Attachment>): string | undefined {
const name = (a.$lookup?.modifiedBy as EmployeeAccount).name
if (name !== undefined) {
return formatName(name)
}
}
</script>
{#if savedMessages.length > 0}
{#each savedMessages as message}
<div on:click={() => openMessage(message)}>
<Message
{message}
{employees}
on:openThread
thread
isPinned={pinnedIds.includes(message._id)}
isSaved={savedIds.includes(message._id)}
/>
<Scroller>
{#if savedMessages.length > 0 || savedAttachments.length > 0}
{#each savedMessages as message}
<div on:click={() => openMessage(message)}>
<Message
{message}
{employees}
on:openThread
thread
isPinned={pinnedIds.includes(message._id)}
isSaved={savedMessagesIds.includes(message._id)}
{savedAttachmentsIds}
/>
</div>
{/each}
{#each savedAttachments as att}
<div class="attachmentContainer" on:click={() => openAttachment(att)}>
<AttachmentPreview value={att} isSaved={true} />
<div class="label">
<Label label={chunter.string.SharedBy} params={{ name: getName(att), time: getTime(att.modifiedOn) }} />
</div>
</div>
{/each}
{:else}
<div class="empty">
<Bookmark size={'large'} />
<div class="an-element__label header">
<Label label={chunter.string.EmptySavedHeader} />
</div>
<span class="an-element__label">
<Label label={chunter.string.EmptySavedText} />
</span>
</div>
{/each}
{:else}
<div class="empty">
<Bookmark size={'large'} />
<div class="an-element__label header">
<Label label={chunter.string.EmptySavedHeader} />
</div>
<span class="an-element__label">
<Label label={chunter.string.EmptySavedText} />
</span>
</div>
{/if}
{/if}
</Scroller>
<style lang="scss">
.empty {
@ -105,4 +166,16 @@
font-weight: 600;
margin: 1rem;
}
.attachmentContainer {
padding: 2rem;
&:hover {
background-color: var(--board-card-bg-hover);
}
.label {
padding-top: 1rem;
}
}
</style>

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import attachment from '@anticrm/attachment'
import attachment, { Attachment } from '@anticrm/attachment'
import { AttachmentRefInput } from '@anticrm/attachment-resources'
import type { ChunterSpace, Message, ThreadMessage } from '@anticrm/chunter'
import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
@ -31,6 +31,7 @@
const query = createQuery()
const messageQuery = createQuery()
export let savedAttachmentsIds: Ref<Attachment>[]
export let _id: Ref<Message>
let parent: Message | undefined
let commentId = generateId() as Ref<ThreadMessage>
@ -181,7 +182,7 @@
</div>
<div class="flex-col content">
{#if parent}
<MsgView message={parent} {employees} thread />
<MsgView message={parent} {employees} thread {savedAttachmentsIds} />
{#if total > comments.length}
<div
class="label pb-2 pt-2 pl-8 over-underline"
@ -193,7 +194,7 @@
</div>
{/if}
{#each comments as comment (comment._id)}
<MsgView message={comment} {employees} thread />
<MsgView message={comment} {employees} thread {savedAttachmentsIds} />
{/each}
<div class="mr-4 ml-4 mb-4 mt-2">
<AttachmentRefInput

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import attachment from '@anticrm/attachment'
import attachment, { Attachment } from '@anticrm/attachment'
import { AttachmentRefInput } from '@anticrm/attachment-resources'
import type { ThreadMessage, Message, ChunterMessage } from '@anticrm/chunter'
import contact, { Employee } from '@anticrm/contact'
@ -123,11 +123,17 @@
))
)
const preferenceQuery = createQuery()
let savedIds: Ref<ChunterMessage>[] = []
const savedMessagesQuery = createQuery()
let savedMessagesIds: Ref<ChunterMessage>[] = []
preferenceQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedIds = res.map((r) => r.attachedTo)
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedMessagesIds = res.map((r) => r.attachedTo)
})
const savedAttachmentsQuery = createQuery()
let savedAttachmentsIds: Ref<Attachment>[] = []
savedAttachmentsQuery.query(attachment.class.SavedAttachments, {}, (res) => {
savedAttachmentsIds = res.map((r) => r.attachedTo)
})
async function onMessage (event: CustomEvent) {
@ -192,7 +198,7 @@
</div>
<div class="flex-col vScroll content" bind:this={div}>
{#if message}
<MsgView {message} {employees} thread />
<MsgView {message} {employees} thread isSaved={savedMessagesIds.includes(message._id)} {savedAttachmentsIds} />
{#if comments.length}
<ChannelSeparator title={chunter.string.RepliesCount} line params={{ replies: comments.length }} />
{/if}
@ -205,7 +211,8 @@
{employees}
thread
isPinned={pinnedIds.includes(comment._id)}
isSaved={savedIds.includes(comment._id)}
isSaved={savedMessagesIds.includes(comment._id)}
{savedAttachmentsIds}
/>
{/each}
{/if}

View File

@ -13,6 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import attachment, { Attachment } from '@anticrm/attachment'
import type { Message } from '@anticrm/chunter'
import { getCurrentAccount, Ref, SortingOrder } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
@ -40,6 +41,12 @@
}
}
)
const savedAttachmentsQuery = createQuery()
let savedAttachmentsIds: Ref<Attachment>[] = []
savedAttachmentsQuery.query(attachment.class.SavedAttachments, {}, (res) => {
savedAttachmentsIds = res.map((r) => r.attachedTo)
})
</script>
<div class="ac-header full divide">
@ -49,7 +56,7 @@
</div>
<Scroller>
{#each threads as thread (thread)}
<div class="item"><Thread _id={thread} /></div>
<div class="item"><Thread _id={thread} {savedAttachmentsIds} /></div>
{/each}
</Scroller>

View File

@ -17,6 +17,7 @@ import core from '@anticrm/core'
import chunter, { ChunterSpace, Channel, ChunterMessage, Message, ThreadMessage } from '@anticrm/chunter'
import { NotificationClientImpl } from '@anticrm/notification-resources'
import { Resources } from '@anticrm/platform'
import preference from '@anticrm/preference'
import { getClient, MessageBox } from '@anticrm/presentation'
import { getCurrentLocation, navigate, showPopup } from '@anticrm/ui'
import TxBacklinkCreate from './components/activity/TxBacklinkCreate.svelte'
@ -36,7 +37,6 @@ import EditChannel from './components/EditChannel.svelte'
import ThreadView from './components/ThreadView.svelte'
import Threads from './components/Threads.svelte'
import SavedMessages from './components/SavedMessages.svelte'
import preference from '@anticrm/preference'
import { getDmName } from './utils'
@ -135,7 +135,7 @@ async function UnarchiveChannel (channel: Channel): Promise<void> {
)
}
export async function AddToSaved (message: ChunterMessage): Promise<void> {
export async function AddMessageToSaved (message: ChunterMessage): Promise<void> {
const client = getClient()
await client.createDoc(chunter.class.SavedMessages, preference.space.Preference, {
@ -143,7 +143,7 @@ export async function AddToSaved (message: ChunterMessage): Promise<void> {
})
}
export async function DeleteFromSaved (message: ChunterMessage): Promise<void> {
export async function DeleteMessageFromSaved (message: ChunterMessage): Promise<void> {
const client = getClient()
const current = await client.findOne(chunter.class.SavedMessages, { attachedTo: message._id })

View File

@ -77,6 +77,7 @@ export default mergeIds(chunterId, chunter, {
RemoveFromSaved: '' as IntlString,
EmptySavedHeader: '' as IntlString,
EmptySavedText: '' as IntlString,
SharedBy: '' as IntlString,
LeaveChannel: '' as IntlString,
ChannelBrowser: '' as IntlString
}