import { Link as LinkTipTap } from '@tiptap/extension-link'
import { Plugin, PluginKey } from 'prosemirror-state'
import { find, registerCustomProtocol, init, reset } from 'linkifyjs'
import { NODE_TYPE, LINK_PROTOCOLS_DEFAULT, NODE_ATTR } from '../constants.js'
function gifPreviewHandler (options) {
return new Plugin({
key: new PluginKey('handleGifPreview'),
props: {
handlePaste: (view, event, slice) => {
let textContent = ''
slice.content.forEach((node) => {
textContent += node.textContent
})
const links = find(textContent).filter((link) => link.href.includes('.gif'))
for (const link of links) {
window.fetch(link.href)
.then((response) => {
if (!response.ok) {
throw new Error('Error fetching url')
}
const contentType = response.headers.get('content-type')
if (contentType !== 'image/gif') {
throw new Error('Not a gif')
}
return response.blob()
})
.then(async (blob) => {
try {
const { state, dispatch } = view
const { schema, doc } = state
const name = options.createRandomName(link.href)
const file = new window.File([blob], name, { type: 'image/gif' })
const { id, file: { url } } = await options.editor.commands.upload([file])
const node = schema.nodes[NODE_TYPE.FILE_IMAGE].create({
[NODE_ATTR.ID]: id,
src: url,
name,
alt: name,
title: name
})
const transaction = view.state.tr.insert(doc.content.size, node)
dispatch(transaction)
} catch (e) {
console.log('ERROR', e)
}
})
.catch((e) => console.log('ERROR', e))
}
return false
}
}
})
}
export const Link = LinkTipTap.extend({
inclusive: false,
onCreate () {
reset()
// reset before creation prevents following console warning
// linkifyjs: already initialized - will not register
// custom scheme "pear" until manual call of linkify.init().
// Register all schemes and plugins before invoking linkify the first time.
// LinkTipTap.onCreate
if (this.options.validate && !this.options.shouldAutoLink) {
// Copy the validate function to the shouldAutoLink option
this.options.shouldAutoLink = this.options.validate
console.warn(
'The `validate` option is deprecated. Rename to the `shouldAutoLink` option instead.'
)
}
this.options.protocols.forEach(protocol => {
if (typeof protocol === 'string') {
registerCustomProtocol(protocol)
return
}
registerCustomProtocol(protocol.scheme, protocol.optionalSlashes)
})
init() // linkify js expect init to be called after registerCustomProtocol
},
addAttributes () {
return {
href: {
default: null,
parseHTML (element) {
return element.getAttribute('href')
}
},
pasted: { default: false },
target: {
default: this.options.HTMLAttributes.target
},
rel: {
default: this.options.HTMLAttributes.rel
}
}
},
onDestroy () {
// We need this listener to overwrite the Link internal onDestroy to
// disable reseting linkify, going to be reset on pre create
},
addOptions () {
return {
...this.parent?.(),
protocols: LINK_PROTOCOLS_DEFAULT
}
},
addProseMirrorPlugins () {
const plugins = this.parent()
return [
gifPreviewHandler({
editor: this.editor,
type: this.type,
...this.options
}),
...plugins
]
}
})