Rendering UI kit from a Custom UI resolver function

This is a bit of a long-shot, but I’ll ask anyway…

We are in the process of porting an existing Connect app to Forge + Custom UI.

The Connect app uses AUI to display Atlassian-style errors / warnings / info using the messages component.

Having AUI as a dependency in Connect apps has been problematic for us, as AUI was primarily designed for use in server environments (P2 plugins).

AtlasKit was supposed to be the answer for Connect UIs, but AtlasKit assumes that everyone is building their apps in React (which ours isn’t).

As part of the move to Forge, we’d like to remove our dependency on AUI, and instead use UI kit’s <SectionMessage> component to render errors / warnings / info.

Our Custom UI uses @forge/bridge to invoke a function, e.g.

import { invoke } from "@forge/bridge";

async function getConfig() {
  const config = await invoke("getConfig");
}

The corresponding backend resolver includes a license check:

import Resolver from "@forge/resolver";

const resolver = new Resolver();

resolver.define("getConfig", ({ context }) => {

  if (context.license && context.license.isActive) {
    // license is OK, return the config
    return context.extension.config; 
  } else {
    // handle unlicensed case here
  }

});

export const handler = resolver.getDefinitions();

For the case where the license check fails, we’d like to render a <SectionMessage> component; but it’s not clear how we could do this from inside a Custom UI resolver function?

Something we tried (unsuccessfully) was using @forge/ui’s render() function to get at the generated HTML:

(similar to using ReactDOMServer.renderToString(someElement) for SSR in React)

import ForgeUI, { SectionMessage, Text, render } from "@forge/ui";
import Resolver from "@forge/resolver";

const resolver = new Resolver();

// define our <Unlicensed/> component
const Unlicensed = () => {
  return (
    <SectionMessage title="Unlicensed" appearance="error">
      <Text>Sorry but it appears that your license is not active</Text>
    </SectionMessage>
  );
};

resolver.define("getConfig", async ({ context }) => {

  if (context.license && context.license.isActive) {
    // license is OK, return the config
    return context.extension.config; 
  } else {
    // handle unlicensed case here
    return render(<Unlicensed/>)();  // immediately invoke the function that render() returns
  }

});

export const handler = resolver.getDefinitions();

Unsurprisingly, that doesn’t work.

Is there an analog of renderToString() in UI kit?
Or is there another solution to this problem that we haven’t thought of?

(e.g. could we somehow ‘swap out’ the entire Custom UI module for a UI kit module when we detect that a license is not valid?)

I won’t be surprised if the answer is No, but it’s worth asking the question.

2 Likes

Hi @scottohara ,

At this stage UI kit rendering involves the generation of JSON in the function which is transferred to the client for rendering. I can see that this could be adapted to support server side rendering, but I can’t see that happening in the foreseeable future.

Regards,
Dugald