import { Extension as t } from "@tiptap/core"
import { NODE_ATTR as e } from "../constants.js"
import { Plugin as i } from "prosemirror-state"
import {
getNodeTextLength as s,
getDocTextLength as r,
isAllowedLink as n,
} from "./helpers/index.js"
import { find as o } from "linkifyjs"
function a(t) {
let e = !1
return (
t.descendants((t) => {
if (e) return !1
if (t.marks?.length)
for (const i of t.marks) if ("link" === i.type.name) return ((e = !0), !1)
t.isText && t.text && (e = o(t.text).some((t) => n(t.href, {})))
}),
e
)
}
export const Submit = t.create({
name: "submit",
addOptions: () => ({
onSubmit: () => {},
maxLength: () => 1e5,
isSubmitDisabledInitial: () => !1,
allowSubmitEmpty: () => !1,
isFileSharingDisabled: () => !1,
isLinkSharingDisabled: () => !1,
}),
addStorage: () => ({
charNumber: 0,
filesNumber: 0,
hasLinks: !1,
isSubmitDisabled: !0,
isValid: !0,
}),
addCommands() {
return {
getFiles:
() =>
({ editor: t }) => {
const { content: i } = t.getJSON()
return (i || []).reduce((t, i) => (i.attrs?.[e.ID] && t.push(i.attrs[e.ID]), t), [])
},
getLinkPreviews:
() =>
({ editor: t }) => {
const { content: e } = t.getJSON()
return (e || []).reduce(
(t, e) => (
"link-preview" === e.type &&
e.attrs?.url &&
(t[e.attrs.url] = {
url: e.attrs.url,
title: e.attrs.title,
image: e.attrs.image,
description: e.attrs.description,
}),
t
),
{},
)
},
submit:
() =>
({ editor: t, commands: e }) => {
const { onSubmit: i } = this.options
if (this.storage.isSubmitDisabled && !this.options.allowSubmitEmpty()) return !1
const s = t.getJSON(),
r = e.getLinkPreviews(),
n = e.getFiles()
return (i(s, n, r), t.emit?.("submit", { doc: s, files: n, linkPreviews: r }), !0)
},
}
},
addKeyboardShortcuts: () => ({
Enter: ({ editor: t }) => {
const { emoji: e, mention: i } = t.storage
return !e?.suggestionOpen && !i?.suggestionOpen && (t.commands.submit(), !0)
},
}),
addProseMirrorPlugins() {
const t = this.options.maxLength()
return [
new i({
state: { init: (t, e) => e.doc, apply: (t, e, i, s) => (t.docChanged ? s.doc : e) },
view: (i) => ({
update: () => {
const s = i.state.doc,
n = r(s),
o = (s.content.content || []).filter((t) => t.attrs?.[e.ID]).length,
u = a(s),
l =
n <= t &&
!(this.options?.isFileSharingDisabled?.() && o > 0) &&
!(this.options?.isLinkSharingDisabled?.() && u),
m =
(0 === n && !this.options.allowSubmitEmpty()) ||
!l ||
this.options.isSubmitDisabledInitial()
;(this.storage.charNumber !== n ||
this.storage.isValid !== l ||
this.storage.isSubmitDisabled !== m ||
this.storage.hasLinks !== u) &&
((this.storage.charNumber = n),
(this.storage.isValid = l),
(this.storage.isSubmitDisabled = m),
(this.storage.filesNumber = o),
(this.storage.hasLinks = u),
i.dispatch(i.state.tr.setMeta("submit:update", Date.now())))
},
}),
}),
new i({
appendTransaction: (e, i, n) => {
if (e.some((t) => t.getMeta("truncate"))) return null
if (r(n.doc) <= t) return null
let o = 0,
a = null
;(n.doc.descendants((e, i) => {
if (null !== a) return !1
let r = 0
if (((r = s(e)), o + r > t)) {
const s = o + r - t
return ((a = e.isText ? i + (e.text.length - s) : i), !1)
}
return ((o += r), !0)
}),
null === a && (a = n.doc.content.size))
const u = n.tr
return (u.delete(a, n.doc.content.size), u.setMeta("truncate", !0), u)
},
filterTransaction: (e, i) =>
this.storage.charNumber < t || r(e.doc) <= this.storage.charNumber,
}),
]
},
})