Merge remote-tracking branch 'origin/develop'

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2024-07-29 16:35:04 +07:00
21 changed files with 316 additions and 119 deletions
+3 -3
View File
@@ -158,7 +158,7 @@ services:
limits:
memory: 1024M
print:
image: hardcoreeng/uberflow-print
image: hardcoreeng/print
restart: unless-stopped
ports:
- 4005:4005
@@ -171,7 +171,7 @@ services:
limits:
memory: 300M
sign:
image: hardcoreeng/uberflow-sign
image: hardcoreeng/sign
restart: unless-stopped
ports:
- 4006:4006
@@ -193,7 +193,7 @@ services:
limits:
memory: 300M
analytics:
image: hardcoreeng/uberflow-analytics-collector
image: hardcoreeng/analytics-collector
restart: unless-stopped
ports:
- 4077:4007
@@ -43,6 +43,7 @@
export let checkForHeaders: boolean = false
export let stickedScrollBars: boolean = false
export let thinScrollBars: boolean = false
export let disableOverscroll = false
export let onScroll: ((params: ScrollParams) => void) | undefined = undefined
export let onResize: (() => void) | undefined = undefined
@@ -542,6 +543,7 @@
onResize?.()
}}
class="scroll relative flex-shrink"
class:disableOverscroll
style:overflow-x={horizontal ? 'auto' : 'hidden'}
on:scroll={() => {
if (onScroll) {
@@ -838,6 +840,9 @@
height: 100%;
overflow-y: auto;
&.disableOverscroll {
overscroll-behavior: none;
}
&::-webkit-scrollbar:vertical {
width: 0;
}
@@ -60,7 +60,6 @@ interface IChannelDataProvider {
datesStore: Readable<Timestamp[]>
metadataStore: Readable<MessageMetadata[]>
loadMore: (mode: LoadMode, loadAfter: Timestamp) => Promise<void>
canLoadMore: (mode: LoadMode, loadAfter: Timestamp) => boolean
jumpToDate: (date: Timestamp) => Promise<void>
}
@@ -95,10 +94,27 @@ export class ChannelDataProvider implements IChannelDataProvider {
([initialLoaded, tailLoading]) => !initialLoaded || tailLoading
)
private readonly backwardNextStore = writable<Chunk | undefined>(undefined)
private readonly forwardNextStore = writable<Chunk | undefined>(undefined)
private backwardNextPromise: Promise<void> | undefined = undefined
private forwardNextPromise: Promise<void> | undefined = undefined
private readonly isBackwardLoading = writable(false)
private readonly isForwardLoading = writable(false)
private nextChunkAdding = false
public messagesStore = derived([this.chunksStore, this.tailStore], ([chunks, tail]) => {
return [...chunks.map(({ data }) => data).flat(), ...tail]
})
public canLoadNextForwardStore = derived([this.messagesStore, this.forwardNextStore], ([messages, forwardNext]) => {
if (forwardNext !== undefined) return false
return this.canLoadMore('forward', messages[messages.length - 1]?.createdOn)
})
constructor (
chatId: Ref<Doc>,
_class: Ref<Class<ActivityMessage>>,
@@ -139,20 +155,15 @@ export class ChannelDataProvider implements IChannelDataProvider {
private clearData (): void {
this.metadataStore.set([])
this.tailStore.set([])
this.chunksStore.set([])
this.isInitialLoadingStore.set(false)
this.isInitialLoadedStore.set(false)
this.isTailLoading.set(false)
this.datesStore.set([])
this.newTimestampStore.set(undefined)
this.isLoadingMoreStore.set(false)
this.tailStart = undefined
this.chatId = undefined
this.selectedMsgId = undefined
this.clearMessages()
}
private async loadData (loadAll = false): Promise<void> {
@@ -211,9 +222,13 @@ export class ChannelDataProvider implements IChannelDataProvider {
this.isTailLoading.set(true)
const tailStart = metadata[startIndex]?.createdOn
this.loadTail(tailStart)
this.backwardNextPromise = this.loadNext('backward', metadata[startIndex]?.createdOn, this.limit)
} else {
const newStart = Math.max(startPosition - this.limit / 2, 0)
await this.loadMore('forward', metadata[newStart]?.createdOn, this.limit)
if (newStart > 0) {
this.backwardNextPromise = this.loadNext('backward', metadata[newStart]?.createdOn, this.limit)
}
}
this.isInitialLoadingStore.set(false)
@@ -260,41 +275,28 @@ export class ChannelDataProvider implements IChannelDataProvider {
)
}
public async loadMore (mode: LoadMode, loadAfter?: Timestamp, limit?: number): Promise<void> {
if (this.chatId === undefined || loadAfter === undefined) {
return
}
isNextLoading (mode: LoadMode): boolean {
return mode === 'forward' ? get(this.isForwardLoading) : get(this.isBackwardLoading)
}
if (!this.canLoadMore(mode, loadAfter) || get(this.isLoadingMoreStore)) {
return
}
isNextLoaded (mode: LoadMode): boolean {
return mode === 'forward' ? get(this.forwardNextStore) !== undefined : get(this.backwardNextStore) !== undefined
}
this.isLoadingMoreStore.set(true)
setNextLoading (mode: LoadMode, value: boolean): void {
mode === 'forward' ? this.isForwardLoading.set(value) : this.isBackwardLoading.set(value)
}
const isBackward = mode === 'backward'
const isForward = mode === 'forward'
getTailStartIndex (metadata: MessageMetadata[], loadAfter: Timestamp): number {
const index = metadata.slice(-this.limit - 1).findIndex(({ createdOn }) => createdOn === loadAfter)
const chunks = get(this.chunksStore)
const tail = get(this.tailStore)
const lastChunk: Chunk | undefined = isBackward ? chunks[0] : chunks[chunks.length - 1]
const skipIds = (lastChunk?.data ?? [])
.concat(tail)
.filter(({ createdOn }) => createdOn === loadAfter)
.map(({ _id }) => _id) as Array<Ref<ChatMessage>>
if (isForward) {
const metadata = get(this.metadataStore)
const metaIndex = metadata.findIndex(({ createdOn }) => createdOn === loadAfter)
const shouldLoadTail = metaIndex >= 0 && metaIndex + this.limit >= metadata.length
if (shouldLoadTail) {
this.loadTail(metadata[metaIndex + 1]?.createdOn, undefined, { _id: { $nin: skipIds } })
this.isLoadingMoreStore.set(false)
return
}
}
return index !== -1 ? metadata.length - index : -1
}
async loadChunk (isBackward: boolean, loadAfter: Timestamp, limit?: number): Promise<Chunk | undefined> {
const client = getClient()
const skipIds = this.getChunkSkipIds(loadAfter)
const messages = await client.findAll(
chunter.class.ChatMessage,
{
@@ -312,20 +314,149 @@ export class ChannelDataProvider implements IChannelDataProvider {
)
if (messages.length === 0) {
this.isLoadingMoreStore.set(false)
return
}
const from = isBackward ? messages[0] : messages[messages.length - 1]
const to = isBackward ? messages[messages.length - 1] : messages[0]
const chunk: Chunk = {
return {
from: from.createdOn ?? from.modifiedOn,
to: to.createdOn ?? to.modifiedOn,
data: isBackward ? messages.reverse() : messages
}
}
getChunkSkipIds (after: Timestamp, loadTail = false): Array<Ref<ChatMessage>> {
const chunks = get(this.chunksStore)
const metadata = get(this.metadataStore)
const tail = get(this.tailStore)
const tailData = tail.length > 0 ? get(this.tailStore) : metadata.slice(-this.limit)
return chunks
.filter(({ to, from }) => from >= after || to <= after)
.map(({ data }) => data as MessageMetadata[])
.flat()
.concat(loadTail ? [] : tailData)
.filter(({ createdOn }) => createdOn === after)
.map(({ _id }) => _id) as Array<Ref<ChatMessage>>
}
async loadNext (mode: LoadMode, loadAfter?: Timestamp, limit?: number): Promise<void> {
if (this.chatId === undefined || loadAfter === undefined) {
return
}
if (this.isNextLoading(mode) || this.isNextLoaded(mode)) {
return
}
if (!this.canLoadMore(mode, loadAfter)) {
return
}
this.setNextLoading(mode, true)
const isBackward = mode === 'backward'
const isForward = mode === 'forward'
const metadata = get(this.metadataStore)
if (isForward && this.getTailStartIndex(metadata, loadAfter) !== -1) {
this.setNextLoading(mode, false)
return
}
const chunk = await this.loadChunk(isBackward, loadAfter, limit)
if (chunk !== undefined && isBackward) {
this.backwardNextStore.set(chunk)
}
if (chunk !== undefined && isForward) {
this.forwardNextStore.set(chunk)
}
this.setNextLoading(mode, false)
}
public async addNextChunk (mode: LoadMode, loadAfter?: Timestamp, limit?: number): Promise<void> {
if (loadAfter === undefined || this.nextChunkAdding) {
return
}
this.nextChunkAdding = true
if (this.forwardNextPromise instanceof Promise && mode === 'forward') {
await this.forwardNextPromise
this.forwardNextPromise = undefined
}
if (this.backwardNextPromise instanceof Promise && mode === 'backward') {
await this.backwardNextPromise
this.backwardNextPromise = undefined
}
if (this.isNextLoaded(mode)) {
const next = mode === 'forward' ? get(this.forwardNextStore) : get(this.backwardNextStore)
if (next !== undefined) {
if (mode === 'forward') {
this.forwardNextStore.set(undefined)
this.chunksStore.set([...get(this.chunksStore), next])
this.forwardNextPromise = this.loadNext('forward', next.from, limit)
} else {
this.backwardNextStore.set(undefined)
this.chunksStore.set([next, ...get(this.chunksStore)])
this.backwardNextPromise = this.loadNext('backward', next.to, limit)
}
}
} else {
await this.loadMore(mode, loadAfter, limit)
}
this.nextChunkAdding = false
}
private async loadMore (mode: LoadMode, loadAfter?: Timestamp, limit?: number): Promise<void> {
if (get(this.isLoadingMoreStore) || loadAfter === undefined) {
return
}
if (!this.canLoadMore(mode, loadAfter)) {
return
}
this.isLoadingMoreStore.set(true)
const isBackward = mode === 'backward'
const isForward = mode === 'forward'
const chunks = get(this.chunksStore)
const metadata = get(this.metadataStore)
if (isForward) {
const index = this.getTailStartIndex(metadata, loadAfter)
const tailAfter = metadata[index]?.createdOn
if (tailAfter !== undefined) {
const skipIds = chunks[chunks.length - 1]?.data.map(({ _id }) => _id) ?? []
this.loadTail(tailAfter, undefined, { _id: { $nin: skipIds } })
this.isLoadingMoreStore.set(false)
return
}
}
const chunk = await this.loadChunk(isBackward, loadAfter, limit)
if (chunk !== undefined) {
this.chunksStore.set(isBackward ? [chunk, ...chunks] : [...chunks, chunk])
if (isBackward) {
this.forwardNextPromise = this.loadNext('backward', chunk.to, limit)
} else {
this.forwardNextPromise = this.loadNext('forward', chunk.from, limit)
}
}
this.chunksStore.set(isBackward ? [chunk, ...chunks] : [...chunks, chunk])
this.isLoadingMoreStore.set(false)
}
@@ -426,6 +557,12 @@ export class ChannelDataProvider implements IChannelDataProvider {
this.isInitialLoadedStore.set(false)
this.tailQuery.unsubscribe()
this.tailStart = undefined
this.backwardNextPromise = undefined
this.forwardNextPromise = undefined
this.forwardNextStore.set(undefined)
this.backwardNextStore.set(undefined)
this.isBackwardLoading.set(false)
this.isForwardLoading.set(false)
}
public async jumpToDate (date: Timestamp): Promise<void> {
@@ -67,6 +67,7 @@
const dateSelectorHeight = 30
const headerHeight = 52
const minMsgHeightRem = 2
const loadMoreThreshold = 40
const client = getClient()
const hierarchy = client.getHierarchy()
@@ -244,7 +245,7 @@
return false
}
return scrollElement.scrollTop === 0
return scrollElement.scrollTop <= loadMoreThreshold
}
function shouldLoadMoreDown (): boolean {
@@ -254,10 +255,11 @@
const { scrollHeight, scrollTop, clientHeight } = scrollElement
return scrollHeight - Math.ceil(scrollTop + clientHeight) <= 0
return scrollHeight - Math.ceil(scrollTop + clientHeight) <= loadMoreThreshold
}
let scrollToRestore = 0
let backwardRequested = false
function loadMore (): void {
if (!loadMoreAllowed || $isLoadingMoreStore || !scrollElement || isInitialScrolling) {
@@ -268,18 +270,24 @@
const maxMsgPerScreen = Math.ceil(scrollElement.clientHeight / minMsgHeightPx)
const limit = Math.max(maxMsgPerScreen, provider.limit)
if (shouldLoadMoreUp() && scrollElement && provider.canLoadMore('backward', messages[0]?.createdOn)) {
if (!shouldLoadMoreUp()) {
backwardRequested = false
}
if (shouldLoadMoreUp() && !backwardRequested) {
shouldScrollToNew = false
scrollToRestore = scrollElement.scrollHeight
void provider.loadMore('backward', messages[0]?.createdOn, limit)
} else if (shouldLoadMoreDown() && provider.canLoadMore('forward', messages[messages.length - 1]?.createdOn)) {
scrollToRestore = scrollElement?.scrollHeight ?? 0
provider.addNextChunk('backward', messages[0]?.createdOn, limit)
backwardRequested = true
} else if (shouldLoadMoreDown()) {
scrollToRestore = 0
shouldScrollToNew = false
void provider.loadMore('forward', messages[messages.length - 1]?.createdOn, limit)
isScrollAtBottom = false
provider.addNextChunk('forward', messages[messages.length - 1]?.createdOn, limit)
}
}
function handleScroll ({ autoScrolling }: ScrollParams): void {
async function handleScroll ({ autoScrolling }: ScrollParams): Promise<void> {
saveScrollPosition()
updateDownButtonVisibility($metadataStore, displayMessages, scrollElement)
if (autoScrolling) {
@@ -668,6 +676,8 @@
scrollToBottom()
}
}
const canLoadNextForwardStore = provider.canLoadNextForwardStore
</script>
{#if isLoading}
@@ -694,10 +704,11 @@
bind:divScroll={scrollElement}
bind:divBox={scrollContentBox}
noStretch={false}
disableOverscroll
onScroll={handleScroll}
onResize={handleResize}
>
{#if loadMoreAllowed && provider.canLoadMore('backward', messages[0]?.createdOn)}
{#if loadMoreAllowed}
<HistoryLoading isLoading={$isLoadingMoreStore} />
{/if}
<slot name="header" />
@@ -736,7 +747,7 @@
/>
{/each}
{#if loadMoreAllowed && provider.canLoadMore('forward', messages[messages.length - 1]?.createdOn)}
{#if loadMoreAllowed && $canLoadNextForwardStore}
<HistoryLoading isLoading={$isLoadingMoreStore} />
{/if}
</Scroller>
+1 -2
View File
@@ -20,8 +20,7 @@
"Upload": "Upload",
"CreateDrive": "Create Drive",
"CreateFolder": "Create Folder",
"UploadFile": "Upload File",
"UploadFolder": "Upload Folder",
"UploadFile": "Upload Files",
"EditDrive": "Edit Drive",
"Rename": "Rename",
"Restore": "Restore",
+1 -2
View File
@@ -20,8 +20,7 @@
"Upload": "Subir",
"CreateDrive": "Crear unidad",
"CreateFolder": "Crear carpeta",
"UploadFile": "Subir archivo",
"UploadFolder": "Subir carpeta",
"UploadFile": "Subir archivos",
"EditDrive": "Editar unidad",
"Rename": "Renombrar",
"Restore": "Restaurar",
+1 -2
View File
@@ -20,8 +20,7 @@
"Upload": "Téléverser",
"CreateDrive": "Créer un disque",
"CreateFolder": "Créer un dossier",
"UploadFile": "Télécharger un fichier",
"UploadFolder": "Télécharger un dossier",
"UploadFile": "Télécharger des fichiers",
"EditDrive": "Modifier le disque",
"Rename": "Renommer",
"Restore": "Restaurer",
+1 -2
View File
@@ -20,8 +20,7 @@
"Upload": "Carregar",
"CreateDrive": "Criar unidade",
"CreateFolder": "Criar pasta",
"UploadFile": "Carregar ficheiro",
"UploadFolder": "Carregar pasta",
"UploadFile": "Carregar ficheiros",
"EditDrive": "Editar unidade",
"Rename": "Renomear",
"Restore": "Restaurar",
+1 -2
View File
@@ -20,8 +20,7 @@
"Upload": "Загрузить",
"CreateDrive": "Создать диск",
"CreateFolder": "Создать папку",
"UploadFile": "Загрузить файл",
"UploadFolder": "Загрузить папку",
"UploadFile": "Загрузить файлы",
"EditDrive": "Редактировать",
"Rename": "Переименовать",
"Restore": "Восстановить",
-1
View File
@@ -21,7 +21,6 @@
"CreateDrive": "创建磁盘",
"CreateFolder": "创建文件夹",
"UploadFile": "上传文件",
"UploadFolder": "上传文件夹",
"EditDrive": "编辑磁盘",
"Rename": "重命名",
"Restore": "恢复",
@@ -14,22 +14,19 @@
-->
<script lang="ts">
import { AccountRole, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
import { createFile, type Drive } from '@hcengineering/drive'
import { setPlatformStatus, unknownError } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { type Drive } from '@hcengineering/drive'
import { createQuery } from '@hcengineering/presentation'
import { Button, ButtonWithDropdown, IconAdd, IconDropdown, Loading, SelectPopupValueType } from '@hcengineering/ui'
import { showFilesUploadPopup } from '@hcengineering/uploader'
import drive from '../plugin'
import { getFolderIdFromFragment } from '../navigation'
import { showCreateDrivePopup, showCreateFolderPopup } from '../utils'
import { showCreateDrivePopup, showCreateFolderPopup, uploadFilesToDrivePopup } from '../utils'
export let currentSpace: Ref<Drive> | undefined
export let currentFragment: string | undefined
const me = getCurrentAccount()
const client = getClient()
const query = createQuery()
let loading = true
@@ -66,27 +63,7 @@
async function handleUploadFile (): Promise<void> {
if (currentSpace !== undefined) {
const space = currentSpace
const target =
parent !== drive.ids.Root
? { objectId: parent, objectClass: drive.class.Folder }
: { objectId: space, objectClass: drive.class.Drive }
await showFilesUploadPopup(target, {}, async (uuid, name, file, path, metadata) => {
try {
const data = {
file: uuid,
size: file.size,
type: file.type,
lastModified: file instanceof File ? file.lastModified : Date.now(),
name,
metadata
}
await createFile(client, space, parent, data)
} catch (err) {
void setPlatformStatus(unknownError(err))
}
})
await uploadFilesToDrivePopup(currentSpace, parent)
}
}
@@ -95,12 +72,10 @@
{ id: drive.string.CreateDrive, label: drive.string.CreateDrive, icon: drive.icon.Drive },
{ id: drive.string.CreateFolder, label: drive.string.CreateFolder, icon: drive.icon.Folder },
{ id: drive.string.UploadFile, label: drive.string.UploadFile, icon: drive.icon.File }
// { id: drive.string.UploadFolder, label: drive.string.UploadFolder }
]
: [
{ id: drive.string.CreateFolder, label: drive.string.CreateFolder, icon: drive.icon.Folder },
{ id: drive.string.UploadFile, label: drive.string.UploadFile, icon: drive.icon.File }
// { id: drive.string.UploadFolder, label: drive.string.UploadFolder }
]
</script>
@@ -73,6 +73,7 @@
maxNumberOfFiles: 1,
hideProgress: true
},
{},
async (uuid, name, file, path, metadata) => {
const data = {
file: uuid,
-1
View File
@@ -21,7 +21,6 @@ export default mergeIds(driveId, drive, {
CreateDrive: '' as IntlString,
CreateFolder: '' as IntlString,
UploadFile: '' as IntlString,
UploadFolder: '' as IntlString,
Download: '' as IntlString,
Upload: '' as IntlString,
EditDrive: '' as IntlString,
+42 -9
View File
@@ -19,7 +19,12 @@ import drive, { createFile } from '@hcengineering/drive'
import { type Asset, setPlatformStatus, unknownError } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { type AnySvelteComponent, showPopup } from '@hcengineering/ui'
import { uploadFiles } from '@hcengineering/uploader'
import {
type FileUploadCallback,
getDataTransferFiles,
showFilesUploadPopup,
uploadFiles
} from '@hcengineering/uploader'
import { openDoc } from '@hcengineering/view-resources'
import CreateDrive from './components/CreateDrive.svelte'
@@ -154,7 +159,38 @@ export async function resolveParents (object: Resource): Promise<Doc[]> {
return parents.reverse()
}
export async function uploadFilesToDrive (files: DataTransfer, space: Ref<Drive>, parent: Ref<Folder>): Promise<void> {
export async function uploadFilesToDrive (dt: DataTransfer, space: Ref<Drive>, parent: Ref<Folder>): Promise<void> {
const files = await getDataTransferFiles(dt)
const onFileUploaded = await fileUploadCallback(space, parent)
const target =
parent !== drive.ids.Root
? { objectId: parent, objectClass: drive.class.Folder }
: { objectId: space, objectClass: drive.class.Drive }
await uploadFiles(files, target, {}, onFileUploaded)
}
export async function uploadFilesToDrivePopup (space: Ref<Drive>, parent: Ref<Folder>): Promise<void> {
const onFileUploaded = await fileUploadCallback(space, parent)
const target =
parent !== drive.ids.Root
? { objectId: parent, objectClass: drive.class.Folder }
: { objectId: space, objectClass: drive.class.Drive }
await showFilesUploadPopup(
target,
{},
{
fileManagerSelectionType: 'both'
},
onFileUploaded
)
}
async function fileUploadCallback (space: Ref<Drive>, parent: Ref<Folder>): Promise<FileUploadCallback> {
const client = getClient()
const query = parent !== drive.ids.Root ? { space, path: parent } : { space }
@@ -190,12 +226,7 @@ export async function uploadFilesToDrive (files: DataTransfer, space: Ref<Drive>
return current
}
const target =
parent !== drive.ids.Root
? { objectId: parent, objectClass: drive.class.Folder }
: { objectId: space, objectClass: drive.class.Drive }
await uploadFiles(files, target, {}, async (uuid, name, file, path, metadata) => {
const callback: FileUploadCallback = async (uuid, name, file, path, metadata) => {
const folder = await findParent(path)
try {
const data = {
@@ -211,5 +242,7 @@ export async function uploadFilesToDrive (files: DataTransfer, space: Ref<Drive>
} catch (err) {
void setPlatformStatus(unknownError(err))
}
})
}
return callback
}
@@ -14,6 +14,7 @@
-->
<script lang="ts">
import { themeStore } from '@hcengineering/ui'
import { type FileUploadPopupOptions } from '@hcengineering/uploader'
import { type Uppy } from '@uppy/core'
import Dashboard from '@uppy/dashboard'
@@ -25,6 +26,7 @@
const dispatch = createEventDispatcher()
export let uppy: Uppy<any, any>
export let options: FileUploadPopupOptions
let container: HTMLElement
@@ -46,7 +48,8 @@
width: 750,
disableInformer: true,
proudlyDisplayPoweredByUppy: false,
theme: dark ? 'dark' : 'light'
theme: dark ? 'dark' : 'light',
fileManagerSelectionType: options.fileManagerSelectionType
})
})
@@ -79,7 +79,7 @@
class="container flex-row-center flex-gap-2 active"
class:error={state.error}
on:click={handleClick}
use:tooltip={state.error != null ? { label: getEmbeddedLabel(state.error) } : undefined}
use:tooltip={state.error !== undefined ? { label: getEmbeddedLabel(state.error) } : undefined}
>
{#if state.error}
<IconError size={'small'} fill={'var(--negative-button-default)'} />
@@ -70,6 +70,10 @@
}
}
function handleCancelAll (): void {
upload.uppy.cancelAll()
}
function handleCancelFile (file: UppyFile<any, any>): void {
upload.uppy.removeFile(file.id)
}
@@ -123,6 +127,17 @@
noUnderline
/>
</div>
{#if state.error}
<Button
kind={'icon'}
icon={IconClose}
iconProps={{ size: 'small' }}
showTooltip={{ label: uploader.string.Cancel }}
on:click={() => {
handleCancelAll()
}}
/>
{/if}
</div>
<Scroller>
<div class="upload-popup__content flex-col flex-no-shrink flex-gap-4">
@@ -209,6 +224,8 @@
.upload-popup__header {
padding-bottom: 1rem;
margin-left: 0.5rem;
margin-right: 0.625rem;
}
.upload-popup__content {
+5 -5
View File
@@ -17,8 +17,8 @@ import { showPopup } from '@hcengineering/ui'
import {
type FileUploadCallback,
type FileUploadOptions,
type FileUploadPopupOptions,
type FileUploadTarget,
getDataTransferFiles,
toFileWithPath
} from '@hcengineering/uploader'
@@ -31,11 +31,12 @@ import { getUppy } from './uppy'
export async function showFilesUploadPopup (
target: FileUploadTarget,
options: FileUploadOptions,
popupOptions: FileUploadPopupOptions,
onFileUploaded: FileUploadCallback
): Promise<void> {
const uppy = getUppy(options, onFileUploaded)
showPopup(FileUploadPopup, { uppy, target }, undefined, (res) => {
showPopup(FileUploadPopup, { uppy, target, options: popupOptions }, undefined, (res) => {
if (res === true && options.hideProgress !== true) {
dockFileUpload(target, uppy)
}
@@ -44,13 +45,12 @@ export async function showFilesUploadPopup (
/** @public */
export async function uploadFiles (
files: File[] | FileList | DataTransfer,
files: File[] | FileList,
target: FileUploadTarget,
options: FileUploadOptions,
onFileUploaded: FileUploadCallback
): Promise<void> {
const items =
files instanceof DataTransfer ? await getDataTransferFiles(files) : Array.from(files, (p) => toFileWithPath(p))
const items = Array.from(files, (p) => toFileWithPath(p))
if (items.length === 0) return
+7 -1
View File
@@ -24,12 +24,13 @@ export interface FileWithPath extends File {
export type UploadFilesPopupFn = (
target: FileUploadTarget,
options: FileUploadOptions,
popupOptions: FileUploadPopupOptions,
onFileUploaded: FileUploadCallback
) => Promise<void>
/** @public */
export type UploadFilesFn = (
files: File[] | FileList | DataTransfer,
files: File[] | FileList,
target: FileUploadTarget,
options: FileUploadOptions,
onFileUploaded: FileUploadCallback
@@ -49,6 +50,11 @@ export interface FileUploadOptions {
hideProgress?: boolean
}
/** @public */
export interface FileUploadPopupOptions {
fileManagerSelectionType?: 'files' | 'folders' | 'both'
}
/** @public */
export type FileUploadCallback = (
uuid: Ref<PlatformBlob>,
+13 -3
View File
@@ -16,21 +16,28 @@
import { getResource } from '@hcengineering/platform'
import uploader from './plugin'
import type { FileUploadCallback, FileUploadOptions, FileUploadTarget, FileWithPath } from './types'
import type {
FileUploadCallback,
FileUploadOptions,
FileUploadPopupOptions,
FileUploadTarget,
FileWithPath
} from './types'
/** @public */
export async function showFilesUploadPopup (
target: FileUploadTarget,
options: FileUploadOptions,
popupOptions: FileUploadPopupOptions,
onFileUploaded: FileUploadCallback
): Promise<void> {
const fn = await getResource(uploader.function.ShowFilesUploadPopup)
await fn(target, options, onFileUploaded)
await fn(target, options, popupOptions, onFileUploaded)
}
/** @public */
export async function uploadFiles (
files: File[] | FileList | DataTransfer,
files: File[] | FileList,
target: FileUploadTarget,
options: FileUploadOptions,
onFileUploaded: FileUploadCallback
@@ -63,6 +70,9 @@ export async function getDataTransferFiles (dataTransfer: DataTransfer): Promise
/** @public */
export function toFileWithPath (file: File, path?: string): FileWithPath {
const { webkitRelativePath } = file
if ('relativePath' in file) {
return file as FileWithPath
}
Object.defineProperty(file, 'relativePath', {
value:
typeof path === 'string'
+11 -5
View File
@@ -81,9 +81,9 @@ import {
type Document,
type Filter,
type FindCursor,
type FindOptions as MongoFindOptions,
type Sort,
type UpdateFilter,
type FindOptions as MongoFindOptions
type UpdateFilter
} from 'mongodb'
import { DBCollectionHelper, getMongoClient, getWorkspaceDB, type MongoClientReference } from './utils'
@@ -1477,8 +1477,10 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
@withContext('get-model')
async getModel (ctx: MeasureContext): Promise<Tx[]> {
const cursor = await ctx.with('find', {}, async () =>
this.db.collection<Tx>(DOMAIN_TX).find(
const txCollection = this.db.collection<Tx>(DOMAIN_TX)
const exists = await txCollection.indexExists('objectSpace_fi_1__id_fi_1_modifiedOn_fi_1')
const cursor = await ctx.with('find', {}, async () => {
let c = txCollection.find(
{ objectSpace: core.space.Model },
{
sort: {
@@ -1490,7 +1492,11 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
}
}
)
)
if (exists) {
c = c.hint({ objectSpace: 1, _id: 1, modifiedOn: 1 })
}
return c
})
const model = await ctx.with('to-array', {}, async () => await toArray<Tx>(cursor))
// We need to put all core.account.System transactions first
const systemTx: Tx[] = []