Skip to content

Commit

Permalink
refactor: search provider registration
Browse files Browse the repository at this point in the history
  • Loading branch information
JammingBen committed Oct 12, 2023
1 parent 923d73b commit fd1a21d
Show file tree
Hide file tree
Showing 28 changed files with 271 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
*/
Expand Down
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
Expand Up @@ -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'
Expand All @@ -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) {
Expand All @@ -40,7 +37,7 @@ const appInfo = {
extensions: [],
fileSideBars
}
const navItems = (context): AppNavigationItem[] => {
export const navItems = (context): AppNavigationItem[] => {
return [
{
name(capabilities) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
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()
})
})
})
Expand Down
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
Expand All @@ -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'),
Expand Down
30 changes: 19 additions & 11 deletions packages/web-app-search/src/portals/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -29,6 +30,7 @@
>
<template #locationFilter>
<search-bar-filter
v-if="locationFilterAvailable"
:current-folder-available="currentFolderAvailable"
@update:model-value="onLocationFilterChange"
/>
Expand Down Expand Up @@ -103,7 +105,6 @@
</template>

<script lang="ts">
import { providerStore } from '../service'
import {
createLocationCommon,
isLocationCommonActive,
Expand All @@ -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',
Expand All @@ -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)
Expand All @@ -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"]')
Expand All @@ -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)) {
Expand Down Expand Up @@ -202,6 +205,10 @@ export default defineComponent({
}
const onKeyUpEnter = () => {
if (!unref(listProviderAvailable)) {
return
}
if (unref(optionsDrop)) {
unref(optionsDrop).hide()
}
Expand Down Expand Up @@ -273,6 +280,8 @@ export default defineComponent({
showCancelButton,
onLocationFilterChange,
currentFolderAvailable,
listProviderAvailable,
locationFilterAvailable,
store,
scopeQueryValue,
optionsDrop,
Expand All @@ -282,7 +291,6 @@ export default defineComponent({
onKeyUpEnter,
searchResults,
loading,
providerStore,
availableProviders,
search,
showPreview,
Expand Down
1 change: 0 additions & 1 deletion packages/web-app-search/src/service/index.ts

This file was deleted.

Loading

0 comments on commit fd1a21d

Please sign in to comment.