Skip to content

Commit c2ca1c4

Browse files
diocasrodcoffani
authored andcommitted
feat: trashbin filtering
No default value is sent to the server. The server will apply its own defaults.
1 parent 2e6c332 commit c2ca1c4

File tree

5 files changed

+287
-32
lines changed

5 files changed

+287
-32
lines changed

packages/web-app-files/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
"description": "ownCloud web files",
66
"license": "AGPL-3.0",
77
"devDependencies": {
8-
"@ownclouders/web-test-helpers": "workspace:*"
8+
"@ownclouders/web-test-helpers": "workspace:*",
9+
"@popperjs/core": "^2.11.8",
10+
"v-calendar": "^3.1.2"
911
},
1012
"peerDependencies": {
1113
"@ownclouders/design-system": "workspace:^",
1214
"@ownclouders/web-client": "workspace:*",
1315
"@ownclouders/web-pkg": "workspace:*",
16+
"@popperjs/core": "^2.11.8",
1417
"@uppy/core": "^3.3.0",
1518
"@vueuse/core": "^11.0.0",
1619
"axios": "1.7.7",
@@ -23,6 +26,7 @@
2326
"pinia": "2.2.6",
2427
"qs": "6.13.0",
2528
"uuid": "11.0.2",
29+
"v-calendar": "^3.1.2",
2630
"vue-concurrency": "5.0.1",
2731
"vue-router": "4.2.5",
2832
"vue3-gettext": "2.4.0",
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<template>
2+
<div class="oc-flex oc-flex-row trashbin-datepicker">
3+
<date-picker
4+
v-model="rangeSelected"
5+
:min-date="dateMin"
6+
:max-date="dateNow"
7+
:locale="currentLanguage"
8+
:is-range="true"
9+
timezone="utc"
10+
class="oc-datepicker"
11+
data-testid="trashbin-datepicker"
12+
>
13+
<template #default="{ togglePopover }">
14+
<oc-button
15+
class="oc-p-m action-menu-item"
16+
data-testid="trashbin-datepicker-btn"
17+
appearance="raw"
18+
:aria-label="
19+
rangeSelected ? $gettext('Edit time interval') : $gettext('Set time interval')
20+
"
21+
@click="togglePopover"
22+
>
23+
<oc-icon name="calendar-event" fill-type="line" size="medium" variation="passive" />
24+
<span
25+
v-if="!rangeSelected"
26+
key="no-selected-date-label"
27+
v-text="$gettext('Set time interval')"
28+
/>
29+
<span v-else key="set-selected-date-label" v-text="formatRange(rangeSelected)" />
30+
</oc-button>
31+
</template>
32+
<template #day-popover="{ format }"> </template>
33+
</date-picker>
34+
</div>
35+
</template>
36+
37+
<script lang="ts">
38+
import { DateTime } from 'luxon'
39+
import { ref, unref, watch, defineComponent } from 'vue'
40+
import { getLocaleFromLanguage } from '@ownclouders/web-pkg'
41+
import { useRouteQuery, queryItemAsString } from '@ownclouders/web-pkg'
42+
import { useGettext } from 'vue3-gettext'
43+
import { DatePicker } from 'v-calendar'
44+
import 'v-calendar/style.css'
45+
46+
export default defineComponent({
47+
name: 'TrashbinDatePicker',
48+
components: {
49+
DatePicker
50+
},
51+
props: {},
52+
emits: ['rangeChanged'],
53+
setup(props, { emit }) {
54+
const { current: currentLanguage } = useGettext()
55+
const fromQuery = useRouteQuery('from')
56+
const toQuery = useRouteQuery('to')
57+
58+
const locale = getLocaleFromLanguage(currentLanguage)
59+
const dateNow = DateTime.now().setLocale(locale)
60+
const dateMin = dateNow.minus({ week: 6 })
61+
const defaultStart = dateNow.minus({ days: 2 })
62+
63+
const rangeSelected =
64+
unref(fromQuery) && unref(toQuery)
65+
? ref({
66+
start: new Date(queryItemAsString(unref(fromQuery))),
67+
end: new Date(queryItemAsString(unref(toQuery)))
68+
})
69+
: ref({
70+
start: defaultStart,
71+
end: dateNow
72+
})
73+
74+
watch(rangeSelected, (newRange, oldRange) => {
75+
if (newRange?.start && newRange?.end) {
76+
const from = newRange.start.toISOString().slice(0, 10)
77+
const to = newRange.end.toISOString().slice(0, 10)
78+
fromQuery.value = from
79+
toQuery.value = to
80+
emit('rangeChanged', { range: { from, to } })
81+
}
82+
})
83+
84+
function formatDate(date: Date) {
85+
const day = date.getDate().toString().padStart(2, '0')
86+
const month = (date.getMonth() + 1).toString().padStart(2, '0')
87+
const year = date.getFullYear().toString().slice(-2)
88+
89+
return `${day}-${month}-${year}`
90+
}
91+
92+
function formatRange(range) {
93+
return `${formatDate(new Date(range.start))} - ${formatDate(new Date(range.end))}`
94+
}
95+
96+
// No need to add default values, as the backend will apply them
97+
// onMounted(() => {
98+
// if (!unref(rangeSelected)) {
99+
// rangeSelected.value = {
100+
// start: defaultStart.toJSDate(),
101+
// end: dateNow.toJSDate()
102+
// }
103+
// }
104+
// })
105+
106+
return {
107+
currentLanguage,
108+
rangeSelected,
109+
dateMin,
110+
dateNow,
111+
formatDate,
112+
formatRange
113+
}
114+
}
115+
})
116+
</script>
117+
118+
<style lang="scss">
119+
.trashbin-datepicker {
120+
width: fit-content;
121+
}
122+
.vc-pane-layout {
123+
color: var(--oc-color-text-default) !important;
124+
background-color: var(--oc-color-background-default) !important;
125+
}
126+
.vc-arrow {
127+
background: transparent;
128+
}
129+
.vc-arrow svg path {
130+
fill: var(--oc-color-text-default) !important;
131+
}
132+
.vc-title {
133+
color: var(--oc-color-text-default) !important;
134+
background-color: transparent !important;
135+
font-size: var(--text-lg);
136+
}
137+
.vc-weekday {
138+
color: var(--oc-color-text-muted) !important;
139+
}
140+
.vc-day {
141+
color: var(--oc-color-text-default) !important;
142+
}
143+
144+
.vc-highlights {
145+
.vc-highlight {
146+
background-color: var(--oc-color-swatch-primary-default) !important;
147+
}
148+
+ span {
149+
color: var(--oc-color-text-inverse) !important;
150+
}
151+
}
152+
153+
.vc-day-content.is-disabled {
154+
color: var(--oc-color-text-muted) !important;
155+
}
156+
</style>

packages/web-app-files/src/services/folder/loaderTrashbin.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,23 @@ export class FolderLoaderTrashbin implements FolderLoader {
1919
resourcesStore,
2020
clientService: { webdav }
2121
} = context
22-
return useTask(function* (signal1, signal2, space: SpaceResource) {
22+
return useTask(function* (signal1, signal2, space: SpaceResource, dateFilter) {
2323
resourcesStore.clearResourceList()
2424
resourcesStore.setAncestorMetaData({})
2525

2626
const { resource, children } = yield webdav.listFiles(
2727
space,
2828
{},
29-
{ depth: 1, davProperties: DavProperties.Trashbin, isTrash: true, signal: signal1 }
29+
{
30+
depth: 1,
31+
davProperties: DavProperties.Trashbin,
32+
isTrash: true,
33+
signal: signal1,
34+
headers: dateFilter ? {
35+
'X-Trashbin-From': dateFilter.from,
36+
'X-Trashbin-To': dateFilter.to
37+
} : {}
38+
}
3039
)
3140

3241
resourcesStore.initResourceList({ currentFolder: resource, resources: children })

packages/web-app-files/src/views/spaces/GenericTrash.vue

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99
/>
1010
<app-loading-spinner v-if="areResourcesLoading" />
1111
<template v-else>
12+
<div
13+
class="shared-with-me-filters oc-flex oc-flex-between oc-flex-wrap oc-flex-bottom oc-mx-m oc-mb-m"
14+
>
15+
<div class="oc-flex oc-flex-wrap">
16+
<div class="oc-mr-m oc-flex oc-flex-middle">
17+
<oc-icon name="filter-2" class="oc-mr-xs" />
18+
<span v-text="$gettext('Filter:')" />
19+
</div>
20+
<trashbin-date-picker @range-changed="rangeChanged" />
21+
</div>
22+
</div>
1223
<no-content-message
1324
v-if="isEmpty"
1425
id="files-trashbin-empty"
@@ -66,13 +77,16 @@ import { Pagination } from '@ownclouders/web-pkg'
6677
6778
import { eventBus } from '@ownclouders/web-pkg'
6879
import { useResourcesViewDefaults } from '../../composables'
69-
import { computed, defineComponent, PropType, onMounted, onBeforeUnmount, unref } from 'vue'
80+
import { computed, defineComponent, PropType, onMounted, onBeforeUnmount, unref, ref } from 'vue'
7081
import { Resource } from '@ownclouders/web-client'
7182
import { createLocationTrash } from '@ownclouders/web-pkg'
7283
import { isProjectSpaceResource, SpaceResource } from '@ownclouders/web-client'
7384
import { useDocumentTitle } from '@ownclouders/web-pkg'
7485
import { useGettext } from 'vue3-gettext'
7586
87+
import { useRouteQuery } from '@ownclouders/web-pkg'
88+
import TrashbinDatePicker from '../../components/FilesList/TrashbinDatePicker.vue'
89+
7690
export default defineComponent({
7791
name: 'GenericTrash',
7892
@@ -85,7 +99,8 @@ export default defineComponent({
8599
ListInfo,
86100
NoContentMessage,
87101
Pagination,
88-
ResourceTable
102+
ResourceTable,
103+
TrashbinDatePicker
89104
},
90105
91106
props: {
@@ -106,6 +121,22 @@ export default defineComponent({
106121
const userStore = useUserStore()
107122
const { user } = storeToRefs(userStore)
108123
124+
const filterFrom = useRouteQuery('from')
125+
const filterTo = useRouteQuery('to')
126+
const dateFilter =
127+
unref(filterFrom) && unref(filterTo)
128+
? ref({
129+
from: unref(filterFrom),
130+
to: unref(filterTo)
131+
})
132+
: ref(null)
133+
134+
const rangeChanged = (data) => {
135+
dateFilter.value =
136+
data.range?.from && data.range?.to ? { from: data.range.from, to: data.range.to } : null
137+
performLoaderTask()
138+
}
139+
109140
let loadResourcesEventToken: string
110141
const noContentMessage = computed(() => {
111142
return props.space.driveType === 'personal'
@@ -123,7 +154,7 @@ export default defineComponent({
123154
124155
const resourcesViewDefaults = useResourcesViewDefaults<Resource, any, any[]>()
125156
const performLoaderTask = async () => {
126-
await resourcesViewDefaults.loadResourcesTask.perform(props.space)
157+
await resourcesViewDefaults.loadResourcesTask.perform(props.space, unref(dateFilter))
127158
resourcesViewDefaults.refreshFileListHeaderPosition()
128159
resourcesViewDefaults.scrollToResourceFromRoute(
129160
unref(resourcesViewDefaults.paginatedResources),
@@ -145,7 +176,8 @@ export default defineComponent({
145176
return {
146177
...resourcesViewDefaults,
147178
user,
148-
noContentMessage
179+
noContentMessage,
180+
rangeChanged
149181
}
150182
},
151183

0 commit comments

Comments
 (0)