Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: search provider registration #9794

Merged
merged 1 commit into from
Oct 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import OcSearchBar from './OcSearchBar.vue'
describe('OcSearchBar', () => {
const selectors = {
searchButton: '.oc-search-button',
advancedSearchButton: '.oc-advanced-search',
searchInput: '.oc-search-input',
searchButtonWrapper: '.oc-search-button-wrapper',
searchClearButton: '.oc-search-clear'
@@ -187,11 +188,15 @@ describe('OcSearchBar', () => {
describe('advanced search button', () => {
it('should be visible', () => {
const wrapper = getWrapper()
expect(wrapper.find('.oc-advanced-search').exists()).toBeTruthy()
expect(wrapper.find(selectors.advancedSearchButton).exists()).toBeTruthy()
})
it('should not be visible if disabled', () => {
const wrapper = getWrapper({ showAdvancedSearchButton: false })
expect(wrapper.find(selectors.advancedSearchButton).exists()).toBeFalsy()
})
it('should trigger the "advancedSearch"-event on click', async () => {
const wrapper = getWrapper()
await wrapper.find('.oc-advanced-search').trigger('click')
await wrapper.find(selectors.advancedSearchButton).trigger('click')
expect(wrapper.emitted('advancedSearch').length).toBeGreaterThan(0)
})
})
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
/>
<slot name="locationFilter" />
<oc-button
v-if="showAdvancedSearchButton"
v-oc-tooltip="$gettext('Open advanced search')"
:aria-label="$gettext('Open advanced search')"
class="oc-advanced-search oc-position-small oc-position-center-right oc-mt-rm"
@@ -196,6 +197,13 @@ export default defineComponent({
required: false,
default: false
},
/**
* Show a "Advanced search button.
*/
showAdvancedSearchButton: {
type: Boolean,
default: true
},
/**
* Variation of the cancel button
*/
28 changes: 28 additions & 0 deletions packages/web-app-files/src/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
ApplicationSetupOptions,
Extension,
useStore,
useRouter,
useClientService,
useConfigurationManager
} from '@ownclouders/web-pkg'
import { computed } from 'vue'
import { SDKSearch } from './search'

export const extensions = ({ applicationConfig }: ApplicationSetupOptions) => {
const store = useStore()
const router = useRouter()
const clientService = useClientService()
const configurationManager = useConfigurationManager()

return computed(
() =>
[
{
id: 'com.github.owncloud.web.files.search',
type: 'search',
searchProvider: new SDKSearch(store, router, clientService, configurationManager)
}
] satisfies Extension[]
)
}
70 changes: 32 additions & 38 deletions packages/web-app-files/src/index.ts
Original file line number Diff line number Diff line change
@@ -8,14 +8,12 @@ import SpaceDriveResolver from './views/spaces/DriveResolver.vue'
import SpaceProjects from './views/spaces/Projects.vue'
import TrashOverview from './views/trash/Overview.vue'
import translations from '../l10n/translations.json'
import { quickActions } from '@ownclouders/web-pkg'
import { defineWebApplication, quickActions } from '@ownclouders/web-pkg'
import store from './store'
import { SDKSearch } from './search'
import { eventBus } from '@ownclouders/web-pkg'
import { Registry } from './services'
import { extensions } from './extensions'
import fileSideBars from './fileSideBars'
import { buildRoutes } from '@ownclouders/web-pkg'
import { AppNavigationItem, AppReadyHookArgs } from '@ownclouders/web-pkg'
import { AppNavigationItem } from '@ownclouders/web-pkg'

// dirty: importing view from other extension within project
import SearchResults from '../../web-app-search/src/views/List.vue'
@@ -24,7 +22,6 @@ import {
isPersonalSpaceResource,
isShareSpaceResource
} from '@ownclouders/web-client/src/helpers'
import { configurationManager } from '@ownclouders/web-pkg'

// just a dummy function to trick gettext tools
function $gettext(msg) {
@@ -40,7 +37,7 @@ const appInfo = {
extensions: [],
fileSideBars
}
const navItems = (context): AppNavigationItem[] => {
export const navItems = (context): AppNavigationItem[] => {
return [
{
name(capabilities) {
@@ -121,36 +118,33 @@ const navItems = (context): AppNavigationItem[] => {
]
}

export default {
appInfo,
store,
routes: buildRoutes({
App,
Favorites,
FilesDrop,
SearchResults,
Shares: {
SharedViaLink,
SharedWithMe,
SharedWithOthers
},
Spaces: {
DriveResolver: SpaceDriveResolver,
Projects: SpaceProjects
},
Trash: {
Overview: TrashOverview
export default defineWebApplication({
setup(args) {
return {
appInfo,
store,
routes: buildRoutes({
App,
Favorites,
FilesDrop,
SearchResults,
Shares: {
SharedViaLink,
SharedWithMe,
SharedWithOthers
},
Spaces: {
DriveResolver: SpaceDriveResolver,
Projects: SpaceProjects
},
Trash: {
Overview: TrashOverview
}
}),
navItems,
quickActions,
translations,
extensions: extensions(args)
}
}),
navItems,
quickActions,
translations,
ready({ router, store, globalProperties }: AppReadyHookArgs) {
const { $clientService } = globalProperties
Registry.sdkSearch = new SDKSearch(store, router, $clientService, configurationManager)

// when discussing the boot process of applications we need to implement a
// registry that does not rely on call order, aka first register "on" and only after emit.
eventBus.publish('app.search.register.provider', Registry.sdkSearch)
}
}
})
7 changes: 1 addition & 6 deletions packages/web-app-files/src/search/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Preview from './preview'
import List from './list'
import { EventBus } from '@ownclouders/web-pkg'
import { Store } from 'vuex'
import { Router } from 'vue-router'
import {
@@ -14,28 +13,24 @@ import {
function $gettext(msg) {
return msg
}
export default class Provider extends EventBus implements SearchProvider {
export default class Provider implements SearchProvider {
public readonly id: string
public readonly displayName: string
public readonly previewSearch: SearchPreview
public readonly listSearch: SearchList
private readonly store: Store<any>
private readonly router: Router

constructor(
store: Store<any>,
router: Router,
clientService: ClientService,
configurationManager: ConfigurationManager
) {
super()

this.id = 'files.sdk'
this.displayName = $gettext('Files')
this.previewSearch = new Preview(store, router, clientService, configurationManager)
this.listSearch = new List(store, clientService, configurationManager)
this.store = store
this.router = router
}

public get available(): boolean {
10 changes: 5 additions & 5 deletions packages/web-app-files/tests/unit/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import WebAppFiles from '../../src/index'
import { navItems } from '../../src/index'

describe('Web app files', () => {
describe('navItems', () => {
describe('Personal', () => {
it('should be enabled if user has a personal space', () => {
const navItems = WebAppFiles.navItems({
const items = navItems({
$store: {
getters: {
'runtime/spaces/spaces': [{ id: '1', driveType: 'personal', isOwner: () => true }]
}
}
})
expect(navItems[0].enabled({ spaces: { enabled: true } })).toBeTruthy()
expect(items[0].enabled({ spaces: { enabled: true } })).toBeTruthy()
})
it('should be disabled if user has no a personal space', () => {
const navItems = WebAppFiles.navItems({
const items = navItems({
$store: {
getters: {
'runtime/spaces/spaces': [{ id: '1', driveType: 'project', isOwner: () => false }]
}
}
})
expect(navItems[0].enabled({ spaces: { enabled: true } })).toBeFalsy()
expect(items[0].enabled({ spaces: { enabled: true } })).toBeFalsy()
})
})
})
1 change: 1 addition & 0 deletions packages/web-app-search/src/composables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useAvailableProviders'
14 changes: 14 additions & 0 deletions packages/web-app-search/src/composables/useAvailableProviders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SearchExtension, SearchProvider, useExtensionRegistry } from '@ownclouders/web-pkg'
import { computed, Ref } from 'vue'

export const useAvailableProviders = (): Ref<SearchProvider[]> => {
const extensionRegistry = useExtensionRegistry()

const availableProviders = computed(() => {
return extensionRegistry
.requestExtensions<SearchExtension>('search')
.map(({ searchProvider }) => searchProvider)
})

return availableProviders
}
6 changes: 0 additions & 6 deletions packages/web-app-search/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import SearchBar from './portals/SearchBar.vue'
import App from './App.vue'
import List from './views/List.vue'
import { providerStore } from './service'
import { eventBus, SearchProvider } from '@ownclouders/web-pkg'
import { Component } from 'vue'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -13,10 +11,6 @@ const $gettext = (msg) => {
return msg
}

eventBus.subscribe('app.search.register.provider', (provider: SearchProvider) => {
providerStore.addProvider(provider)
})

export default {
appInfo: {
name: $gettext('Search'),
30 changes: 19 additions & 11 deletions packages/web-app-search/src/portals/SearchBar.vue
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
:placeholder="searchLabel"
:button-hidden="true"
:show-cancel-button="showCancelButton"
:show-advanced-search-button="listProviderAvailable"
cancel-button-variation="brand"
cancel-button-appearance="raw-inverse"
:cancel-handler="cancelSearch"
@@ -29,6 +30,7 @@
>
<template #locationFilter>
<search-bar-filter
v-if="locationFilterAvailable"
:current-folder-available="currentFolderAvailable"
@update:model-value="onLocationFilterChange"
/>
@@ -103,7 +105,6 @@
</template>

<script lang="ts">
import { providerStore } from '../service'
import {
createLocationCommon,
isLocationCommonActive,
@@ -116,6 +117,7 @@ import { eventBus } from '@ownclouders/web-pkg'
import { computed, defineComponent, GlobalComponents, inject, Ref, ref, unref, watch } from 'vue'
import { SearchLocationFilterConstants } from '@ownclouders/web-pkg'
import { SearchBarFilter } from '@ownclouders/web-pkg'
import { useAvailableProviders } from '../composables'

export default defineComponent({
name: 'SearchBar',
@@ -126,7 +128,7 @@ export default defineComponent({
const showCancelButton = ref(false)
const isMobileWidth = inject<Ref<boolean>>('isMobileWidth')
const scopeQueryValue = useRouteQuery('scope')
const shareId = useRouteQuery('shareId')
const availableProviders = useAvailableProviders()
const locationFilterId = ref(SearchLocationFilterConstants.everywhere)
const optionsDropRef = ref(null)
const activePreviewIndex = ref(null)
@@ -135,6 +137,15 @@ export default defineComponent({
const loading = ref(false)
const currentFolderAvailable = ref(false)

const listProviderAvailable = computed(() =>
unref(availableProviders).some((p) => !!p.listSearch)
)

const locationFilterAvailable = computed(() =>
// FIXME: use capability as soon as we have one
unref(availableProviders).some((p) => !!p.listSearch)
)

watch(isMobileWidth, () => {
const searchBarEl = document.getElementById('files-global-search-bar')
const optionDropVisible = !!document.querySelector('.tippy-box[data-state="visible"]')
@@ -153,18 +164,10 @@ export default defineComponent({
}
})

const isShareRoute = () => {
return !!shareId.value
}

const optionsDrop = computed(() => {
return unref(optionsDropRef) as InstanceType<GlobalComponents['OcDrop']>
})

const availableProviders = computed(() => {
return unref(providerStore)?.availableProviders
})

const search = async () => {
searchResults.value = []
if (!unref(term)) {
@@ -202,6 +205,10 @@ export default defineComponent({
}

const onKeyUpEnter = () => {
if (!unref(listProviderAvailable)) {
return
}

if (unref(optionsDrop)) {
unref(optionsDrop).hide()
}
@@ -273,6 +280,8 @@ export default defineComponent({
showCancelButton,
onLocationFilterChange,
currentFolderAvailable,
listProviderAvailable,
locationFilterAvailable,
store,
scopeQueryValue,
optionsDrop,
@@ -282,7 +291,6 @@ export default defineComponent({
onKeyUpEnter,
searchResults,
loading,
providerStore,
availableProviders,
search,
showPreview,
1 change: 0 additions & 1 deletion packages/web-app-search/src/service/index.ts

This file was deleted.

Loading