mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-27 19:00:02 +00:00
refactor attachments
Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
parent
7cdaaf9224
commit
b3e88daf3c
@ -14,8 +14,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Tx, TxCreateDoc, Data, Ref, Doc, TxFactory } from '@anticrm/core'
|
import type { Tx, TxCreateDoc, Data, Ref, Doc, TxFactory, Class } from '@anticrm/core'
|
||||||
import type { Message, Backlink } from '@anticrm/chunter'
|
import type { FindAll } from '@anticrm/server-core'
|
||||||
|
import type { Message, Backlink, Attachment } from '@anticrm/chunter'
|
||||||
|
|
||||||
import core from '@anticrm/core'
|
import core from '@anticrm/core'
|
||||||
import chunter from '@anticrm/chunter'
|
import chunter from '@anticrm/chunter'
|
||||||
@ -26,6 +27,7 @@ function extractBacklinks (backlinkId: Ref<Doc>, message: string, kids: NodeList
|
|||||||
if (kid.nodeName === 'span') {
|
if (kid.nodeName === 'span') {
|
||||||
result.push({
|
result.push({
|
||||||
attachedTo: kid.getAttribute('data-id') as Ref<Doc>,
|
attachedTo: kid.getAttribute('data-id') as Ref<Doc>,
|
||||||
|
attachedToClass: kid.getAttribute('data-class') as Ref<Class<Doc>>,
|
||||||
backlinkId,
|
backlinkId,
|
||||||
backlinkClass: chunter.class.Message,
|
backlinkClass: chunter.class.Message,
|
||||||
message
|
message
|
||||||
@ -57,9 +59,30 @@ export async function OnMessage (tx: Tx, txFactory: TxFactory): Promise<Tx[]> {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WithAttachements extends Doc {
|
||||||
|
attachments: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function OnAttachment (tx: Tx, txFactory: TxFactory, findAll: FindAll<Doc>): Promise<Tx[]> {
|
||||||
|
if (tx._class === core.class.TxCreateDoc) {
|
||||||
|
const createTx = tx as TxCreateDoc<Attachment>
|
||||||
|
if (createTx.objectClass === chunter.class.Attachment) {
|
||||||
|
const _id = createTx.attributes.attachedTo as Ref<WithAttachements>
|
||||||
|
const _class = createTx.attributes.attachedToClass as Ref<Class<WithAttachements>>
|
||||||
|
const attachedTo = (await findAll(_class, { _id }))[0]
|
||||||
|
return [txFactory.createTxUpdateDoc(_class, attachedTo.space, _id, { $inc: { attachments: 1 } })]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
trigger: {
|
trigger: {
|
||||||
OnMessage
|
OnMessage,
|
||||||
|
OnAttachment
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ import type { IntlString } from '@anticrm/platform'
|
|||||||
import { Builder, Model, Prop, UX, TypeString, Index } from '@anticrm/model'
|
import { Builder, Model, Prop, UX, TypeString, Index } from '@anticrm/model'
|
||||||
import type { Ref, Doc, Class, Domain } from '@anticrm/core'
|
import type { Ref, Doc, Class, Domain } from '@anticrm/core'
|
||||||
import { IndexKind } from '@anticrm/core'
|
import { IndexKind } from '@anticrm/core'
|
||||||
import core, { TSpace, TDoc, TObj } from '@anticrm/model-core'
|
import core, { TSpace, TDoc } from '@anticrm/model-core'
|
||||||
import type { Backlink, Channel, Message, Comment, Attachment } from '@anticrm/chunter'
|
import type { Backlink, Channel, Message, Comment, Attachment } from '@anticrm/chunter'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ export class TMessage extends TDoc implements Message {
|
|||||||
@Model(chunter.class.Comment, core.class.Doc, DOMAIN_COMMENT)
|
@Model(chunter.class.Comment, core.class.Doc, DOMAIN_COMMENT)
|
||||||
export class TComment extends TDoc implements Comment {
|
export class TComment extends TDoc implements Comment {
|
||||||
attachedTo!: Ref<Doc>
|
attachedTo!: Ref<Doc>
|
||||||
|
attachedToClass!: Ref<Class<Doc>>
|
||||||
@Prop(TypeString(), 'Message' as IntlString)
|
@Prop(TypeString(), 'Message' as IntlString)
|
||||||
@Index(IndexKind.FullText)
|
@Index(IndexKind.FullText)
|
||||||
message!: string
|
message!: string
|
||||||
@ -55,8 +56,10 @@ export class TBacklink extends TComment implements Backlink {
|
|||||||
backlinkClass!: Ref<Class<Doc>>
|
backlinkClass!: Ref<Class<Doc>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(chunter.class.Attachment, core.class.Obj, DOMAIN_ATTACHMENT)
|
@Model(chunter.class.Attachment, core.class.Doc, DOMAIN_ATTACHMENT)
|
||||||
export class TAttachment extends TObj implements Attachment {
|
export class TAttachment extends TDoc implements Attachment {
|
||||||
|
attachedTo!: Ref<Doc>
|
||||||
|
attachedToClass!: Ref<Class<Doc>>
|
||||||
name!: string
|
name!: string
|
||||||
file!: string
|
file!: string
|
||||||
size!: number
|
size!: number
|
||||||
|
@ -44,8 +44,7 @@ export function createDemo (builder: Builder): void {
|
|||||||
provider: contact.channelProvider.Email,
|
provider: contact.channelProvider.Email,
|
||||||
value: 'andrey@hc.engineering'
|
value: 'andrey@hc.engineering'
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
attachments: {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.createDoc(recruit.class.Candidate, recruit.space.CandidatesPublic, {
|
builder.createDoc(recruit.class.Candidate, recruit.space.CandidatesPublic, {
|
||||||
@ -57,8 +56,7 @@ export function createDemo (builder: Builder): void {
|
|||||||
provider: contact.channelProvider.Email,
|
provider: contact.channelProvider.Email,
|
||||||
value: 'marina@hc.engineering'
|
value: 'marina@hc.engineering'
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
attachments: {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.createDoc(recruit.class.Candidate, recruit.space.CandidatesPublic, {
|
builder.createDoc(recruit.class.Candidate, recruit.space.CandidatesPublic, {
|
||||||
@ -70,7 +68,6 @@ export function createDemo (builder: Builder): void {
|
|||||||
provider: contact.channelProvider.Email,
|
provider: contact.channelProvider.Email,
|
||||||
value: 'alex@hc.engineering'
|
value: 'alex@hc.engineering'
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
attachments: {}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,8 @@ export class TCandidate extends TPerson implements Candidate {
|
|||||||
@Prop(TypeString(), 'Title' as IntlString)
|
@Prop(TypeString(), 'Title' as IntlString)
|
||||||
title?: string
|
title?: string
|
||||||
|
|
||||||
@Prop(TypeBag(), 'Attachments' as IntlString)
|
@Prop(TypeString(), 'Attachments' as IntlString)
|
||||||
attachments!: Bag<Attachment>
|
attachments?: number
|
||||||
|
|
||||||
@Prop(TypeString(), 'Applications' as IntlString)
|
@Prop(TypeString(), 'Applications' as IntlString)
|
||||||
applications?: number
|
applications?: number
|
||||||
|
@ -24,4 +24,7 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
trigger: serverChunter.trigger.OnMessage
|
trigger: serverChunter.trigger.OnMessage
|
||||||
})
|
})
|
||||||
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
|
trigger: serverChunter.trigger.OnAttachment
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ export interface UXObject extends Obj {
|
|||||||
*/
|
*/
|
||||||
export interface AttachedDoc extends Doc {
|
export interface AttachedDoc extends Doc {
|
||||||
attachedTo: Ref<Doc>
|
attachedTo: Ref<Doc>
|
||||||
|
attachedToClass: Ref<Class<Doc>>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Bag } from '@anticrm/core'
|
import type { Doc } from '@anticrm/core'
|
||||||
import type { Attachment } from '@anticrm/chunter'
|
import type { Attachment } from '@anticrm/chunter'
|
||||||
import { IconFile, Link, Tooltip, IconAttachment } from '@anticrm/ui'
|
import { IconFile, Link, Tooltip, IconAttachment } from '@anticrm/ui'
|
||||||
import { PDFViewer } from '@anticrm/presentation'
|
import { PDFViewer } from '@anticrm/presentation'
|
||||||
import AttachmentPopup from './AttachmentPopup.svelte'
|
import AttachmentPopup from './AttachmentPopup.svelte'
|
||||||
|
|
||||||
export let value: { attachments: Bag<Attachment> }
|
export let value: Doc & { attachments?: number }
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -33,10 +33,10 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{/if} -->
|
{/if} -->
|
||||||
|
|
||||||
{#if value.attachments && Object.keys(value.attachments).length > 0}
|
{#if value.attachments && value.attachments > 0}
|
||||||
<Tooltip label={'Attachments (' + Object.values(value.attachments).length + ')'} component={AttachmentPopup} props={{ files: value.attachments }}>
|
<Tooltip label={'Attachments (' + value.attachments + ')'} component={AttachmentPopup} props={{ files: value._id }}>
|
||||||
<div class="sm-tool-icon">
|
<div class="sm-tool-icon">
|
||||||
<span class="icon"><IconAttachment size="small"/></span>{Object.keys(value.attachments).length}
|
<span class="icon"><IconAttachment size="small"/></span>{value.attachments}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
import { plugin } from '@anticrm/platform'
|
import { plugin } from '@anticrm/platform'
|
||||||
import type { Asset, Plugin } from '@anticrm/platform'
|
import type { Asset, Plugin } from '@anticrm/platform'
|
||||||
import type { Space, Obj, Doc, Ref, Class, AttachedDoc } from '@anticrm/core'
|
import type { Space, Doc, Ref, Class, AttachedDoc } from '@anticrm/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -47,7 +47,7 @@ export interface Backlink extends Comment {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface Attachment extends Obj {
|
export interface Attachment extends AttachedDoc {
|
||||||
name: string
|
name: string
|
||||||
file: string
|
file: string
|
||||||
size: number
|
size: number
|
||||||
|
@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
import chunter from '@anticrm/chunter'
|
import chunter from '@anticrm/chunter'
|
||||||
|
|
||||||
export let objectId: Ref<Doc & { attachments: Bag<Attachment> }>
|
export let objectId: Ref<Doc>
|
||||||
export let space: Ref<Space>
|
export let space: Ref<Space>
|
||||||
export let _class: Ref<Class<Doc & { attachments: Bag<Attachment> }>>
|
export let _class: Ref<Class<Doc>>
|
||||||
|
|
||||||
export let object: Doc & { attachments: Bag<Attachment> } | undefined = undefined
|
let attachments: Attachment[] = []
|
||||||
|
|
||||||
// const query = createQuery()
|
const query = createQuery()
|
||||||
// $: query.query(_class, { _id: objectId }, result => { object = result[0] })
|
$: query.query(chunter.class.Attachment, { attachedTo: objectId }, result => { attachments = result })
|
||||||
|
|
||||||
let inputFile: HTMLInputElement
|
let inputFile: HTMLInputElement
|
||||||
let loading = false
|
let loading = false
|
||||||
@ -45,8 +45,9 @@
|
|||||||
try {
|
try {
|
||||||
const uuid = await uploadFile(space, file, objectId)
|
const uuid = await uploadFile(space, file, objectId)
|
||||||
console.log('uploaded file uuid', uuid)
|
console.log('uploaded file uuid', uuid)
|
||||||
client.putBag(_class, space, objectId, 'attachments', encodeURIComponent(uuid), {
|
client.createDoc(chunter.class.Attachment, space, {
|
||||||
_class: chunter.class.Attachment,
|
attachedTo: objectId,
|
||||||
|
attachedToClass: _class,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
file: uuid,
|
file: uuid,
|
||||||
type: file.type,
|
type: file.type,
|
||||||
@ -81,7 +82,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
|
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
|
||||||
</div>
|
</div>
|
||||||
{#if object?.attachments !== undefined}
|
|
||||||
<table class="table-body">
|
<table class="table-body">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="tr-head">
|
<tr class="tr-head">
|
||||||
@ -90,7 +90,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each Object.values(object.attachments) as file}
|
{#each attachments as file}
|
||||||
<tr class="tr-body">
|
<tr class="tr-body">
|
||||||
<td class="item flex-row-center">
|
<td class="item flex-row-center">
|
||||||
<div class="flex-center file-icon">pdf</div>
|
<div class="flex-center file-icon">pdf</div>
|
||||||
@ -104,7 +104,6 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -34,7 +34,7 @@ export interface Candidates extends Space {}
|
|||||||
*/
|
*/
|
||||||
export interface Candidate extends Person {
|
export interface Candidate extends Person {
|
||||||
title?: string
|
title?: string
|
||||||
attachments: Bag<Attachment>
|
attachments?: number
|
||||||
applications?: number
|
applications?: number
|
||||||
onsite?: boolean
|
onsite?: boolean
|
||||||
remote?: boolean
|
remote?: boolean
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Tx, TxCreateDoc, Data, Ref, Doc, TxFactory } from '@anticrm/core'
|
import type { Tx, TxCreateDoc, Data, Ref, Doc, TxFactory, Class } from '@anticrm/core'
|
||||||
import type { Message, Backlink } from '@anticrm/chunter'
|
import type { FindAll } from '@anticrm/server-core'
|
||||||
|
import type { Message, Backlink, Attachment } from '@anticrm/chunter'
|
||||||
import { parse, Node, HTMLElement } from 'node-html-parser'
|
import { parse, Node, HTMLElement } from 'node-html-parser'
|
||||||
|
|
||||||
import core from '@anticrm/core'
|
import core from '@anticrm/core'
|
||||||
@ -27,6 +28,7 @@ function extractBacklinks (backlinkId: Ref<Doc>, message: string, kids: Node[]):
|
|||||||
if ((kid as HTMLElement).localName === 'span') {
|
if ((kid as HTMLElement).localName === 'span') {
|
||||||
result.push({
|
result.push({
|
||||||
attachedTo: (kid as HTMLElement).getAttribute('data-id') as Ref<Doc>,
|
attachedTo: (kid as HTMLElement).getAttribute('data-id') as Ref<Doc>,
|
||||||
|
attachedToClass: (kid as HTMLElement).getAttribute('data-class') as Ref<Class<Doc>>,
|
||||||
backlinkId,
|
backlinkId,
|
||||||
backlinkClass: chunter.class.Message,
|
backlinkClass: chunter.class.Message,
|
||||||
message
|
message
|
||||||
@ -57,9 +59,30 @@ export async function OnMessage (tx: Tx, txFactory: TxFactory): Promise<Tx[]> {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WithAttachements extends Doc {
|
||||||
|
attachments: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function OnAttachment (tx: Tx, txFactory: TxFactory, findAll: FindAll<Doc>): Promise<Tx[]> {
|
||||||
|
if (tx._class === core.class.TxCreateDoc) {
|
||||||
|
const createTx = tx as TxCreateDoc<Attachment>
|
||||||
|
if (createTx.objectClass === chunter.class.Attachment) {
|
||||||
|
const _id = createTx.attributes.attachedTo as Ref<WithAttachements>
|
||||||
|
const _class = createTx.attributes.attachedToClass as Ref<Class<WithAttachements>>
|
||||||
|
const attachedTo = (await findAll(_class, { _id }))[0]
|
||||||
|
return [txFactory.createTxUpdateDoc(_class, attachedTo.space, _id, { $inc: { attachments: 1 } })]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
trigger: {
|
trigger: {
|
||||||
OnMessage
|
OnMessage,
|
||||||
|
OnAttachment
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -28,6 +28,7 @@ export const serverChunterId = 'server-chunter' as Plugin
|
|||||||
*/
|
*/
|
||||||
export default plugin(serverChunterId, {
|
export default plugin(serverChunterId, {
|
||||||
trigger: {
|
trigger: {
|
||||||
OnMessage: '' as Resource<TriggerFunc>
|
OnMessage: '' as Resource<TriggerFunc>,
|
||||||
|
OnAttachment: '' as Resource<TriggerFunc>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user