Skip to content

Commit

Permalink
refactor: shortcut modal usage
Browse files Browse the repository at this point in the history
Before, the state of the shortcut modal was handled by the outside where it was being used. Refactors the modal usage so that the modal state handling is done via the create shortcut action instead, meaning the consuming party doesn't need to care about it anymore.
  • Loading branch information
JammingBen committed Dec 15, 2023
1 parent 1b039c5 commit 249d9a5
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 158 deletions.
17 changes: 2 additions & 15 deletions packages/web-app-files/src/components/AppBar/CreateAndUpload.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
<template>
<create-shortcut-modal
v-if="isCreateNewShortcutModalOpen"
:space="space"
:cancel="closeCreateNewShortcutModal"
/>
<div v-if="showActions" class="create-and-upload-actions oc-flex-inline oc-mr-s">
<template v-if="createFileActionsAvailable">
<span v-oc-tooltip="newButtonTooltip">
Expand Down Expand Up @@ -212,7 +207,6 @@ import {
} from '@ownclouders/web-pkg'
import ResourceUpload from './Upload/ResourceUpload.vue'
import CreateShortcutModal from '@ownclouders/web-pkg/src/components/CreateShortcutModal.vue'
import {
computed,
Expand All @@ -236,8 +230,7 @@ import { v4 as uuidv4 } from 'uuid'
export default defineComponent({
components: {
ResourceUpload,
CreateShortcutModal
ResourceUpload
},
props: {
space: {
Expand Down Expand Up @@ -294,11 +287,7 @@ export default defineComponent({
})
const createNewFolderAction = computed(() => unref(createNewFolder)[0].handler)
const {
actions: createNewShortcut,
modalOpen: isCreateNewShortcutModalOpen,
closeModal: closeCreateNewShortcutModal
} = useFileActionsCreateNewShortcut({ store })
const { actions: createNewShortcut } = useFileActionsCreateNewShortcut({ space: props.space })
const createNewShortcutAction = computed(() => unref(createNewShortcut)[0].handler)
Expand Down Expand Up @@ -450,8 +439,6 @@ export default defineComponent({
isActionDisabled,
actionKeySuffix,
showDrop,
isCreateNewShortcutModalOpen,
closeCreateNewShortcutModal,
areFileExtensionsShown,
// HACK: exported for unit tests:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CreateAndUpload component action buttons should show and be enabled if file creation is possible 1`] = `
<!--v-if-->
<div class="create-and-upload-actions oc-flex-inline oc-mr-s">
<span>
<button aria-label="New folder" class="oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-primary oc-button-primary-filled" id="new-folder-btn" type="button">
Expand Down Expand Up @@ -36,8 +34,6 @@ exports[`CreateAndUpload component action buttons should show and be enabled if
`;

exports[`CreateAndUpload component file handlers should show additional handlers 1`] = `
<!--v-if-->
<div class="create-and-upload-actions oc-flex-inline oc-mr-s">
<span>
<button aria-label="Create new files or folders" class="oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-primary oc-button-primary-filled" id="new-file-menu-btn" type="button">
Expand Down Expand Up @@ -120,8 +116,6 @@ exports[`CreateAndUpload component file handlers should show additional handlers
`;

exports[`CreateAndUpload component file handlers should show file extension if file extensions are enabled 1`] = `
<!--v-if-->
<div class="create-and-upload-actions oc-flex-inline oc-mr-s">
<span>
<button aria-label="Create new files or folders" class="oc-button oc-rounded oc-button-m oc-button-justify-content-center oc-button-gap-m oc-button-primary oc-button-primary-filled" id="new-file-menu-btn" type="button">
Expand Down
211 changes: 102 additions & 109 deletions packages/web-pkg/src/components/CreateShortcutModal.vue
Original file line number Diff line number Diff line change
@@ -1,98 +1,97 @@
<template>
<portal to="app.runtime.modal">
<oc-modal
:title="$gettext('Create a Shortcut')"
:button-cancel-text="$gettext('Cancel')"
:button-confirm-text="$gettext('Create')"
:button-confirm-disabled="confirmButtonDisabled"
@cancel="cancel"
@confirm="createShortcut(inputUrl, inputFilename)"
@keydown.enter="onKeyEnter"
>
<template #content>
<oc-text-input
id="create-shortcut-modal-url-input"
v-model="inputUrl"
:label="$gettext('Shortcut to a webpage or file')"
@keydown.up="onKeyUpDrop"
@keydown.down="onKeyDownDrop"
@keydown.esc="onKeyEscDrop"
@keydown.enter="onKeyEnterDrop"
@input="onInputUrlInput"
@click="onClickUrlInput"
/>
<oc-drop
ref="dropRef"
class="oc-pt-s"
padding-size="remove"
drop-id="create-shortcut-modal-contextmenu"
mode="manual"
position="bottom-start"
:close-on-click="true"
@hide-drop="onHideDrop"
@show-drop="onShowDrop"
<oc-text-input
id="create-shortcut-modal-url-input"
v-model="inputUrl"
:label="$gettext('Shortcut to a webpage or file')"
@keydown.up="onKeyUpDrop"
@keydown.down="onKeyDownDrop"
@keydown.esc="onKeyEscDrop"
@keydown.enter="onKeyEnterDrop"
@input="onInputUrlInput"
@click="onClickUrlInput"
/>
<oc-drop
ref="dropRef"
class="oc-pt-s"
padding-size="remove"
drop-id="create-shortcut-modal-contextmenu"
mode="manual"
position="bottom-start"
:close-on-click="true"
@hide-drop="onHideDrop"
@show-drop="onShowDrop"
>
<oc-list>
<li
class="oc-p-xs selectable-item selectable-item-url"
:class="{
active: isDropItemActive(0)
}"
>
<oc-button
class="oc-width-1-1"
appearance="raw"
justify-content="left"
@click="dropItemUrlClicked"
>
<oc-list>
<li
class="oc-p-xs selectable-item selectable-item-url"
:class="{
active: isDropItemActive(0)
}"
>
<oc-button
class="oc-width-1-1"
appearance="raw"
justify-content="left"
@click="dropItemUrlClicked"
>
<oc-icon name="external-link" />
<span v-text="dropItemUrl" />
</oc-button>
</li>
<li v-if="searchTask.isRunning" class="oc-p-xs oc-flex oc-flex-center">
<oc-spinner />
</li>
<template v-if="searchResult?.values?.length">
<li
class="create-shortcut-modal-search-separator oc-text-muted oc-text-small oc-pl-xs"
>
<span v-text="$gettext('Link to a file')" />
</li>
<li
v-for="(value, index) in searchResult.values"
:key="index"
class="oc-p-xs selectable-item"
:class="{
active: isDropItemActive(index + 1)
}"
>
<oc-button
class="oc-width-1-1"
appearance="raw"
justify-content="left"
@click="dropItemResourceClicked(value)"
>
<resource-preview :search-result="value" :is-clickable="false" />
</oc-button>
</li>
</template>
</oc-list>
</oc-drop>
<div class="oc-flex oc-width-1-1 oc-mt-m">
<oc-text-input
v-model="inputFilename"
<oc-icon name="external-link" />
<span v-text="dropItemUrl" />
</oc-button>
</li>
<li v-if="searchTask.isRunning" class="oc-p-xs oc-flex oc-flex-center">
<oc-spinner />
</li>
<template v-if="searchResult?.values?.length">
<li class="create-shortcut-modal-search-separator oc-text-muted oc-text-small oc-pl-xs">
<span v-text="$gettext('Link to a file')" />
</li>
<li
v-for="(value, index) in searchResult.values"
:key="index"
class="oc-p-xs selectable-item"
:class="{
active: isDropItemActive(index + 1)
}"
>
<oc-button
class="oc-width-1-1"
:label="$gettext('Shortcut name')"
:error-message="inputFileNameErrorMessage"
:fix-message-line="true"
/>
<span class="oc-ml-s oc-flex oc-flex-bottom create-shortcut-modal-url-extension"
>.url</span
appearance="raw"
justify-content="left"
@click="dropItemResourceClicked(value)"
>
</div>
<resource-preview :search-result="value" :is-clickable="false" />
</oc-button>
</li>
</template>
</oc-modal>
</portal>
</oc-list>
</oc-drop>
<div class="oc-flex oc-width-1-1 oc-mt-m">
<oc-text-input
v-model="inputFilename"
class="oc-width-1-1"
:label="$gettext('Shortcut name')"
:error-message="inputFileNameErrorMessage"
:fix-message-line="true"
/>
<span class="oc-ml-s oc-flex oc-flex-bottom create-shortcut-modal-url-extension">.url</span>
</div>
<div class="oc-flex oc-flex-right oc-flex-middle oc-mt-m">
<oc-button
class="oc-modal-body-actions-cancel oc-ml-s"
appearance="outline"
variation="passive"
@click="onCancel"
>{{ $gettext('Cancel') }}
</oc-button>
<oc-button
class="oc-modal-body-actions-confirm oc-ml-s"
appearance="filled"
variation="primary"
:disabled="confirmButtonDisabled"
@click="onConfirm"
>{{ $gettext('Create') }}
</oc-button>
</div>
</template>

<script lang="ts">
Expand Down Expand Up @@ -131,13 +130,9 @@ export default defineComponent({
space: {
type: Object as PropType<SpaceResource>,
required: true
},
cancel: {
type: Function as PropType<(...args: any) => unknown>,
required: true
}
},
setup(props) {
setup(props, { expose }) {
const clientService = useClientService()
const { $gettext } = useGettext()
const store = useStore()
Expand Down Expand Up @@ -234,12 +229,6 @@ export default defineComponent({
inputFilename.value = filename
}
const onKeyEnter = () => {
if (!unref(confirmButtonDisabled)) {
createShortcut(unref(inputUrl), unref(inputFilename))
}
}
const isDropItemActive = (index) => {
return unref(activeDropItemIndex) === index
}
Expand Down Expand Up @@ -335,21 +324,19 @@ export default defineComponent({
}
}
const createShortcut = async (url: string, filename: string) => {
// Closes the modal
props.cancel()
const onConfirm = async () => {
try {
// Omit possible xss code
const sanitizedUrl = DOMPurify.sanitize(url, { USE_PROFILES: { html: true } })
const sanitizedUrl = DOMPurify.sanitize(unref(inputUrl), { USE_PROFILES: { html: true } })
const content = `[InternetShortcut]\nURL=${sanitizedUrl}`
const path = urlJoin(unref(currentFolder).path, `${filename}.url`)
const path = urlJoin(unref(currentFolder).path, `${unref(inputFilename)}.url`)
const resource = await clientService.webdav.putFileContents(props.space, {
path,
content
})
store.commit('Files/UPSERT_RESOURCE', resource)
store.dispatch('hideModal')
store.dispatch('showMessage', {
title: $gettext('Shortcut was created successfully')
})
Expand All @@ -362,6 +349,10 @@ export default defineComponent({
}
}
const onCancel = () => {
store.dispatch('hideModal')
}
onMounted(async () => {
await nextTick()
markInstance = new Mark(unref(dropRef)?.$refs?.drop)
Expand Down Expand Up @@ -401,6 +392,8 @@ export default defineComponent({
)
})
expose({ onConfirm, onCancel })
return {
inputUrl,
inputFilename,
Expand All @@ -410,8 +403,8 @@ export default defineComponent({
confirmButtonDisabled,
inputFileNameErrorMessage,
searchTask,
createShortcut,
onKeyEnter,
onConfirm,
onCancel,
dropItemUrlClicked,
dropItemResourceClicked,
getPathPrefix,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { Resource } from '@ownclouders/web-client/src/helpers'
import { Store } from 'vuex'
import { computed, unref, ref } from 'vue'
import { Resource, SpaceResource } from '@ownclouders/web-client/src/helpers'
import { computed, unref } from 'vue'
import { useStore } from '../../store'
import { FileAction } from '../../../'
import { FileAction } from '../../../composables'
import { CreateShortcutModal } from '../../../components'
import { useGettext } from 'vue3-gettext'

export const useFileActionsCreateNewShortcut = ({ store }: { store?: Store<any> } = {}) => {
store = store || useStore()
export const useFileActionsCreateNewShortcut = ({ space }: { space: SpaceResource }) => {
const store = useStore()
const { $gettext } = useGettext()
const currentFolder = computed((): Resource => store.getters['Files/currentFolder'])

const modalOpen = ref(false)

const closeModal = () => {
modalOpen.value = false
}

const handler = () => {
modalOpen.value = true
return store.dispatch('createModal', {
title: $gettext('Create a Shortcut'),
hideActions: true,
customComponent: CreateShortcutModal,
customComponentAttrs: { space }
})
}

const actions = computed((): FileAction[] => {
Expand All @@ -39,8 +38,6 @@ export const useFileActionsCreateNewShortcut = ({ store }: { store?: Store<any>
})

return {
actions,
modalOpen,
closeModal
actions
}
}
Loading

0 comments on commit 249d9a5

Please sign in to comment.