Skip to content

Commit

Permalink
Implement tag filter on search result page
Browse files Browse the repository at this point in the history
  • Loading branch information
JammingBen committed May 16, 2023
1 parent b5d75e9 commit 9d528f0
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 239 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-search-tag-filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Search tag filter

The search result page now has a tag filter which can be used to filter the displayed search result by tags.

https://github.com/owncloud/web/pull/9044
https://github.com/owncloud/web/issues/9054
Original file line number Diff line number Diff line change
Expand Up @@ -632,9 +632,10 @@ export default defineComponent({
shouldDisplayThumbnails(item) {
return this.areThumbnailsDisplayed && !isResourceTxtFileAlmostEmpty(item)
},
getTagLink(tag) {
getTagLink(tag: string) {
const currentTerm = unref(this.$router.currentRoute).query?.term
return createLocationCommon('files-common-search', {
query: { term: `Tags:"${tag}"`, provider: 'files.sdk' }
query: { provider: 'files.sdk', q_tags: tag, ...(currentTerm && { term: currentTerm }) }
})
},
getTagComponentAttrs(tag) {
Expand Down
113 changes: 108 additions & 5 deletions packages/web-app-files/src/components/Search/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@
<div class="files-search-result oc-flex">
<files-view-wrapper>
<app-bar :has-bulk-actions="false" :side-bar-open="sideBarOpen" />
<div class="files-search-result-filter oc-mx-m oc-mb-m">
<item-filter
v-if="availableTags.length"
ref="tagFilter"
:allow-multiple="true"
:filter-label="$gettext('Tags')"
:filterable-attributes="['label']"
:items="availableTags"
:option-filter-label="$gettext('Filter tags')"
:show-option-filter="true"
class="files-search-filter-tags oc-mr-s"
display-name-attribute="label"
filter-name="tags"
>
<template #image="{ item }">
<oc-avatar :width="24" :user-name="item.label" />
</template>
<template #item="{ item }">
<div v-text="item.label" />
</template>
</item-filter>
</div>
<app-loading-spinner v-if="loading" />
<template v-else>
<no-content-message
Expand Down Expand Up @@ -77,7 +99,7 @@ import ContextActions from '../FilesList/ContextActions.vue'
import { debounce } from 'lodash-es'
import { mapMutations, mapGetters, mapActions } from 'vuex'
import AppBar from '../AppBar/AppBar.vue'
import { defineComponent, nextTick } from 'vue'
import { computed, defineComponent, nextTick, onMounted, ref, unref, VNodeRef, watch } from 'vue'
import ListInfo from '../FilesList/ListInfo.vue'
import Pagination from '../FilesList/Pagination.vue'
import { useFileActions } from '../../composables/actions/files/useFileActions'
Expand All @@ -86,14 +108,30 @@ import { Resource } from 'web-client'
import FilesViewWrapper from '../FilesViewWrapper.vue'
import SideBar from '../../components/SideBar/SideBar.vue'
import { buildShareSpaceResource, SpaceResource } from 'web-client/src/helpers'
import { useFileListHeaderPosition, useStore } from 'web-pkg/src/composables'
import {
queryItemAsString,
useClientService,
useFileListHeaderPosition,
useRoute,
useRouteQuery,
useRouter,
useStore
} from 'web-pkg/src/composables'
import { configurationManager } from 'web-pkg/src/configuration'
import { basename } from 'path'
import { onBeforeRouteLeave } from 'vue-router'
import { useTask } from 'vue-concurrency'
import { eventBus } from 'web-pkg'
import ItemFilter from 'web-pkg/src/components/ItemFilter.vue'
import { isLocationCommonActive } from 'web-app-files/src/router'
const visibilityObserver = new VisibilityObserver()
type Tag = {
id: string
label: string
}
export default defineComponent({
components: {
AppBar,
Expand All @@ -104,7 +142,8 @@ export default defineComponent({
Pagination,
NoContentMessage,
ResourceTable,
FilesViewWrapper
FilesViewWrapper,
ItemFilter
},
props: {
searchResult: {
Expand All @@ -118,9 +157,28 @@ export default defineComponent({
default: false
}
},
setup() {
emits: ['search'],
setup(props, { emit }) {
const store = useStore()
const router = useRouter()
const route = useRoute()
const { y: fileListHeaderY } = useFileListHeaderPosition()
const clientService = useClientService()
const searchTermQuery = useRouteQuery('term')
const searchTerm = computed(() => {
return queryItemAsString(unref(searchTermQuery))
})
const availableTags = ref<Tag[]>([])
const tagFilter = ref<VNodeRef>()
const tagParam = useRouteQuery('q_tags')
const loadAvailableTagsTask = useTask(function* () {
const {
data: { value: tags = [] }
} = yield clientService.graphAuthenticated.tags.getTags()
availableTags.value = [...tags.map((t) => ({ id: t, label: t }))]
})
onBeforeRouteLeave(() => {
eventBus.publish('app.search.term.clear')
Expand All @@ -136,11 +194,51 @@ export default defineComponent({
return store.getters['runtime/spaces/spaces'].find((space) => space.id === resource.storageId)
}
const buildSearchTerm = (manuallyUpdateFilterChip = false) => {
let term = unref(searchTerm)
const tags = queryItemAsString(unref(tagParam))
if (tags) {
const foo = tags.split('+')?.join(',') || ''
term += ` Tags:"${unref(foo)}"`
if (manuallyUpdateFilterChip && unref(tagFilter)) {
/**
* Handles edge cases where a filter is not being applied via the filter directly,
* e.g. when clicking on a tag in the files list.
* We need to manually update the selected items in the ItemFilter component because normally
* it only does this on mount or when interacting with the filter directly.
*/
;(unref(tagFilter) as any).setSelectedItemsBasedOnQuery()
}
}
return term
}
onMounted(async () => {
await loadAvailableTagsTask.perform()
emit('search', buildSearchTerm())
})
watch(
() => unref(route).query,
(newVal, oldVal) => {
const isChange = newVal?.term !== oldVal?.term || newVal?.q_tags !== oldVal?.q_tags
if (isChange && isLocationCommonActive(router, 'files-common-search')) {
emit('search', buildSearchTerm(true))
}
},
{ deep: true }
)
return {
...useFileActions({ store }),
...useResourcesViewDefaults<Resource, any, any[]>(),
loadAvailableTagsTask,
fileListHeaderY,
getSpace
getSpace,
availableTags,
tagFilter
}
},
computed: {
Expand Down Expand Up @@ -224,3 +322,8 @@ export default defineComponent({
}
})
</script>
<style lang="scss">
.files-search-result .files-app-bar-actions {
display: none !important;
}
</style>
2 changes: 1 addition & 1 deletion packages/web-app-files/src/router/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const buildRoutes = (components: RouteComponents): RouteRecordRaw[] => [
meta: {
authContext: 'user',
title: $gettext('Search results'),
contextQueryItems: ['term', 'provider']
contextQueryItems: ['term', 'provider', 'q_tags']
}
}
]
Expand Down
Loading

0 comments on commit 9d528f0

Please sign in to comment.