Skip to content

Commit

Permalink
fixup! fix(files): add "Link to file or folder" and "Upload" buttons
Browse files Browse the repository at this point in the history
Signed-off-by: julia.kirschenheuter <julia.kirschenheuter@nextcloud.com>
  • Loading branch information
JuliaKirschenheuter committed Jan 30, 2025
1 parent dcdd7c7 commit bc78ba4
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 114 deletions.
60 changes: 5 additions & 55 deletions src/components/Menu/ActionInsertLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@

<script>
import { NcActions, NcActionButton, NcActionInput } from '@nextcloud/vue'
import { getLinkWithPicker } from '@nextcloud/vue/dist/Components/NcRichText.js'
import { FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'

Expand All @@ -76,6 +74,7 @@ import { Document, Loading, LinkOff, Web, Shape } from '../icons.js'
import { BaseActionEntry } from './BaseActionEntry.js'
import { useFileMixin } from '../Editor.provider.js'
import { useMenuIDMixin } from './MenuBar.provider.js'
import { buildFilePicker } from '../../helpers/filePicker.js'

export default {
name: 'ActionInsertLink',
Expand Down Expand Up @@ -122,12 +121,7 @@ export default {
this.startPath = this.relativePath.split('/').slice(0, -1).join('/')
}

const filePicker = getFilePickerBuilder(t('text', 'Select file or folder to link to'))
.startAt(this.startPath)
.allowDirectories(true)
.setMultiSelect(false)
.setType(FilePickerType.Choose)
.build()
const filePicker = buildFilePicker(this.startPath)

Check warning on line 124 in src/components/Menu/ActionInsertLink.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Menu/ActionInsertLink.vue#L124

Added line #L124 was not covered by tests

filePicker.pick()
.then((file) => {
Expand Down Expand Up @@ -173,42 +167,9 @@ export default {
* @param {string} text Text part of the link
*/
setLink(url, text) {
// Heuristics for determining if we need a https:// prefix.
const noPrefixes = [
/^[a-zA-Z]+:/, // url with protocol ("mailTo:email@domain.tld")
/^\//, // absolute path
/\?fileId=/, // relative link with fileId
/^\.\.?\//, // relative link starting with ./ or ../
/^[^.]*[/$]/, // no dots before first '/' - not a domain name
/^#/, // url fragment
]
if (url && !noPrefixes.find(regex => url.match(regex))) {
url = 'https://' + url
}

// Avoid issues when parsing urls later on in markdown that might be entered in an invalid format (e.g. "mailto: example@example.com")
const href = url.replaceAll(' ', '%20')
const chain = this.$editor.chain()
// Check if any text is selected, if not insert the link using the given text property
if (this.$editor.view.state?.selection.empty) {
chain.insertContent({
type: 'paragraph',
content: [{
type: 'text',
marks: [{
type: 'link',
attrs: {
href,
},
}],
text,
}],
})
} else {
chain.setLink({ href })
}
chain.focus().run()
this.$editor.chain().setOrInsertLink(url, text).focus().run()

Check warning on line 170 in src/components/Menu/ActionInsertLink.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Menu/ActionInsertLink.vue#L170

Added line #L170 was not covered by tests
},

/**
* Remove link markup at current position
* Triggered by the "remove link" button
Expand All @@ -218,18 +179,7 @@ export default {
this.menuOpen = false
},
linkPicker() {
getLinkWithPicker(null, true)
.then(link => {
const chain = this.$editor.chain()
if (this.$editor.view.state?.selection.empty) {
chain.focus().insertPreview(link).run()
} else {
chain.setLink({ href: link }).focus().run()
}
})
.catch(error => {
console.error('Smart picker promise rejected', error)
})
this.$editor.chain().getLinkPicker().focus().run()

Check warning on line 182 in src/components/Menu/ActionInsertLink.vue

View check run for this annotation

Codecov / codecov/patch

src/components/Menu/ActionInsertLink.vue#L182

Added line #L182 was not covered by tests
},
},
}
Expand Down
64 changes: 5 additions & 59 deletions src/components/SuggestionsBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
import { NcButton } from '@nextcloud/vue'
import { Document, Table, Shape } from './icons.js'
import { useActionChooseLocalAttachmentMixin } from './Editor/MediaHandler.provider.js'

Check warning on line 45 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L43-L45

Added lines #L43 - L45 were not covered by tests
import { getLinkWithPicker } from '@nextcloud/vue/dist/Components/NcRichText.js'
import { useEditorMixin, useFileMixin } from './Editor.provider.js'
import { FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import { buildFilePicker } from '../helpers/filePicker.js'

Check warning on line 49 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L47-L49

Added lines #L47 - L49 were not covered by tests
export default {
name: 'SuggestionsBar',
Expand Down Expand Up @@ -75,23 +75,8 @@ export default {
},

Check warning on line 75 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L71-L75

Added lines #L71 - L75 were not covered by tests
methods: {
/**
* Open smart picker dialog
* Triggered by the "Smart Picker" button
*/
linkPicker() {
getLinkWithPicker(null, true)
.then(link => {
const chain = this.$editor.chain()
if (this.$editor.view.state?.selection.empty) {
chain.focus().insertPreview(link).run()
} else {
chain.setLink({ href: link }).focus().run()
}
})
.catch(error => {
console.error('Smart picker promise rejected', error)
})
this.$editor.chain().getLinkPicker().focus().run()
},

Check warning on line 80 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L77-L80

Added lines #L77 - L80 were not covered by tests
/**
Expand All @@ -111,12 +96,7 @@ export default {
this.startPath = this.relativePath.split('/').slice(0, -1).join('/')
}

Check warning on line 97 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L90-L97

Added lines #L90 - L97 were not covered by tests
const filePicker = getFilePickerBuilder(t('text', 'Select file or folder to link to'))
.startAt(this.startPath)
.allowDirectories(true)
.setMultiSelect(false)
.setType(FilePickerType.Choose)
.build()
const filePicker = buildFilePicker(this.startPath)

Check warning on line 99 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L99

Added line #L99 was not covered by tests
filePicker.pick()
.then((file) => {
Expand All @@ -141,41 +121,7 @@ export default {
* @param {string} text Text part of the link
*/
setLink(url, text) {
// Heuristics for determining if we need a https:// prefix.
const noPrefixes = [
/^[a-zA-Z]+:/, // url with protocol ("mailTo:email@domain.tld")
/^\//, // absolute path
/\?fileId=/, // relative link with fileId
/^\.\.?\//, // relative link starting with ./ or ../
/^[^.]*[/$]/, // no dots before first '/' - not a domain name
/^#/, // url fragment
]
if (url && !noPrefixes.find(regex => url.match(regex))) {
url = 'https://' + url
}
// Avoid issues when parsing urls later on in markdown that might be entered in an invalid format (e.g. "mailto: example@example.com")
const href = url.replaceAll(' ', '%20')
const chain = this.$editor.chain()
// Check if any text is selected, if not insert the link using the given text property
if (this.$editor.view.state?.selection.empty) {
chain.insertContent({
type: 'paragraph',
content: [{
type: 'text',
marks: [{
type: 'link',
attrs: {
href,
},
}],
text,
}],
})
} else {
chain.setLink({ href })
}
chain.focus().run()
this.$editor.chain().setOrInsertLink(url, text).focus().run()
},
},

Check warning on line 126 in src/components/SuggestionsBar.vue

View check run for this annotation

Codecov / codecov/patch

src/components/SuggestionsBar.vue#L116-L126

Added lines #L116 - L126 were not covered by tests
Expand Down
15 changes: 15 additions & 0 deletions src/helpers/filePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs'

export const buildFilePicker = (startPath) => {
return getFilePickerBuilder(t('text', 'Select file or folder to link to'))
.startAt(startPath)
.allowDirectories(true)
.setMultiSelect(false)
.setType(FilePickerType.Choose)
.build()
}

Check warning on line 15 in src/helpers/filePicker.js

View check run for this annotation

Codecov / codecov/patch

src/helpers/filePicker.js#L9-L15

Added lines #L9 - L15 were not covered by tests
63 changes: 63 additions & 0 deletions src/marks/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { markInputRule } from '@tiptap/core'
import TipTapLink from '@tiptap/extension-link'
import { domHref, parseHref } from './../helpers/links.js'
import { linkClicking } from '../plugins/links.js'
import { getLinkWithPicker } from '@nextcloud/vue/dist/Components/NcRichText.js'

const PROTOCOLS_TO_LINK_TO = ['http:', 'https:', 'mailto:', 'tel:']

Expand Down Expand Up @@ -88,6 +89,68 @@ const Link = TipTapLink.extend({
]
},

addCommands() {
return {
/**
* Check if any text is selected, if not insert the link using the given text property
*
* @param {string} url href attribute of the link
* @param {string} text Text part of the link
*/
setOrInsertLink: (url, text) => ({ state, chain }) => {
// Heuristics for determining if we need a https:// prefix.
const noPrefixes = [
/^[a-zA-Z]+:/, // url with protocol ("mailTo:email@domain.tld")
/^\//, // absolute path
/\?fileId=/, // relative link with fileId
/^\.\.?\//, // relative link starting with ./ or ../
/^[^.]*[/$]/, // no dots before first '/' - not a domain name
/^#/, // url fragment
]
if (url && !noPrefixes.find(regex => url.match(regex))) {
url = 'https://' + url
}
// Avoid issues when parsing urls later on in markdown that might be entered in an invalid format (e.g. "mailto: example@example.com")
const href = url.replaceAll(' ', '%20')
if (state.selection.empty) {
return chain().insertContent({
type: 'paragraph',
content: [{
type: 'text',
marks: [{
type: 'link',
attrs: {
href,
},
}],
text,
}],
}).run()
} else {
return chain().setLink({ href }).run()
}

Check warning on line 131 in src/marks/Link.js

View check run for this annotation

Codecov / codecov/patch

src/marks/Link.js#L101-L131

Added lines #L101 - L131 were not covered by tests
},

/**
* Open smart picker dialog
* Triggered by the "Smart Picker" button
*/
getLinkPicker: () => ({ state, chain }) => {
getLinkWithPicker(null, true)
.then(link => {
if (state.selection.empty) {
return chain().focus().insertPreview(link).run()
} else {
return chain().setLink({ href: link }).focus().run()
}
})
.catch(error => {
console.error('Smart picker promise rejected', error)
})

Check warning on line 149 in src/marks/Link.js

View check run for this annotation

Codecov / codecov/patch

src/marks/Link.js#L139-L149

Added lines #L139 - L149 were not covered by tests
},
}
},

addProseMirrorPlugins() {
const plugins = this.parent()
// remove upstream link click handle plugin
Expand Down

0 comments on commit bc78ba4

Please sign in to comment.