Async Requests in Linter Example


#1

So my question is related to this one about Async Decoration.

It basically boils down to how to perform an async operation in a plugin. Right now I’m after just getting a simple fetch to work, but eventually I want to implement Web Sockets.

I’m using tiptap and have followed the linting example. Here is my boiled down version.

const properNouns = /\b(jim|oreo|jupiter)\b/gi;

const lint = doc => {
  let result = [];

  doc.descendants((node, pos) => {
    if (node.isText) {
      let m;
      while ((m = properNouns.exec(node.text)))
        result.push({
          from: pos + m.index,
          to: pos + m.index + m[0].length,
          type: "proper-noun"
        });
    }
  });
  return result;
};

const lintDeco = doc => {
  let decos = [];

  lint(doc).forEach(({ from, to, type }) => {
    decos.push(Decoration.inline(from, to, { class: type }));
  });
  return DecorationSet.create(doc, decos);
};

export default class ProperNounPlugin extends Extension {
  get name() {
    return "proper_noun_plugin";
  }

  get plugins() {
    return [
      new Plugin({
        state: {
          init(_, { doc }) {
            return lintDeco(doc);
          },
          apply(transaction, value) {
            return transaction.docChanged ? lintDeco(transaction.doc) : value;
          }
        },
        props: {
          decorations(state) {
            return this.getState(state);
          }
        }
      })
    ];
  }
}

What I’ve gathered from other questions, the docs and examples is that I need the view to dispatch an action. In the other question linked, I think you need to do this in the plugin’s view.update:

Plugin({
  state: { ... },
  view: () => ({
    update: debounce((view, prevState) => {
      // emulate API request
      setTimeout(() => {
        const response = true; // emulate response from API
        view.dispatch(view.state.tr.setMeta("response", response));
      }, 200);
    }, 500)
  })
});

From there, the state's apply function will no longer just check if the doc has changed but if there’s metadata on the transaction:

Plugin({
  state: {
    apply(tr, value) {
      if (tr.docChanged && tr.getMeta("response")) return lintDeco(tr.doc);
      return value;
    }
  }
});

However, I still can’t get this to work for me. tr.docChanged will return true when tr.getMeta("response") is returning undefined and vice versa – so lintDeco is never getting called. What am I missing?

Also, what are the conditions I need to check in the view.update method to know when the doc has been changed. Right now, debounced requests will fire constantly even though no changes have occurred to the document.

Any help is appreciated!