OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-editor / src / extensions / submit.js

141 lines · javascript ref:
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,
      }),
    ]
  },
})