Skip to content

Commit

Permalink
Merge pull request #10102 from owncloud/quickActionsInjectionContext
Browse files Browse the repository at this point in the history
refactor: register file quick actions as extensions
  • Loading branch information
JammingBen authored Dec 1, 2023
2 parents 35f1c3c + 010fecf commit ff110ff
Show file tree
Hide file tree
Showing 23 changed files with 153 additions and 189 deletions.
8 changes: 8 additions & 0 deletions changelog/unreleased/enhancement-quick-actions-via-extension
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: Registering quick actions as extension

Quick actions can now registered as extension via our extension registry. They need to be of type `action` and have the `files.quick-action` scope.

The old way of registering quick actions via the `quickaction` property of an app is now officially deprecated.

https://github.com/owncloud/web/pull/10102
https://github.com/owncloud/web/issues/7338
58 changes: 22 additions & 36 deletions packages/web-app-files/src/components/FilesList/QuickActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,50 @@
<div v-if="!isEmbedModeEnabled" class="oc-flex">
<oc-button
v-for="action in filteredActions"
:key="action.label($gettext)"
v-oc-tooltip="action.label($gettext)"
:aria-label="action.label($gettext)"
:key="action.label()"
v-oc-tooltip="action.label()"
:aria-label="action.label()"
appearance="raw"
class="oc-mr-xs quick-action-button oc-p-xs"
:class="`files-quick-action-${action.id}`"
@click="
action.handler({ ability, clientService, passwordPolicyService, item, language, store })
"
:class="`files-quick-action-${action.name}`"
@click="action.handler({ space, resources: [item] })"
>
<oc-icon :name="action.icon" fill-type="line" />
</oc-button>
</div>
</template>

<script lang="ts">
import pickBy from 'lodash-es/pickBy'
import { computed, defineComponent } from 'vue'
import {
useAbility,
useClientService,
useEmbedMode,
usePasswordPolicyService,
useStore
} from '@ownclouders/web-pkg'
import { useGettext } from 'vue3-gettext'
import { computed, defineComponent, PropType } from 'vue'
import { ActionExtension, useEmbedMode, useExtensionRegistry } from '@ownclouders/web-pkg'
import { Resource, SpaceResource } from '@ownclouders/web-client'
import { unref } from 'vue'
export default defineComponent({
name: 'QuickActions',
props: {
actions: {
type: Object,
required: true
},
item: {
type: Object,
type: Object as PropType<Resource>,
required: true
},
space: {
type: Object as PropType<SpaceResource>,
default: undefined
}
},
setup(props) {
const store = useStore()
const ability = useAbility()
const clientService = useClientService()
const passwordPolicyService = usePasswordPolicyService()
const language = useGettext()
const extensionRegistry = useExtensionRegistry()
const { isEnabled: isEmbedModeEnabled } = useEmbedMode()
const filteredActions = computed(() =>
pickBy(props.actions, (action) => action.displayed(props.item, store, ability) === true)
)
const filteredActions = computed(() => {
return unref(extensionRegistry)
.requestExtensions<ActionExtension>('action')
.filter(({ scopes }) => scopes.includes('files.quick-action'))
.map((e) => e.action)
.filter(({ isEnabled }) => isEnabled({ space: props.space, resources: [props.item] }))
})
return {
ability,
clientService,
passwordPolicyService,
store,
language,
filteredActions,
isEmbedModeEnabled
}
Expand Down
21 changes: 19 additions & 2 deletions packages/web-app-files/src/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,40 @@ import {
Extension,
useStore,
useRouter,
useSearch
useSearch,
useFileActionsShowShares,
useFileActionsCreateQuickLink
} from '@ownclouders/web-pkg'
import { computed } from 'vue'
import { computed, unref } from 'vue'
import { SDKSearch } from './search'

export const extensions = ({ applicationConfig }: ApplicationSetupOptions) => {
const store = useStore()
const router = useRouter()
const { search: searchFunction } = useSearch()

const { actions: showSharesActions } = useFileActionsShowShares()
const { actions: quickLinkActions } = useFileActionsCreateQuickLink()

return computed(
() =>
[
{
id: 'com.github.owncloud.web.files.search',
type: 'search',
searchProvider: new SDKSearch(store, router, searchFunction)
},
{
id: 'com.github.owncloud.web.files.quick-action.collaborator',
scopes: ['files', 'files.quick-action'],
type: 'action',
action: unref(showSharesActions)[0]
},
{
id: 'com.github.owncloud.web.files.quick-action.quicklink',
scopes: ['files', 'files.quick-action'],
type: 'action',
action: unref(quickLinkActions)[0]
}
] satisfies Extension[]
)
Expand Down
3 changes: 1 addition & 2 deletions packages/web-app-files/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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 { defineWebApplication, quickActions } from '@ownclouders/web-pkg'
import { defineWebApplication } from '@ownclouders/web-pkg'
import store from './store'
import { extensions } from './extensions'
import fileSideBars from './fileSideBars'
Expand Down Expand Up @@ -151,7 +151,6 @@ export default defineWebApplication({
}
}),
navItems,
quickActions,
translations,
extensions: extensions(args)
}
Expand Down
6 changes: 1 addition & 5 deletions packages/web-app-files/src/views/Favorites.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@
@sort="handleSort"
>
<template #quickActions="props">
<quick-actions
class="oc-visible@s"
:item="props.resource"
:actions="app.quickActions"
/>
<quick-actions class="oc-visible@s" :item="props.resource" />
</template>
<template #contextMenu="{ resource }">
<context-actions
Expand Down
2 changes: 1 addition & 1 deletion packages/web-app-files/src/views/spaces/GenericSpace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@
<quick-actions
:class="resource.preview"
class="oc-visible@s"
:space="space"
:item="resource"
:actions="app.quickActions"
/>
</template>
<template #contextMenu="{ resource }">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { useEmbedMode } from '@ownclouders/web-pkg'
import { ActionExtension, useEmbedMode } from '@ownclouders/web-pkg'
import QuickActions from '../../../../src/components/FilesList/QuickActions.vue'
import { defaultComponentMocks, defaultPlugins, shallowMount } from 'web-test-helpers'
import { useExtensionRegistry } from '@ownclouders/web-pkg'
import { mock } from 'jest-mock-extended'
import { ref } from 'vue'
import { useExtensionRegistryMock } from 'web-test-helpers/src/mocks/useExtensionRegistryMock'

jest.mock('@ownclouders/web-pkg', () => ({
...jest.requireActual('@ownclouders/web-pkg'),
useEmbedMode: jest.fn()
useEmbedMode: jest.fn(),
useExtensionRegistry: jest.fn()
}))

const collaboratorAction = {
displayed: jest.fn(() => true),
isEnabled: jest.fn(() => true),
handler: jest.fn(),
icon: 'group-add',
id: 'collaborators',
name: 'show-shares',
label: () => 'Add people'
}

const quicklinkAction = {
displayed: jest.fn(() => false),
isEnabled: jest.fn(() => false),
handler: jest.fn(),
icon: 'link-add',
id: 'quicklink',
name: 'create-quicklink',
label: () => 'Create and copy quicklink'
}

Expand All @@ -45,14 +50,14 @@ describe('QuickActions', () => {
const iconEl = actionButton.find('oc-icon-stub')

expect(actionButton.exists()).toBeTruthy()
expect(actionButton.attributes().class).toContain('files-quick-action-collaborators')
expect(actionButton.attributes().class).toContain('files-quick-action-show-shares')
expect(iconEl.exists()).toBeTruthy()
expect(iconEl.attributes().name).toBe('group-add')
expect(actionButton.attributes('aria-label')).toBe('Add people')
})

it('should not display action buttons where "displayed" is set to false', () => {
const linkActionButton = wrapper.find('.files-quick-action-public-link')
const linkActionButton = wrapper.find('.files-quick-action-create-quicklink')

expect(linkActionButton.exists()).toBeFalsy()
})
Expand All @@ -66,9 +71,6 @@ describe('QuickActions', () => {
const actionButton = wrapper.find('.oc-button')
await actionButton.trigger('click')
expect(handlerAction).toHaveBeenCalledTimes(1)
Object.keys(testItem).forEach((key) => {
expect(handlerAction.mock.calls[0][0].item[key]).toBe(testItem[key])
})
})
})

Expand All @@ -83,13 +85,19 @@ function getWrapper({ embedModeEnabled = false } = {}) {
.mocked(useEmbedMode)
.mockReturnValue(mock<ReturnType<typeof useEmbedMode>>({ isEnabled: ref(embedModeEnabled) }))

jest.mocked(useExtensionRegistry).mockImplementation(() =>
useExtensionRegistryMock({
requestExtensions: () =>
[
mock<ActionExtension>({ scopes: ['files.quick-action'], action: collaboratorAction }),
mock<ActionExtension>({ scopes: ['files.quick-action'], action: quicklinkAction })
] as any
})
)

return {
wrapper: shallowMount(QuickActions, {
props: {
actions: {
collaborators: collaboratorAction,
publicLink: quicklinkAction
},
item: testItem
},
global: {
Expand Down
7 changes: 3 additions & 4 deletions packages/web-pkg/src/apps/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export interface AppNavigationItem {
/**
* ApplicationQuickAction describes an application action that is used in the runtime.
*
* @deprecated In the future quick actions should be registered just like any other extension. Fine
* to use this interface for now, but it will be changed in the near future.
* @deprecated Quick actions should be registered as extension via the `files.quick-action` scope.
*/
export interface ApplicationQuickAction {
id?: string
Expand All @@ -43,8 +42,7 @@ export interface ApplicationQuickAction {
/**
* ApplicationQuickActions describes a map of application actions that are used in the runtime
*
* @deprecated In the future quick actions should be registered just like any other extension. Fine
* to use this interface for now, but it will be changed in the near future.
* @deprecated Quick actions should be registered as extension via the `files.quick-action` scope.
*/
export interface ApplicationQuickActions {
[key: string]: ApplicationQuickAction
Expand Down Expand Up @@ -85,6 +83,7 @@ export interface ClassicApplicationScript {
store?: Module<unknown, unknown>
routes?: ((...args) => RouteRecordRaw[]) | RouteRecordRaw[]
navItems?: ((...args) => AppNavigationItem[]) | AppNavigationItem[]
/** @deprecated Quick actions should be registered as extension via the `files.quick-action` scope. */
quickActions?: ApplicationQuickActions
translations?: ApplicationTranslations
extensions?: Ref<Extension[]>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import quickActions, { canShare } from '../../../quickActions'
import { copyQuicklink } from '../../../helpers/share'
import { ShareStatus } from '@ownclouders/web-client/src/helpers/share'

Expand All @@ -12,6 +11,7 @@ import { useGettext } from 'vue3-gettext'
import { Store } from 'vuex'
import { FileAction, FileActionOptions } from '../types'
import { usePasswordPolicyService } from '../../passwordPolicyService'
import { useCanShare } from '../../shares'

export const useFileActionsCreateQuickLink = ({
store
Expand All @@ -25,6 +25,7 @@ export const useFileActionsCreateQuickLink = ({
const ability = useAbility()
const clientService = useClientService()
const passwordPolicyService = usePasswordPolicyService()
const { canShare } = useCanShare()

const handler = async ({ space, resources }: FileActionOptions) => {
const [resource] = resources
Expand All @@ -43,8 +44,7 @@ export const useFileActionsCreateQuickLink = ({
const actions = computed((): FileAction[] => [
{
name: 'create-quicklink',
icon: quickActions.quicklink.icon,
iconFillType: quickActions.quicklink.iconFillType,
icon: 'link',
label: () => $gettext('Copy link'),
handler,
isEnabled: ({ resources }) => {
Expand All @@ -56,7 +56,7 @@ export const useFileActionsCreateQuickLink = ({
return false
}
}
return canShare(resources[0], store, ability)
return canShare(resources[0])
},
componentType: 'button',
class: 'oc-files-actions-create-quicklink-trigger'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import quickActions, { canShare } from '../../../quickActions'
import { isLocationSharesActive, isLocationTrashActive } from '../../../router'
import { ShareStatus } from '@ownclouders/web-client/src/helpers/share'
import { eventBus } from '../../../services'
Expand All @@ -10,14 +9,14 @@ import { useRouter } from '../../router'
import { useStore } from '../../store'
import { Store } from 'vuex'
import { FileAction, FileActionOptions } from '../types'
import { useAbility } from '../../ability'
import { useCanShare } from '../../shares'

export const useFileActionsShowShares = ({ store }: { store?: Store<any> } = {}) => {
store = store || useStore()
const router = useRouter()
const ability = useAbility()
const { $gettext } = useGettext()
const isFilesAppActive = useIsFilesAppActive()
const { canShare } = useCanShare()

const handler = ({ resources }: FileActionOptions) => {
store.commit('Files/SET_FILE_SELECTION', resources)
Expand All @@ -27,8 +26,7 @@ export const useFileActionsShowShares = ({ store }: { store?: Store<any> } = {})
const actions = computed((): FileAction[] => [
{
name: 'show-shares',
icon: quickActions.collaborators.icon,
iconFillType: quickActions.collaborators.iconFillType,
icon: 'user-add',
label: () => $gettext('Share'),
handler,
isEnabled: ({ resources }) => {
Expand All @@ -48,7 +46,7 @@ export const useFileActionsShowShares = ({ store }: { store?: Store<any> } = {})
return false
}
}
return canShare(resources[0], store, ability)
return canShare(resources[0])
},
componentType: 'button',
class: 'oc-files-actions-show-shares-trigger'
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/src/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export * from './scrollTo'
export * from './search'
export * from './selection'
export * from './service'
export * from './shares'
export * from './sideBar'
export * from './sort'
export * from './spaces'
Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/src/composables/shares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useCanShare'
Loading

0 comments on commit ff110ff

Please sign in to comment.