mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Chunter: saved attachments (#1515)
Signed-off-by: budaeva <irina.budaeva@xored.com>
This commit is contained in:
parent
25bc560b8b
commit
e5c64849ed
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -16,9 +16,9 @@
|
||||
import activity from '@anticrm/activity'
|
||||
import type {
|
||||
Backlink,
|
||||
ChunterSpace,
|
||||
Channel,
|
||||
ChunterMessage,
|
||||
ChunterSpace,
|
||||
Comment,
|
||||
Message,
|
||||
SavedMessages,
|
||||
|
@ -36,6 +36,8 @@
|
||||
"FileBrowserTypeFilterImages": "Images",
|
||||
"FileBrowserTypeFilterAudio": "Audio",
|
||||
"FileBrowserTypeFilterVideos": "Videos",
|
||||
"FileBrowserTypeFilterPDFs": "PDFs"
|
||||
"FileBrowserTypeFilterPDFs": "PDFs",
|
||||
"AddAttachmentToSaved": "Add attachment to saved",
|
||||
"RemoveAttachmentFromSaved": "Remove attachment from saved"
|
||||
}
|
||||
}
|
@ -36,6 +36,8 @@
|
||||
"FileBrowserTypeFilterImages": "Изображения",
|
||||
"FileBrowserTypeFilterAudio": "Звук",
|
||||
"FileBrowserTypeFilterVideos": "Видео",
|
||||
"FileBrowserTypeFilterPDFs": "PDF-файлы"
|
||||
"FileBrowserTypeFilterPDFs": "PDF-файлы",
|
||||
"AddAttachmentToSaved": "Добавить вложение в сохраненные",
|
||||
"RemoveAttachmentFromSaved": "Удалить вложение из сохраненных"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -54,6 +54,7 @@
|
||||
"RemoveFromSaved": "Удалить из сохраненных",
|
||||
"EmptySavedHeader": "Добавляйте сообщения и файлы, чтобы вернуться к ним позже",
|
||||
"EmptySavedText": "Пометьте свои задачи или сохраните что-нибудь на потом. Только вы можете просматривать свои сохраненные объекты, поэтому используйте их как угодно.",
|
||||
"SharedBy": "Доступ открыт {name} {time}",
|
||||
"LeaveChannel": "Покинуть канал",
|
||||
"ChannelBrowser": "Браузер каналов"
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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} />
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 })
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user