diff --git a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue index 15d4098dd30..4f1c54a8b11 100644 --- a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue +++ b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue @@ -27,7 +27,7 @@ :label="t('core', 'Search apps, files, tags, messages') + '...'" @update:value="debouncedFind" />
- + @@ -43,7 +43,7 @@ {{ provider.name }} - + @@ -120,7 +120,8 @@

{{ t('core', 'Results') }}

-
+ +

{{ providerResult.name }}

@@ -144,6 +145,36 @@
+ +
@@ -321,6 +352,50 @@ export default defineComponent({ debouncedFilterContacts() { return debounce(this.filterContacts, 300) }, + + hasContentFilters() { + return this.filters.some((filter) => filter.type === 'date' || filter.type === 'person') + }, + + filteredResults() { + const isInFolderAtRoot = (result) => { + if (result.id !== 'in-folder') { + return false + } + const path = result.extraParams?.path + return !path || path === '/' || path === '' + } + + if (!this.hasContentFilters) { + return this.results.filter((result) => !isInFolderAtRoot(result)) + } + return this.results.filter((result) => result.supportsActiveFilters === true && !isInFolderAtRoot(result)) + }, + + filteredResultUrls() { + const urls = new Set() + this.filteredResults.forEach((provider) => { + provider.results.forEach((entry) => { + if (entry.resourceUrl) { + urls.add(entry.resourceUrl) + } + }) + }) + return urls + }, + + unfilteredResults() { + if (!this.hasContentFilters) { + return [] + } + return this.results + .filter((result) => result.supportsActiveFilters === false) + .map((provider) => ({ + ...provider, + results: provider.results.filter((entry) => !this.filteredResultUrls.has(entry.resourceUrl)), + })) + .filter((provider) => provider.results.length > 0) + }, }, watch: { @@ -413,20 +488,30 @@ export default defineComponent({ // This block of filter checks should be dynamic somehow and should be handled in // nextcloud/search lib - const activeFilters = this.filters.filter(filter => { + const contentFilterTypes = this.filters + .filter((f) => f.type !== 'provider') + .map((f) => f.type) + const supportsActiveFilters = contentFilterTypes.length === 0 + || contentFilterTypes.every((type) => this.providerIsCompatibleWithFilters(provider, [type])) + + const baseProvider = provider.searchFrom + ? this.providers.find((p) => p.id === provider.searchFrom) ?? provider + : provider + + const activeFilters = this.filters.filter((filter) => { return filter.type !== 'provider' && this.providerIsCompatibleWithFilters(provider, [filter.type]) }) - activeFilters.forEach(filter => { + activeFilters.forEach((filter) => { switch (filter.type) { case 'date': - if (provider.filters?.since && provider.filters?.until) { + if (baseProvider.filters?.since && baseProvider.filters?.until) { params.since = this.dateFilter.startFrom params.until = this.dateFilter.endAt } break case 'person': - if (provider.filters?.person) { + if (baseProvider.filters?.person) { params.person = this.personFilter.user } break @@ -445,6 +530,7 @@ export default defineComponent({ ...provider, results: response.data.ocs.data.entries, limit: params.limit ?? 5, + supportsActiveFilters, }) unifiedSearchLogger.debug('Unified search results:', { results: this.results, newResults }) @@ -716,8 +802,20 @@ export default defineComponent({ return flattenedArray }, - async providerIsCompatibleWithFilters(provider, filterIds) { - return filterIds.every(filterId => provider.filters?.[filterId] !== undefined) + providerIsCompatibleWithFilters(provider, filterIds) { + const baseProvider = provider.searchFrom + ? this.providers.find((p) => p.id === provider.searchFrom) ?? provider + : provider + return filterIds.every((filterId) => { + switch (filterId) { + case 'date': + return baseProvider.filters?.since !== undefined && baseProvider.filters?.until !== undefined + case 'person': + return baseProvider.filters?.person !== undefined + default: + return baseProvider.filters?.[filterId] !== undefined + } + }) }, async enableAllProviders() { this.providers.forEach(async (_, index) => { @@ -793,9 +891,27 @@ export default defineComponent({ align-items: center; display: flex; } + + &--unfiltered { + opacity: 0.7; + } } } + + &__unfiltered-header { + display: flex; + flex-direction: column; + gap: 2px; + margin-block: 16px 8px; + padding-block: 12px 0; + border-top: 1px solid var(--color-border); + } + + &__unfiltered-label { + font-weight: bold; + color: var(--color-text-maxcontrast); + } } .filter-button__icon {