-
Notifications
You must be signed in to change notification settings - Fork 157
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
[full-ci] Refactor CreateAndUpload into composables #8938
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
6787465
WIP
lookacat 667e727
Make createFolder work
lookacat 2a685cd
Fix unref files
lookacat a98ff9a
Refactor useFileCreateNewFolder, implement useFileCreateNewFile
lookacat f41a535
Implement composables into CreateAndUpload
lookacat 4bc18e4
Refactor CreateAndUpload
lookacat 69423d3
Reimplement addAppProviderFileFunc
lookacat 55b9d7d
Fix import
lookacat 07d5bf7
Make linter happy
lookacat a7282a3
PoC: let CreateNewFile composable accumulate available actions
JammingBen 74f4174
Address PR issues
lookacat e375562
remove dev leftover
lookacat ad4b4bb
Add basic tests for file/folder name validation
lookacat cdfe227
Remove old CreateAndUpload.spec unittests
lookacat cf78597
Address PR issues
lookacat fa0a8cf
Address PR issue
lookacat c92da28
Implement working unittests for useFileActionsCreateNewFolder.spec.ts
lookacat 8bfdfe3
Implement working unittests for useFileActionsCreateNewFile
lookacat 100a8cb
Fix file extension and mime type fiel actions
JammingBen 5791cef
Remove unrelated file
JammingBen 3de5db8
Make linter happy
lookacat 0945b4f
Fix ref error in unittest
lookacat b9d4b14
Fix space issue, fix locationspace generic
lookacat 08c264f
Remove ref from space
lookacat 8d4e6e5
Fix unittests
lookacat 18a7a4c
Remove accidental change
lookacat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
335 changes: 55 additions & 280 deletions
335
packages/web-app-files/src/components/AppBar/CreateAndUpload.vue
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
255 changes: 255 additions & 0 deletions
255
packages/web-app-files/src/composables/actions/files/useFileActionsCreateNewFile.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
import { Resource, SpaceResource, extractNameWithoutExtension } from 'web-client/src/helpers' | ||
import { Store } from 'vuex' | ||
import { computed, Ref, unref } from 'vue' | ||
import { useClientService, useRequest, useRouter, useStore } from 'web-pkg/src/composables' | ||
import { FileAction, FileActionOptions } from 'web-pkg/src/composables/actions' | ||
import { useGettext } from 'vue3-gettext' | ||
import { resolveFileNameDuplicate } from 'web-app-files/src/helpers/resource' | ||
import { join } from 'path' | ||
import { WebDAV } from 'web-client/src/webdav' | ||
import { isLocationSpacesActive } from 'web-app-files/src/router' | ||
import { getIndicators } from 'web-app-files/src/helpers/statusIndicators' | ||
import { EDITOR_MODE_CREATE, useFileActions } from './useFileActions' | ||
import { urlJoin } from 'web-client/src/utils' | ||
import { configurationManager } from 'web-pkg/src' | ||
import { stringify } from 'qs' | ||
|
||
export const useFileActionsCreateNewFile = ({ | ||
store, | ||
space, | ||
newFileHandlers, | ||
mimetypesAllowedForCreation | ||
}: { | ||
store?: Store<any> | ||
space?: SpaceResource | ||
newFileHandlers?: Ref<any> // FIXME: type? | ||
mimetypesAllowedForCreation?: Ref<any> // FIXME: type? | ||
} = {}) => { | ||
store = store || useStore() | ||
const router = useRouter() | ||
const { $gettext } = useGettext() | ||
const { makeRequest } = useRequest() | ||
|
||
const { openEditor, triggerDefaultAction } = useFileActions() | ||
const clientService = useClientService() | ||
const currentFolder = computed((): Resource => store.getters['Files/currentFolder']) | ||
const files = computed((): Array<Resource> => store.getters['Files/files']) | ||
const ancestorMetaData = computed(() => store.getters['Files/ancestorMetaData']) | ||
const areFileExtensionsShown = computed((): boolean => store.state.Files.areFileExtensionsShown) | ||
|
||
const capabilities = computed(() => store.getters['capabilities']) | ||
|
||
const checkNewFileName = (fileName) => { | ||
if (fileName === '') { | ||
return $gettext('File name cannot be empty') | ||
} | ||
|
||
if (/[/]/.test(fileName)) { | ||
return $gettext('File name cannot contain "/"') | ||
} | ||
|
||
if (fileName === '.') { | ||
return $gettext('File name cannot be equal to "."') | ||
} | ||
|
||
if (fileName === '..') { | ||
return $gettext('File name cannot be equal to ".."') | ||
} | ||
|
||
if (/\s+$/.test(fileName)) { | ||
return $gettext('File name cannot end with whitespace') | ||
} | ||
|
||
const exists = unref(files).find((file) => file.name === fileName) | ||
|
||
if (exists) { | ||
return $gettext('%{name} already exists', { name: fileName }, true) | ||
} | ||
|
||
return null | ||
} | ||
|
||
const addAppProviderFileFunc = async (fileName) => { | ||
// FIXME: this belongs in web-app-external, but the app provider handles file creation differently than other editor extensions. Needs more refactoring. | ||
if (fileName === '') { | ||
return | ||
} | ||
try { | ||
const baseUrl = urlJoin( | ||
configurationManager.serverUrl, | ||
unref(capabilities).files.app_providers[0].new_url | ||
) | ||
const query = stringify({ | ||
parent_container_id: unref(currentFolder).fileId, | ||
filename: fileName | ||
}) | ||
const url = `${baseUrl}?${query}` | ||
const response = await makeRequest('POST', url) | ||
if (response.status !== 200) { | ||
throw new Error(`An error has occurred: ${response.status}`) | ||
} | ||
const path = join(unref(currentFolder).path, fileName) || '' | ||
const resource = await (clientService.webdav as WebDAV).getFileInfo(space, { | ||
path | ||
}) | ||
if (unref(loadIndicatorsForNewFile)) { | ||
resource.indicators = getIndicators({ resource, ancestorMetaData: unref(ancestorMetaData) }) | ||
} | ||
triggerDefaultAction({ space: space, resources: [resource] }) | ||
store.commit('Files/UPSERT_RESOURCE', resource) | ||
store.dispatch('hideModal') | ||
store.dispatch('showMessage', { | ||
title: $gettext('"%{fileName}" was created successfully', { fileName }) | ||
}) | ||
} catch (error) { | ||
console.error(error) | ||
store.dispatch('showMessage', { | ||
title: $gettext('Failed to create file'), | ||
status: 'danger' | ||
}) | ||
} | ||
} | ||
|
||
const loadIndicatorsForNewFile = computed(() => { | ||
return isLocationSpacesActive(router, 'files-spaces-generic') && space.driveType !== 'share' | ||
}) | ||
|
||
const addNewFile = async (fileName, openAction) => { | ||
if (fileName === '') { | ||
return | ||
} | ||
|
||
try { | ||
const path = join(unref(currentFolder).path, fileName) | ||
const resource = await (clientService.webdav as WebDAV).putFileContents(space, { | ||
path | ||
}) | ||
|
||
if (loadIndicatorsForNewFile.value) { | ||
resource.indicators = getIndicators({ resource, ancestorMetaData: unref(ancestorMetaData) }) | ||
} | ||
|
||
store.commit('Files/UPSERT_RESOURCE', resource) | ||
|
||
if (openAction) { | ||
openEditor( | ||
openAction, | ||
space.getDriveAliasAndItem(resource), | ||
resource.webDavPath, | ||
resource.fileId, | ||
EDITOR_MODE_CREATE | ||
) | ||
store.dispatch('hideModal') | ||
|
||
return | ||
} | ||
|
||
store.dispatch('hideModal') | ||
store.dispatch('showMessage', { | ||
title: $gettext('"%{fileName}" was created successfully', { fileName }) | ||
}) | ||
} catch (error) { | ||
console.error(error) | ||
store.dispatch('showMessage', { | ||
title: $gettext('Failed to create file'), | ||
status: 'danger' | ||
}) | ||
} | ||
} | ||
|
||
const handler = ( | ||
fileActionOptions: FileActionOptions, | ||
extension: string, | ||
openAction: any // FIXME: type? | ||
) => { | ||
const checkInputValue = (value) => { | ||
store.dispatch( | ||
'setModalInputErrorMessage', | ||
checkNewFileName(areFileExtensionsShown.value ? value : `${value}.${extension}`) | ||
) | ||
} | ||
let defaultName = $gettext('New file') + `.${extension}` | ||
|
||
if (unref(files).some((f) => f.name === defaultName)) { | ||
defaultName = resolveFileNameDuplicate(defaultName, extension, unref(files)) | ||
} | ||
|
||
if (!areFileExtensionsShown.value) { | ||
defaultName = extractNameWithoutExtension({ name: defaultName, extension } as any) | ||
} | ||
|
||
const inputSelectionRange = !areFileExtensionsShown.value | ||
? null | ||
: [0, defaultName.length - (extension.length + 1)] | ||
|
||
const modal = { | ||
variation: 'passive', | ||
title: $gettext('Create a new file'), | ||
cancelText: $gettext('Cancel'), | ||
confirmText: $gettext('Create'), | ||
hasInput: true, | ||
inputValue: defaultName, | ||
inputLabel: $gettext('File name'), | ||
inputError: checkNewFileName( | ||
areFileExtensionsShown.value ? defaultName : `${defaultName}.${extension}` | ||
), | ||
inputSelectionRange, | ||
onCancel: () => store.dispatch('hideModal'), | ||
onConfirm: !openAction | ||
? addAppProviderFileFunc | ||
: (fileName) => { | ||
if (!areFileExtensionsShown.value) { | ||
fileName = `${fileName}.${extension}` | ||
} | ||
addNewFile(fileName, openAction) | ||
}, | ||
onInput: checkInputValue | ||
} | ||
|
||
store.dispatch('createModal', modal) | ||
} | ||
|
||
const actions = computed((): FileAction[] => { | ||
const actions = [] | ||
for (const newFileHandler of unref(newFileHandlers) || []) { | ||
const openAction = newFileHandler.action | ||
actions.push({ | ||
name: 'create-new-file', | ||
icon: 'add', | ||
handler: (args) => handler(args, newFileHandler.ext, openAction), | ||
label: () => newFileHandler.menuTitle($gettext), | ||
isEnabled: ({ resources }) => { | ||
return true | ||
}, | ||
canBeDefault: true, | ||
componentType: 'button', | ||
class: 'oc-files-actions-create-new-file', | ||
ext: newFileHandler.ext | ||
}) | ||
} | ||
for (const mimeType of unref(mimetypesAllowedForCreation) || []) { | ||
const openAction = false | ||
actions.push({ | ||
name: 'create-new-file', | ||
icon: 'add', | ||
handler: (args) => handler(args, mimeType.ext, openAction), | ||
label: () => mimeType.name, | ||
isEnabled: ({ resources }) => { | ||
return true | ||
}, | ||
canBeDefault: true, | ||
componentType: 'button', | ||
class: 'oc-files-actions-create-new-file', | ||
ext: mimeType.ext | ||
}) | ||
} | ||
|
||
return actions | ||
}) | ||
|
||
return { | ||
actions, | ||
checkNewFileName, | ||
addNewFile | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idea for the future: In
CreateAndUpload
we currently load the icon viagetIconResource
. Would be nice to somehow handle this here. Maybe we can even removeext: newFileHandler.ext
then (which is only used for the icon).