Plugin Feed Endpoint

Exposing a datasource from a plugin

Any QuickBrick plugin can expose a method to return a feed, which can be used as data source in any UI component in Zapp. This is especially useful for action plugins, which can store data and context. With this feature, these plugins can easily provide a feed for other components to display the data held in the context.

In order to achieve this, you simply need to add a function to the main export of your plugin:

// plugin/index.ts
import { MyPlugin } from "./src/index.ts";
MyPlugin.getDataSource = async function (url, params) {
try {
// if you use this in an action plugin, it is probably
// a good idea to check that your context is properly
// initialized before returning the feed. for instance:
if (!ContextManager.state) {
await ContextManager.initialize();
}
// build the feed to return in any way needed. You
// can leverage the url & params provided to tweak
// the entries you are returning, and the metadata
// of the feed
const feed = ContextManager.buildFeed(url, params);
// and return an object with a feed property containing
// your feed
return { feed };
} catch (e) {
// handle errors !
// otherwise components relying on your plugin's feed
// may be left in a hanging state
return { error, feed: null };
}
};
export default MyPlugin;

If your plugin identifier is my-plugin, you can know create an endpoint in Zapp with the url pipesv2://my-plugin. Like any endpoint, you can define context keys to be added with the request. The value in these context keys will be either in the query or header property of the second params argument, depending on how you set them up in your endpoint.

You can then create a feed based on this endpoint, with all the capabilities available for Zapp Pipes feeds; you can for instance add parameters based on entry or screen context. The feed url will be passed as the first url argument, so you will be able to parse this url and extract path or query parameters to modify the feed returns (i.e. filter by state, categories, etc...)

Allow UI components to register listeners for content change

Plugins exposing a getDataSource function can also expose a function which allows UI components to register a listener which the plugin should invoke when content changes, and the datasource can be refreshed.

The UI components will basically run code similar to this:

function UIComponent(props) {
// pulling the datasource from component data
const dataSource = props?.component?.data?.source;
// using the feedLoader, we get the feedData, and a reloadData function
// which can be invoked to refresh the datasource
const { data, reloadData } = useFeedLoader({ feedUrl: dataSource });
useEffect(() => {
// on mount, we register this reloadData as a listener, so it is
// invoked when content changes
const removeListener = Plugin.addDataSourceListener(reloadData);
// this will remove the listener when the component unmounts
return removeListener;
}, []);
return <View>...
}

For this to work, add a addDataSourceListener function to your plugin's export. This function should save the listener in a place where in can be invoked when content changes in your plugin. It should then return a function which will remove the reference to the listener - this will be invoked when the UI Component using your plugin as datasource unmounts.

let listeners = [];
MyPlugin.addDataSourceListener = function (listener) {
listeners.push(listener);
return () => {
listeners = listeners.filter((l) => l === listener);
};
};
// assuming that when content changes, your plugin invokes ContextManager.onContentChange();
// you would need there to invoke the listeners to notify that content changed
// and the datasource should be refreshed
ContextManager.onContentChange = function () {
listeners.forEach((listener) => listener());
};