mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-18 14:23:36 +00:00
Chunter: file browser additional fixes (#1547)
Signed-off-by: Ruslan Izhitsky <ruslan.izhitskiy@xored.com>
This commit is contained in:
parent
469ac85e99
commit
ca2b5ba0f3
@ -334,7 +334,10 @@ export function createModel (builder: Builder): void {
|
|||||||
id: 'fileBrowser',
|
id: 'fileBrowser',
|
||||||
label: attachment.string.FileBrowser,
|
label: attachment.string.FileBrowser,
|
||||||
icon: attachment.icon.FileBrowser,
|
icon: attachment.icon.FileBrowser,
|
||||||
component: attachment.component.FileBrowser
|
component: attachment.component.FileBrowser,
|
||||||
|
componentProps: {
|
||||||
|
requestedSpaceClasses: [chunter.class.Channel, chunter.class.DirectMessage]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
spaces: [
|
spaces: [
|
||||||
|
@ -8,15 +8,18 @@
|
|||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
"NotSelected": "Not selected",
|
"NotSelected": "Not selected",
|
||||||
"Deselect": "Deselect",
|
"Deselect": "Deselect",
|
||||||
|
"Archived": "(archived)",
|
||||||
"AddSocialLinks": "Add social links",
|
"AddSocialLinks": "Add social links",
|
||||||
"EditSocialLinks": "Edit social links",
|
"EditSocialLinks": "Edit social links",
|
||||||
"Change": "Change",
|
"Change": "Change",
|
||||||
"Remove": "Remove",
|
"Remove": "Remove",
|
||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"Search": "Search...",
|
"Search": "Search...",
|
||||||
|
"Spaces": "Spaces",
|
||||||
"Unassigned": "Unassigned",
|
"Unassigned": "Unassigned",
|
||||||
"CreateMore": "Create more",
|
"CreateMore": "Create more",
|
||||||
"NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}",
|
"NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}",
|
||||||
|
"NumberSpaces": "{count, plural, =0 {In} =1 {In 1 place} other {In # places}}",
|
||||||
"InThis": "In this {space}",
|
"InThis": "In this {space}",
|
||||||
"NoMatchesInThis": "No matches in this {space}",
|
"NoMatchesInThis": "No matches in this {space}",
|
||||||
"NoMatchesFound": "No matches found",
|
"NoMatchesFound": "No matches found",
|
||||||
|
@ -8,15 +8,18 @@
|
|||||||
"Close": "Закрыть",
|
"Close": "Закрыть",
|
||||||
"NotSelected": "Не выбрано",
|
"NotSelected": "Не выбрано",
|
||||||
"Deselect": "Снять выделение",
|
"Deselect": "Снять выделение",
|
||||||
|
"Archived": "(архивирован)",
|
||||||
"AddSocialLinks": "Добавить контактную информацию",
|
"AddSocialLinks": "Добавить контактную информацию",
|
||||||
"EditSocialLinks": "Редактировать контактную информацию",
|
"EditSocialLinks": "Редактировать контактную информацию",
|
||||||
"Change": "Изменить",
|
"Change": "Изменить",
|
||||||
"Remove": "Удалить",
|
"Remove": "Удалить",
|
||||||
"Members": "Участники",
|
"Members": "Участники",
|
||||||
"Search": "Поиск...",
|
"Search": "Поиск...",
|
||||||
|
"Spaces": "Пространства",
|
||||||
"Unassigned": "Не назначен",
|
"Unassigned": "Не назначен",
|
||||||
"CreateMore": "Создать еще",
|
"CreateMore": "Создать еще",
|
||||||
"NumberMembers": "{count, plural, =0 {нет участников} =1 {1 участник} other {# участника}}",
|
"NumberMembers": "{count, plural, =0 {нет участников} =1 {1 участник} other {# участника}}",
|
||||||
|
"NumberSpaces": "{count, plural, =0 {В} =1 {В 1 месте} other {В # местах}}",
|
||||||
"InThis": "В этом {space}",
|
"InThis": "В этом {space}",
|
||||||
"NoMatchesInThis": "В этом {space} совпадения не обнаружены",
|
"NoMatchesInThis": "В этом {space} совпадения не обнаружены",
|
||||||
"NoMatchesFound": "Не найдено соответсвий",
|
"NoMatchesFound": "Не найдено соответсвий",
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { IconFolder } from '@anticrm/ui'
|
import { IconFolder, Label } from '@anticrm/ui'
|
||||||
|
|
||||||
import type { Space } from '@anticrm/core'
|
import type { Space } from '@anticrm/core'
|
||||||
|
import presentation from '..'
|
||||||
|
|
||||||
export let value: Space
|
export let value: Space
|
||||||
export let subtitle: string | undefined = undefined
|
export let subtitle: string | undefined = undefined
|
||||||
@ -27,7 +28,12 @@
|
|||||||
<div class="flex-center {size} caption-color flex-no-shrink"><IconFolder size={'small'} /></div>
|
<div class="flex-center {size} caption-color flex-no-shrink"><IconFolder size={'small'} /></div>
|
||||||
<div class="flex-col ml-2 min-w-0">
|
<div class="flex-col ml-2 min-w-0">
|
||||||
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
|
||||||
<div class="content-accent-color overflow-label">{value.name}</div>
|
<div class="content-accent-color overflow-label">
|
||||||
|
{value.name}
|
||||||
|
{#if value.archived}
|
||||||
|
<Label label={presentation.string.Archived}/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import core, { Class, Ref, Space } from '@anticrm/core'
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
import { translate } from '@anticrm/platform'
|
||||||
|
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
||||||
|
import { Tooltip, showPopup, Button } from '@anticrm/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import presentation, { SpacesMultiPopup } from '..'
|
||||||
|
|
||||||
|
export let selectedItems: Ref<Space>[] = []
|
||||||
|
export let _classes: Ref<Class<Space>>[] = []
|
||||||
|
export let label: IntlString
|
||||||
|
|
||||||
|
export let kind: ButtonKind = 'no-border'
|
||||||
|
export let size: ButtonSize = 'small'
|
||||||
|
export let justify: 'left' | 'center' = 'center'
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
async function addSpace (evt: Event): Promise<void> {
|
||||||
|
showPopup(
|
||||||
|
SpacesMultiPopup,
|
||||||
|
{
|
||||||
|
_classes,
|
||||||
|
label,
|
||||||
|
selectedSpaces: selectedItems
|
||||||
|
},
|
||||||
|
evt.target as HTMLElement,
|
||||||
|
() => { },
|
||||||
|
(result) => {
|
||||||
|
if (result !== undefined) {
|
||||||
|
selectedItems = result
|
||||||
|
dispatch('update', selectedItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Tooltip {label} fill={width === '100%'} direction={labelDirection}>
|
||||||
|
<Button
|
||||||
|
label={selectedItems.length === 0 ? presentation.string.Spaces : undefined}
|
||||||
|
width={width ?? 'min-content'}
|
||||||
|
{kind} {size} {justify}
|
||||||
|
on:click={addSpace}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
{#if selectedItems.length > 0}
|
||||||
|
<div class="flex-row-center flex-nowrap">
|
||||||
|
{#await translate(presentation.string.NumberSpaces, { count: selectedItems.length }) then text}
|
||||||
|
<span class="ml-1-5">{text}</span>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
114
packages/presentation/src/components/SpacesMultiPopup.svelte
Normal file
114
packages/presentation/src/components/SpacesMultiPopup.svelte
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020 Anticrm Platform Contributors.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
import { translate } from '@anticrm/platform'
|
||||||
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
|
|
||||||
|
import core, { Class, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||||
|
import { Tooltip, CheckBox, Label } from '@anticrm/ui'
|
||||||
|
|
||||||
|
import { createQuery } from '../utils'
|
||||||
|
import presentation from '..'
|
||||||
|
import SpaceInfo from './SpaceInfo.svelte';
|
||||||
|
|
||||||
|
export let _classes: Ref<Class<Space>>[] = []
|
||||||
|
export let allowDeselect: boolean = false
|
||||||
|
export let titleDeselect: IntlString | undefined = undefined
|
||||||
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
|
export let selected: Ref<Space> | undefined
|
||||||
|
export let selectedSpaces: Ref<Space>[] = []
|
||||||
|
|
||||||
|
let searchQuery: string = ''
|
||||||
|
let spaces: Space[] = []
|
||||||
|
let shownSpaces: Space[] = []
|
||||||
|
let input: HTMLInputElement
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const query = createQuery()
|
||||||
|
const myAccId = getCurrentAccount()._id
|
||||||
|
|
||||||
|
$: query.query<Space>(
|
||||||
|
core.class.Space,
|
||||||
|
{
|
||||||
|
name: { $like: '%' + searchQuery + '%' },
|
||||||
|
_class: { $in: _classes },
|
||||||
|
},
|
||||||
|
result => {
|
||||||
|
spaces = result
|
||||||
|
},
|
||||||
|
{ limit: 200 }
|
||||||
|
)
|
||||||
|
|
||||||
|
$: update(spaces)
|
||||||
|
const update = (spaces_: Space[]) => {
|
||||||
|
shownSpaces = spaces_.filter((sp) => {
|
||||||
|
// don't show archived unless search is specified or this space is selected
|
||||||
|
// show private only if it includes the current user
|
||||||
|
return (!sp.archived || searchQuery || selectedSpaces.includes(sp._id)) && (!sp.private || sp.members.includes(myAccId))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let phTraslate: string = ''
|
||||||
|
$: if (placeholder) translate(placeholder, {}).then(res => { phTraslate = res })
|
||||||
|
|
||||||
|
const isSelected = (space: Space): boolean => {
|
||||||
|
if (selectedSpaces.filter(s => s === space._id).length > 0) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkSelected = (space: Space): void => {
|
||||||
|
if (isSelected(space)) {
|
||||||
|
selectedSpaces = selectedSpaces.filter(s => s !== space._id)
|
||||||
|
} else {
|
||||||
|
selectedSpaces.push(space._id)
|
||||||
|
}
|
||||||
|
spaces = spaces
|
||||||
|
dispatch('update', selectedSpaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => { if (input) input.focus() })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="selectPopup">
|
||||||
|
<div class="header">
|
||||||
|
<input bind:this={input} type='text' bind:value={searchQuery} placeholder={phTraslate} on:change/>
|
||||||
|
</div>
|
||||||
|
<div class="scroll">
|
||||||
|
<div class="box">
|
||||||
|
{#each shownSpaces as space}
|
||||||
|
<button class="menu-item" on:click={() => {
|
||||||
|
checkSelected(space)
|
||||||
|
}}>
|
||||||
|
<div class="check pointer-events-none">
|
||||||
|
<CheckBox checked={isSelected(space)} primary />
|
||||||
|
</div>
|
||||||
|
<SpaceInfo size={'medium'} value={space} />
|
||||||
|
{#if allowDeselect && space._id === selected}
|
||||||
|
<div class="check-right pointer-events-none">
|
||||||
|
{#if titleDeselect}
|
||||||
|
<Tooltip label={titleDeselect ?? presentation.string.Deselect}>
|
||||||
|
<CheckBox checked circle primary />
|
||||||
|
</Tooltip>
|
||||||
|
{:else}
|
||||||
|
<CheckBox checked circle primary />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -29,6 +29,8 @@ export { default as MessageBox } from './components/MessageBox.svelte'
|
|||||||
export { default as MessageViewer } from './components/MessageViewer.svelte'
|
export { default as MessageViewer } from './components/MessageViewer.svelte'
|
||||||
export { default as PDFViewer } from './components/PDFViewer.svelte'
|
export { default as PDFViewer } from './components/PDFViewer.svelte'
|
||||||
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
|
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
|
||||||
|
export { default as SpacesMultiPopup } from './components/SpacesMultiPopup.svelte'
|
||||||
|
export { default as SpaceMultiBoxList } from './components/SpaceMultiBoxList.svelte'
|
||||||
export { default as SpaceSelect } from './components/SpaceSelect.svelte'
|
export { default as SpaceSelect } from './components/SpaceSelect.svelte'
|
||||||
export { default as UserBox } from './components/UserBox.svelte'
|
export { default as UserBox } from './components/UserBox.svelte'
|
||||||
export { default as UserBoxList } from './components/UserBoxList.svelte'
|
export { default as UserBoxList } from './components/UserBoxList.svelte'
|
||||||
|
@ -37,15 +37,18 @@ export default plugin(presentationId, {
|
|||||||
Close: '' as IntlString,
|
Close: '' as IntlString,
|
||||||
NotSelected: '' as IntlString,
|
NotSelected: '' as IntlString,
|
||||||
Deselect: '' as IntlString,
|
Deselect: '' as IntlString,
|
||||||
|
Archived: '' as IntlString,
|
||||||
AddSocialLinks: '' as IntlString,
|
AddSocialLinks: '' as IntlString,
|
||||||
EditSocialLinks: '' as IntlString,
|
EditSocialLinks: '' as IntlString,
|
||||||
Change: '' as IntlString,
|
Change: '' as IntlString,
|
||||||
Remove: '' as IntlString,
|
Remove: '' as IntlString,
|
||||||
Members: '' as IntlString,
|
Members: '' as IntlString,
|
||||||
Search: '' as IntlString,
|
Search: '' as IntlString,
|
||||||
|
Spaces: '' as IntlString,
|
||||||
Unassigned: '' as IntlString,
|
Unassigned: '' as IntlString,
|
||||||
CreateMore: '' as IntlString,
|
CreateMore: '' as IntlString,
|
||||||
NumberMembers: '' as IntlString,
|
NumberMembers: '' as IntlString,
|
||||||
|
NumberSpaces: '' as IntlString,
|
||||||
InThis: '' as IntlString,
|
InThis: '' as IntlString,
|
||||||
NoMatchesInThis: '' as IntlString,
|
NoMatchesInThis: '' as IntlString,
|
||||||
NoMatchesFound: '' as IntlString,
|
NoMatchesFound: '' as IntlString,
|
||||||
|
@ -21,10 +21,13 @@
|
|||||||
"FileBrowserFilterIn": "In",
|
"FileBrowserFilterIn": "In",
|
||||||
"FileBrowserFilterDate": "Date",
|
"FileBrowserFilterDate": "Date",
|
||||||
"FileBrowserFilterFileType": "File type",
|
"FileBrowserFilterFileType": "File type",
|
||||||
|
"FileBrowserSort": "Sort:",
|
||||||
"FileBrowserSortNewest": "Newest file",
|
"FileBrowserSortNewest": "Newest file",
|
||||||
"FileBrowserSortOldest": "Oldest file",
|
"FileBrowserSortOldest": "Oldest file",
|
||||||
"FileBrowserSortAZ": "A to Z",
|
"FileBrowserSortAZ": "A to Z",
|
||||||
"FileBrowserSortZA": "Z to A",
|
"FileBrowserSortZA": "Z to A",
|
||||||
|
"FileBrowserSortSmallest": "Smallest file",
|
||||||
|
"FileBrowserSortBiggest": "Biggest file",
|
||||||
"FileBrowserDateFilterAny": "Any time",
|
"FileBrowserDateFilterAny": "Any time",
|
||||||
"FileBrowserDateFilterToday": "Today",
|
"FileBrowserDateFilterToday": "Today",
|
||||||
"FileBrowserDateFilterYesterday": "Yesterday",
|
"FileBrowserDateFilterYesterday": "Yesterday",
|
||||||
|
@ -21,10 +21,13 @@
|
|||||||
"FileBrowserFilterIn": "В",
|
"FileBrowserFilterIn": "В",
|
||||||
"FileBrowserFilterDate": "Дата",
|
"FileBrowserFilterDate": "Дата",
|
||||||
"FileBrowserFilterFileType": "Тип файла",
|
"FileBrowserFilterFileType": "Тип файла",
|
||||||
|
"FileBrowserSort": "Сортировка:",
|
||||||
"FileBrowserSortNewest": "Самый новый файл",
|
"FileBrowserSortNewest": "Самый новый файл",
|
||||||
"FileBrowserSortOldest": "Самый старый файл",
|
"FileBrowserSortOldest": "Самый старый файл",
|
||||||
"FileBrowserSortAZ": "От А до Я",
|
"FileBrowserSortAZ": "От А до Я",
|
||||||
"FileBrowserSortZA": "От Я до А",
|
"FileBrowserSortZA": "От Я до А",
|
||||||
|
"FileBrowserSortSmallest": "Самый маленький файл",
|
||||||
|
"FileBrowserSortBiggest": "Самый большой файл",
|
||||||
"FileBrowserDateFilterAny": "В любое время",
|
"FileBrowserDateFilterAny": "В любое время",
|
||||||
"FileBrowserDateFilterToday": "Сегодня",
|
"FileBrowserDateFilterToday": "Сегодня",
|
||||||
"FileBrowserDateFilterYesterday": "Вчера",
|
"FileBrowserDateFilterYesterday": "Вчера",
|
||||||
|
@ -16,127 +16,46 @@
|
|||||||
import { Attachment } from '@anticrm/attachment'
|
import { Attachment } from '@anticrm/attachment'
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import contact, { Employee } from '@anticrm/contact'
|
||||||
import { EmployeeAccount } from '@anticrm/contact'
|
import { EmployeeAccount } from '@anticrm/contact'
|
||||||
import { Doc, getCurrentAccount, Ref, SortingOrder, SortingQuery, Space } from '@anticrm/core'
|
import core, { Class, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { getClient, UserBoxList } from '@anticrm/presentation'
|
import ui, {
|
||||||
import { DropdownLabelsIntl, IconMoreV, Label, Menu as UIMenu, showPopup } from '@anticrm/ui'
|
getCurrentLocation,
|
||||||
|
location,
|
||||||
|
IconMoreV,
|
||||||
|
IconSearch,
|
||||||
|
Label,
|
||||||
|
showPopup,
|
||||||
|
navigate,
|
||||||
|
EditWithIcon,
|
||||||
|
Spinner
|
||||||
|
} from '@anticrm/ui'
|
||||||
import { Menu } from '@anticrm/view-resources'
|
import { Menu } from '@anticrm/view-resources'
|
||||||
import { AttachmentPresenter } from '..'
|
import { onDestroy } from 'svelte'
|
||||||
|
import {
|
||||||
|
AttachmentPresenter,
|
||||||
|
FileBrowserSortMode,
|
||||||
|
dateFileBrowserFilters,
|
||||||
|
fileTypeFileBrowserFilters,
|
||||||
|
sortModeToOptionObject
|
||||||
|
} from '..'
|
||||||
import attachment from '../plugin'
|
import attachment from '../plugin'
|
||||||
|
import FileBrowserFilters from './FileBrowserFilters.svelte'
|
||||||
enum SortMode {
|
import FileBrowserSortMenu from './FileBrowserSortMenu.svelte'
|
||||||
NewestFile,
|
|
||||||
OldestFile,
|
|
||||||
AscendingAlphabetical,
|
|
||||||
DescendingAlphabetical
|
|
||||||
}
|
|
||||||
|
|
||||||
const msInDay = 24 * 60 * 60 * 1000
|
|
||||||
const getBeginningOfToday = () => {
|
|
||||||
const date = new Date()
|
|
||||||
date.setUTCHours(0, 0, 0, 0)
|
|
||||||
return date.getTime()
|
|
||||||
}
|
|
||||||
const dateObjects = [
|
|
||||||
{
|
|
||||||
id: 'dateAny',
|
|
||||||
label: attachment.string.FileBrowserDateFilterAny,
|
|
||||||
getDate: () => {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dateToday',
|
|
||||||
label: attachment.string.FileBrowserDateFilterToday,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dateYesterday',
|
|
||||||
label: attachment.string.FileBrowserDateFilterYesterday,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() - msInDay, $lt: getBeginningOfToday() }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'date7Days',
|
|
||||||
label: attachment.string.FileBrowserDateFilter7Days,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() - msInDay * 6 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'date30Days',
|
|
||||||
label: attachment.string.FileBrowserDateFilter30Days,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() - msInDay * 29 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'date3Months',
|
|
||||||
label: attachment.string.FileBrowserDateFilter3Months,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() - msInDay * 90 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'date12Months',
|
|
||||||
label: attachment.string.FileBrowserDateFilter12Months,
|
|
||||||
getDate: () => {
|
|
||||||
return { $gte: getBeginningOfToday() - msInDay * 364 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const fileTypeObjects = [
|
|
||||||
{
|
|
||||||
id: 'typeAny',
|
|
||||||
label: attachment.string.FileBrowserTypeFilterAny,
|
|
||||||
getType: () => {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'typeImage',
|
|
||||||
label: attachment.string.FileBrowserTypeFilterImages,
|
|
||||||
getType: () => {
|
|
||||||
return { $like: '%image/%' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'typeAudio',
|
|
||||||
label: attachment.string.FileBrowserTypeFilterAudio,
|
|
||||||
getType: () => {
|
|
||||||
return { $like: '%audio/%' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'typeVideo',
|
|
||||||
label: attachment.string.FileBrowserTypeFilterVideos,
|
|
||||||
getType: () => {
|
|
||||||
return { $like: '%video/%' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'typePDF',
|
|
||||||
label: attachment.string.FileBrowserTypeFilterPDFs,
|
|
||||||
getType: () => {
|
|
||||||
return 'application/pdf'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
export let space: Space | undefined
|
const loc = getCurrentLocation()
|
||||||
|
const spaceId: Ref<Space> | undefined = loc.query?.spaceId as Ref<Space> | undefined
|
||||||
|
export let requestedSpaceClasses: Ref<Class<Space>>[] = []
|
||||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||||
let participants: Ref<Employee>[] = [currentUser.employee]
|
let selectedParticipants: Ref<Employee>[] = [currentUser.employee]
|
||||||
const assignee: Ref<Employee> | null = null
|
let selectedSpaces: Ref<Space>[] = []
|
||||||
|
let searchQuery: string = ''
|
||||||
|
let isLoading = false
|
||||||
|
|
||||||
let attachments: Attachment[] = []
|
let attachments: Attachment[] = []
|
||||||
let selectedFileNumber: number | undefined
|
let selectedFileNumber: number | undefined
|
||||||
|
|
||||||
let selectedSort: SortMode = SortMode.NewestFile
|
let selectedSort: FileBrowserSortMode = FileBrowserSortMode.NewestFile
|
||||||
let selectedDateId = 'dateAny'
|
let selectedDateId = 'dateAny'
|
||||||
let selectedFileTypeId = 'typeAny'
|
let selectedFileTypeId = 'typeAny'
|
||||||
|
|
||||||
@ -147,135 +66,89 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSortMenu = async (ev: Event): Promise<void> => {
|
$: fetch(searchQuery, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
|
||||||
showPopup(
|
|
||||||
UIMenu,
|
|
||||||
{
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: sortModeToString(SortMode.NewestFile),
|
|
||||||
action: () => {
|
|
||||||
selectedSort = SortMode.NewestFile
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: sortModeToString(SortMode.OldestFile),
|
|
||||||
action: () => {
|
|
||||||
selectedSort = SortMode.OldestFile
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: sortModeToString(SortMode.AscendingAlphabetical),
|
|
||||||
action: () => {
|
|
||||||
selectedSort = SortMode.AscendingAlphabetical
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: sortModeToString(SortMode.DescendingAlphabetical),
|
|
||||||
action: () => {
|
|
||||||
selectedSort = SortMode.DescendingAlphabetical
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
ev.target as HTMLElement
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortModeToString = (sortMode: SortMode): IntlString<{}> => {
|
async function fetch(
|
||||||
switch (sortMode) {
|
searchQuery_: string,
|
||||||
case SortMode.NewestFile:
|
selectedSort_: FileBrowserSortMode,
|
||||||
return attachment.string.FileBrowserSortNewest
|
selectedFileTypeId_: string,
|
||||||
case SortMode.OldestFile:
|
selectedDateId_: string,
|
||||||
return attachment.string.FileBrowserSortOldest
|
selectedParticipants_: Ref<Employee>[],
|
||||||
case SortMode.AscendingAlphabetical:
|
selectedSpaces_: Ref<Space>[]
|
||||||
return attachment.string.FileBrowserSortAZ
|
) {
|
||||||
case SortMode.DescendingAlphabetical:
|
isLoading = true
|
||||||
return attachment.string.FileBrowserSortZA
|
|
||||||
|
const nameQuery = searchQuery_ ? { name: { $like: '%' + searchQuery_ + '%' } } : {}
|
||||||
|
|
||||||
|
const accounts = await client.findAll(contact.class.EmployeeAccount, { employee: { $in: selectedParticipants_ } })
|
||||||
|
const senderQuery = accounts.length ? { modifiedBy: { $in: accounts.map((a) => a._id) } } : {}
|
||||||
|
|
||||||
|
let spaceQuery: { space: any }
|
||||||
|
if (selectedSpaces_.length) {
|
||||||
|
spaceQuery = { space: { $in: selectedSpaces_ } }
|
||||||
|
} else {
|
||||||
|
// nothing is selected in space filter - show all available attachments (except for the archived channels)
|
||||||
|
const allSpaces = await client.findAll(core.class.Space, {
|
||||||
|
archived: false,
|
||||||
|
_class: { $in: requestedSpaceClasses }
|
||||||
|
})
|
||||||
|
const availableSpaces = allSpaces
|
||||||
|
.filter((sp) => !sp.private || sp.members.includes(currentUser._id))
|
||||||
|
.map((sp) => sp._id)
|
||||||
|
spaceQuery = { space: { $in: availableSpaces } }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const sortModeToOptionObject = (sortMode: SortMode): SortingQuery<Attachment> => {
|
const date = dateFileBrowserFilters.find((o) => o.id === selectedDateId_)?.getDate()
|
||||||
switch (sortMode) {
|
|
||||||
case SortMode.NewestFile:
|
|
||||||
return { modifiedOn: SortingOrder.Descending }
|
|
||||||
case SortMode.OldestFile:
|
|
||||||
return { modifiedOn: SortingOrder.Ascending }
|
|
||||||
case SortMode.AscendingAlphabetical:
|
|
||||||
return { name: SortingOrder.Ascending }
|
|
||||||
case SortMode.DescendingAlphabetical:
|
|
||||||
return { name: SortingOrder.Descending }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: fetch(selectedSort, selectedFileTypeId, selectedDateId)
|
|
||||||
|
|
||||||
async function fetch (selectedSort_: SortMode, selectedFileTypeId_: string, selectedDateId_: string) {
|
|
||||||
const spaceQuery = space && { space: space._id }
|
|
||||||
const fileType = fileTypeObjects.find((o) => o.id === selectedFileTypeId_)?.getType()
|
|
||||||
const typeQuery = fileType && { type: fileType }
|
|
||||||
const date = dateObjects.find((o) => o.id === selectedDateId_)?.getDate()
|
|
||||||
const dateQuery = date && { modifiedOn: date }
|
const dateQuery = date && { modifiedOn: date }
|
||||||
|
|
||||||
|
const fileType = fileTypeFileBrowserFilters.find((o) => o.id === selectedFileTypeId_)?.getType()
|
||||||
|
const fileTypeQuery = fileType && { type: fileType }
|
||||||
|
|
||||||
attachments = await client.findAll(
|
attachments = await client.findAll(
|
||||||
attachment.class.Attachment,
|
attachment.class.Attachment,
|
||||||
{ ...spaceQuery, ...typeQuery, ...dateQuery },
|
{ ...nameQuery, ...senderQuery, ...spaceQuery, ...dateQuery, ...fileTypeQuery },
|
||||||
{
|
{
|
||||||
sort: sortModeToOptionObject(selectedSort_)
|
sort: sortModeToOptionObject(selectedSort_),
|
||||||
|
limit: 200
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
isLoading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDestroy(
|
||||||
|
location.subscribe(async (loc) => {
|
||||||
|
loc.query = undefined
|
||||||
|
navigate(loc)
|
||||||
|
})
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="ac-header full divide">
|
<div class="ac-header full divide">
|
||||||
<div class="ac-header__wrap-title">
|
<div class="ac-header__wrap-title">
|
||||||
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
|
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
|
||||||
</div>
|
</div>
|
||||||
|
<EditWithIcon icon={IconSearch} bind:value={searchQuery} placeholder={ui.string.SearchDots} />
|
||||||
</div>
|
</div>
|
||||||
<div class="filterBlockContainer">
|
<FileBrowserFilters
|
||||||
<div class="simpleFilterButton">
|
{requestedSpaceClasses}
|
||||||
<UserBoxList
|
{spaceId}
|
||||||
_class={contact.class.Employee}
|
bind:selectedParticipants
|
||||||
items={participants}
|
bind:selectedSpaces
|
||||||
label={attachment.string.FileBrowserFilterFrom}
|
bind:selectedDateId
|
||||||
on:update={(evt) => {
|
bind:selectedFileTypeId
|
||||||
participants = evt.detail
|
/>
|
||||||
}}
|
|
||||||
noItems={attachment.string.NoParticipants}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- TODO: wait for In filter -->
|
|
||||||
<!-- <div class="simpleFilterButton">
|
|
||||||
<UserBox _class={contact.class.Employee} label={attachment.string.FileBrowserFilterIn} bind:value={assignee} />
|
|
||||||
</div> -->
|
|
||||||
<div class="simpleFilterButton">
|
|
||||||
<DropdownLabelsIntl
|
|
||||||
items={dateObjects}
|
|
||||||
placeholder={attachment.string.FileBrowserFilterDate}
|
|
||||||
label={attachment.string.FileBrowserFilterDate}
|
|
||||||
bind:selected={selectedDateId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="simpleFilterButton">
|
|
||||||
<DropdownLabelsIntl
|
|
||||||
items={fileTypeObjects}
|
|
||||||
placeholder={attachment.string.FileBrowserFilterFileType}
|
|
||||||
label={attachment.string.FileBrowserFilterFileType}
|
|
||||||
bind:selected={selectedFileTypeId}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<div class="groupHeader">
|
<div class="groupHeader">
|
||||||
<div class="eGroupHeaderCount">
|
<div class="eGroupHeaderCount">
|
||||||
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
|
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
|
||||||
</div>
|
</div>
|
||||||
<div class="eGroupHeaderSortMenu" on:click={(event) => showSortMenu(event)}>
|
<FileBrowserSortMenu bind:selectedSort />
|
||||||
{'Sort: '}
|
|
||||||
<Label label={sortModeToString(selectedSort)} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{#if attachments?.length}
|
{#if isLoading}
|
||||||
|
<div class="ml-4">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
{:else if attachments?.length}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
{#each attachments as attachment, i}
|
{#each attachments as attachment, i}
|
||||||
<div class="flex-between attachmentRow" class:fixed={i === selectedFileNumber}>
|
<div class="flex-between attachmentRow" class:fixed={i === selectedFileNumber}>
|
||||||
@ -299,9 +172,8 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.group {
|
.group {
|
||||||
border: 1px solid var(--theme-bg-focused-border);
|
|
||||||
border-radius: 1rem;
|
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.groupHeader {
|
.groupHeader {
|
||||||
@ -313,10 +185,6 @@
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-caption-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.eGroupHeaderSortMenu {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.attachmentRow {
|
.attachmentRow {
|
||||||
@ -360,16 +228,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterBlockContainer {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.simpleFilterButton {
|
|
||||||
min-width: 4rem;
|
|
||||||
max-width: 12rem;
|
|
||||||
margin-left: 0.75rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import contact, { Employee } from '@anticrm/contact'
|
||||||
|
import { Class, Ref, Space } from '@anticrm/core'
|
||||||
|
import { SpaceMultiBoxList, UserBoxList } from '@anticrm/presentation'
|
||||||
|
import { DropdownLabelsIntl } from '@anticrm/ui'
|
||||||
|
import attachment from '../plugin'
|
||||||
|
import { dateFileBrowserFilters, fileTypeFileBrowserFilters } from '..'
|
||||||
|
|
||||||
|
export let requestedSpaceClasses: Ref<Class<Space>>[]
|
||||||
|
export let spaceId: Ref<Space> | undefined
|
||||||
|
export let selectedParticipants: Ref<Employee>[]
|
||||||
|
export let selectedSpaces: Ref<Space>[]
|
||||||
|
export let selectedDateId: string
|
||||||
|
export let selectedFileTypeId: string
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="filterBlockContainer">
|
||||||
|
<div class="simpleFilterButton">
|
||||||
|
<UserBoxList
|
||||||
|
_class={contact.class.Employee}
|
||||||
|
items={selectedParticipants}
|
||||||
|
label={attachment.string.FileBrowserFilterFrom}
|
||||||
|
on:update={(evt) => {
|
||||||
|
selectedParticipants = evt.detail
|
||||||
|
}}
|
||||||
|
noItems={attachment.string.NoParticipants}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="simpleFilterButton">
|
||||||
|
<SpaceMultiBoxList
|
||||||
|
_classes={requestedSpaceClasses}
|
||||||
|
label={attachment.string.FileBrowserFilterIn}
|
||||||
|
selectedItems={spaceId ? [spaceId] : []}
|
||||||
|
on:update={(evt) => {
|
||||||
|
selectedSpaces = evt.detail
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="simpleFilterButton">
|
||||||
|
<DropdownLabelsIntl
|
||||||
|
items={dateFileBrowserFilters}
|
||||||
|
placeholder={attachment.string.FileBrowserFilterDate}
|
||||||
|
label={attachment.string.FileBrowserFilterDate}
|
||||||
|
bind:selected={selectedDateId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="simpleFilterButton">
|
||||||
|
<DropdownLabelsIntl
|
||||||
|
items={fileTypeFileBrowserFilters}
|
||||||
|
placeholder={attachment.string.FileBrowserFilterFileType}
|
||||||
|
label={attachment.string.FileBrowserFilterFileType}
|
||||||
|
bind:selected={selectedFileTypeId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.filterBlockContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.simpleFilterButton {
|
||||||
|
max-width: 12rem;
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,83 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { IntlString } from '@anticrm/platform'
|
||||||
|
import { Label, Menu, showPopup } from '@anticrm/ui'
|
||||||
|
import { FileBrowserSortMode } from '..'
|
||||||
|
import attachment from '../plugin'
|
||||||
|
|
||||||
|
export let selectedSort: FileBrowserSortMode
|
||||||
|
|
||||||
|
const sortModeToString = (sortMode: FileBrowserSortMode): IntlString<{}> => {
|
||||||
|
switch (sortMode) {
|
||||||
|
case FileBrowserSortMode.NewestFile:
|
||||||
|
return attachment.string.FileBrowserSortNewest
|
||||||
|
case FileBrowserSortMode.OldestFile:
|
||||||
|
return attachment.string.FileBrowserSortOldest
|
||||||
|
case FileBrowserSortMode.AscendingAlphabetical:
|
||||||
|
return attachment.string.FileBrowserSortAZ
|
||||||
|
case FileBrowserSortMode.DescendingAlphabetical:
|
||||||
|
return attachment.string.FileBrowserSortZA
|
||||||
|
case FileBrowserSortMode.SmallestSize:
|
||||||
|
return attachment.string.FileBrowserSortSmallest
|
||||||
|
case FileBrowserSortMode.BiggestSize:
|
||||||
|
return attachment.string.FileBrowserSortBiggest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showSortMenu = async (ev: Event): Promise<void> => {
|
||||||
|
showPopup(
|
||||||
|
Menu,
|
||||||
|
{
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.NewestFile),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.NewestFile
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.OldestFile),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.OldestFile
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.AscendingAlphabetical),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.AscendingAlphabetical
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.DescendingAlphabetical),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.DescendingAlphabetical
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.SmallestSize),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.SmallestSize
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortModeToString(FileBrowserSortMode.BiggestSize),
|
||||||
|
action: () => {
|
||||||
|
selectedSort = FileBrowserSortMode.BiggestSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
ev.target as HTMLElement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="sortMenu" on:click={(event) => showSortMenu(event)}>
|
||||||
|
<Label label={attachment.string.FileBrowserSort} />
|
||||||
|
<Label label={sortModeToString(selectedSort)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.sortMenu {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -19,11 +19,25 @@
|
|||||||
|
|
||||||
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
<g {fill}>
|
<g {fill}>
|
||||||
<path d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"/>
|
<path
|
||||||
<path d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"/>
|
d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"
|
||||||
<path d="M40.489,27.248c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.119 C43.746,29.177,42.338,27.573,40.489,27.248z"/>
|
/>
|
||||||
<path d="M68.792,27.211c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.082 C72.049,29.14,70.641,27.535,68.792,27.211z"/>
|
<path
|
||||||
<path d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"
|
||||||
<path d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
/>
|
||||||
|
<path
|
||||||
|
d="M40.489,27.248c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.119 C43.746,29.177,42.338,27.573,40.489,27.248z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M68.792,27.211c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.082 C72.049,29.14,70.641,27.535,68.792,27.211z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"
|
||||||
|
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"
|
||||||
|
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -19,8 +19,15 @@
|
|||||||
|
|
||||||
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
<g {fill}>
|
<g {fill}>
|
||||||
<path d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7 C27.852,25.902,29.798,24.778,31.356,25.677z"/>
|
<path
|
||||||
<path d="M69.981,47.977l-38.625-22.3c-0.233-0.134-0.474-0.21-0.716-0.259l37.341,21.559c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-0.349,0.201-0.716,0.288-1.078,0.301c0.656,0.938,1.961,1.343,3.078,0.699l38.625-22.3 C71.538,51.124,71.538,48.876,69.981,47.977z"/>
|
d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7 C27.852,25.902,29.798,24.778,31.356,25.677z"
|
||||||
<path d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7C27.852,25.902,29.798,24.778,31.356,25.677z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
/>
|
||||||
|
<path
|
||||||
|
d="M69.981,47.977l-38.625-22.3c-0.233-0.134-0.474-0.21-0.716-0.259l37.341,21.559c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-0.349,0.201-0.716,0.288-1.078,0.301c0.656,0.938,1.961,1.343,3.078,0.699l38.625-22.3 C71.538,51.124,71.538,48.876,69.981,47.977z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7C27.852,25.902,29.798,24.778,31.356,25.677z"
|
||||||
|
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -24,9 +24,10 @@ import TxAttachmentCreate from './components/activity/TxAttachmentCreate.svelte'
|
|||||||
import Attachments from './components/Attachments.svelte'
|
import Attachments from './components/Attachments.svelte'
|
||||||
import FileBrowser from './components/FileBrowser.svelte'
|
import FileBrowser from './components/FileBrowser.svelte'
|
||||||
import Photos from './components/Photos.svelte'
|
import Photos from './components/Photos.svelte'
|
||||||
import { Resources } from '@anticrm/platform'
|
|
||||||
import { uploadFile, deleteFile } from './utils'
|
import { uploadFile, deleteFile } from './utils'
|
||||||
import attachment, { Attachment } from '@anticrm/attachment'
|
import attachment, { Attachment } from '@anticrm/attachment'
|
||||||
|
import { SortingOrder, SortingQuery } from '@anticrm/core'
|
||||||
|
import { IntlString, Resources } from '@anticrm/platform'
|
||||||
import preference from '@anticrm/preference'
|
import preference from '@anticrm/preference'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
|
|
||||||
@ -41,6 +42,143 @@ export {
|
|||||||
AttachmentDocList
|
AttachmentDocList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FileBrowserSortMode {
|
||||||
|
NewestFile,
|
||||||
|
OldestFile,
|
||||||
|
AscendingAlphabetical,
|
||||||
|
DescendingAlphabetical,
|
||||||
|
SmallestSize,
|
||||||
|
BiggestSize
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sortModeToOptionObject = (sortMode: FileBrowserSortMode): SortingQuery<Attachment> => {
|
||||||
|
switch (sortMode) {
|
||||||
|
case FileBrowserSortMode.NewestFile:
|
||||||
|
return { modifiedOn: SortingOrder.Descending }
|
||||||
|
case FileBrowserSortMode.OldestFile:
|
||||||
|
return { modifiedOn: SortingOrder.Ascending }
|
||||||
|
case FileBrowserSortMode.AscendingAlphabetical:
|
||||||
|
return { name: SortingOrder.Ascending }
|
||||||
|
case FileBrowserSortMode.DescendingAlphabetical:
|
||||||
|
return { name: SortingOrder.Descending }
|
||||||
|
case FileBrowserSortMode.SmallestSize:
|
||||||
|
return { size: SortingOrder.Ascending }
|
||||||
|
case FileBrowserSortMode.BiggestSize:
|
||||||
|
return { size: SortingOrder.Descending }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const msInDay = 24 * 60 * 60 * 1000
|
||||||
|
const getBeginningOfDate = (customDate?: Date) => {
|
||||||
|
if (!customDate) {
|
||||||
|
customDate = new Date()
|
||||||
|
}
|
||||||
|
customDate.setUTCHours(0, 0, 0, 0)
|
||||||
|
return customDate.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dateFileBrowserFilters: {
|
||||||
|
id: string
|
||||||
|
label: IntlString<{}>
|
||||||
|
getDate: () => any
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: 'dateAny',
|
||||||
|
label: attachment.string.FileBrowserDateFilterAny,
|
||||||
|
getDate: () => {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dateToday',
|
||||||
|
label: attachment.string.FileBrowserDateFilterToday,
|
||||||
|
getDate: () => {
|
||||||
|
return { $gte: getBeginningOfDate() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dateYesterday',
|
||||||
|
label: attachment.string.FileBrowserDateFilterYesterday,
|
||||||
|
getDate: () => {
|
||||||
|
return { $gte: getBeginningOfDate() - msInDay, $lt: getBeginningOfDate() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date7Days',
|
||||||
|
label: attachment.string.FileBrowserDateFilter7Days,
|
||||||
|
getDate: () => {
|
||||||
|
return { $gte: getBeginningOfDate() - msInDay * 6 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date30Days',
|
||||||
|
label: attachment.string.FileBrowserDateFilter30Days,
|
||||||
|
getDate: () => {
|
||||||
|
return { $gte: getBeginningOfDate() - msInDay * 29 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date3Months',
|
||||||
|
label: attachment.string.FileBrowserDateFilter3Months,
|
||||||
|
getDate: () => {
|
||||||
|
const now = new Date()
|
||||||
|
now.setMonth(now.getMonth() - 3)
|
||||||
|
return { $gte: getBeginningOfDate(now) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date12Months',
|
||||||
|
label: attachment.string.FileBrowserDateFilter12Months,
|
||||||
|
getDate: () => {
|
||||||
|
const now = new Date()
|
||||||
|
now.setMonth(now.getMonth() - 12)
|
||||||
|
return { $gte: getBeginningOfDate(now) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const fileTypeFileBrowserFilters: {
|
||||||
|
id: string
|
||||||
|
label: IntlString<{}>
|
||||||
|
getType: () => any
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: 'typeAny',
|
||||||
|
label: attachment.string.FileBrowserTypeFilterAny,
|
||||||
|
getType: () => {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'typeImage',
|
||||||
|
label: attachment.string.FileBrowserTypeFilterImages,
|
||||||
|
getType: () => {
|
||||||
|
return { $like: '%image/%' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'typeAudio',
|
||||||
|
label: attachment.string.FileBrowserTypeFilterAudio,
|
||||||
|
getType: () => {
|
||||||
|
return { $like: '%audio/%' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'typeVideo',
|
||||||
|
label: attachment.string.FileBrowserTypeFilterVideos,
|
||||||
|
getType: () => {
|
||||||
|
return { $like: '%video/%' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'typePDF',
|
||||||
|
label: attachment.string.FileBrowserTypeFilterPDFs,
|
||||||
|
getType: () => {
|
||||||
|
return 'application/pdf'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
export async function AddAttachmentToSaved(attach: Attachment): Promise<void> {
|
export async function AddAttachmentToSaved(attach: Attachment): Promise<void> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
|
@ -32,22 +32,13 @@ export default mergeIds(attachmentId, attachment, {
|
|||||||
FileBrowserFilterIn: '' as IntlString,
|
FileBrowserFilterIn: '' as IntlString,
|
||||||
FileBrowserFilterDate: '' as IntlString,
|
FileBrowserFilterDate: '' as IntlString,
|
||||||
FileBrowserFilterFileType: '' as IntlString,
|
FileBrowserFilterFileType: '' as IntlString,
|
||||||
|
FileBrowserSort: '' as IntlString,
|
||||||
FileBrowserSortNewest: '' as IntlString,
|
FileBrowserSortNewest: '' as IntlString,
|
||||||
FileBrowserSortOldest: '' as IntlString,
|
FileBrowserSortOldest: '' as IntlString,
|
||||||
FileBrowserSortAZ: '' as IntlString,
|
FileBrowserSortAZ: '' as IntlString,
|
||||||
FileBrowserSortZA: '' as IntlString,
|
FileBrowserSortZA: '' as IntlString,
|
||||||
FileBrowserDateFilterAny: '' as IntlString,
|
FileBrowserSortSmallest: '' as IntlString,
|
||||||
FileBrowserDateFilterToday: '' as IntlString,
|
FileBrowserSortBiggest: '' as IntlString,
|
||||||
FileBrowserDateFilterYesterday: '' as IntlString,
|
|
||||||
FileBrowserDateFilter7Days: '' as IntlString,
|
|
||||||
FileBrowserDateFilter30Days: '' as IntlString,
|
|
||||||
FileBrowserDateFilter3Months: '' as IntlString,
|
|
||||||
FileBrowserDateFilter12Months: '' as IntlString,
|
|
||||||
FileBrowserTypeFilterAny: '' as IntlString,
|
|
||||||
FileBrowserTypeFilterImages: '' as IntlString,
|
|
||||||
FileBrowserTypeFilterAudio: '' as IntlString,
|
|
||||||
FileBrowserTypeFilterVideos: '' as IntlString,
|
|
||||||
FileBrowserTypeFilterPDFs: '' as IntlString,
|
|
||||||
AddAttachmentToSaved: '' as IntlString,
|
AddAttachmentToSaved: '' as IntlString,
|
||||||
RemoveAttachmentFromSaved: '' as IntlString
|
RemoveAttachmentFromSaved: '' as IntlString
|
||||||
},
|
},
|
||||||
|
@ -71,6 +71,18 @@ export default plugin(attachmentId, {
|
|||||||
Files: '' as IntlString,
|
Files: '' as IntlString,
|
||||||
NoFiles: '' as IntlString,
|
NoFiles: '' as IntlString,
|
||||||
NoParticipants: '' as IntlString,
|
NoParticipants: '' as IntlString,
|
||||||
ShowMoreAttachments: '' as IntlString
|
ShowMoreAttachments: '' as IntlString,
|
||||||
|
FileBrowserDateFilterAny: '' as IntlString,
|
||||||
|
FileBrowserDateFilterToday: '' as IntlString,
|
||||||
|
FileBrowserDateFilterYesterday: '' as IntlString,
|
||||||
|
FileBrowserDateFilter7Days: '' as IntlString,
|
||||||
|
FileBrowserDateFilter30Days: '' as IntlString,
|
||||||
|
FileBrowserDateFilter3Months: '' as IntlString,
|
||||||
|
FileBrowserDateFilter12Months: '' as IntlString,
|
||||||
|
FileBrowserTypeFilterAny: '' as IntlString,
|
||||||
|
FileBrowserTypeFilterImages: '' as IntlString,
|
||||||
|
FileBrowserTypeFilterAudio: '' as IntlString,
|
||||||
|
FileBrowserTypeFilterVideos: '' as IntlString,
|
||||||
|
FileBrowserTypeFilterPDFs: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
on:click={() => {
|
on:click={() => {
|
||||||
const loc = getCurrentLocation()
|
const loc = getCurrentLocation()
|
||||||
loc.path[2] = 'fileBrowser'
|
loc.path[2] = 'fileBrowser'
|
||||||
|
loc.query = channel ? { spaceId: channel._id } : {}
|
||||||
navigate(loc)
|
navigate(loc)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user