Webview: Add async method handling

This commit is contained in:
Johannes Kirschbauer
2024-07-11 16:34:27 +02:00
parent 255f33f40b
commit 8e88676d51

View File

@@ -1,5 +1,6 @@
import { FromSchema } from "json-schema-to-ts";
import { schema } from "@/api";
import { nanoid } from "nanoid";
export type API = FromSchema<typeof schema>;
@@ -41,47 +42,44 @@ const operations = schema.properties;
const operationNames = Object.keys(operations) as OperationNames[];
type ObserverRegistry = {
[K in OperationNames]: ((response: OperationResponse<K>) => void)[];
[K in OperationNames]: Record<
string,
(response: OperationResponse<K>) => void
>;
};
const obs: ObserverRegistry = operationNames.reduce(
const registry: ObserverRegistry = operationNames.reduce(
(acc, opName) => ({
...acc,
[opName]: [],
[opName]: {},
}),
{} as ObserverRegistry
);
interface ReceiveOptions {
/**
* Calls only the registered function that has the same key as used with dispatch
*
*/
fnKey: string;
}
function createFunctions<K extends OperationNames>(
operationName: K
): {
dispatch: (args: OperationArgs<K>) => void;
receive: (fn: (response: OperationResponse<K>) => void) => void;
receive: (fn: (response: OperationResponse<K>) => void, id: string) => void;
} {
return {
dispatch: (args: OperationArgs<K>) => {
// console.log(
// `Operation: ${String(operationName)}, Arguments: ${JSON.stringify(args)}`
// );
// Send the data to the gtk app
window.webkit.messageHandlers.gtk.postMessage({
method: operationName,
data: args,
});
},
receive: (
fn: (response: OperationResponse<K>) => void
// options?: ReceiveOptions
) => {
obs[operationName].push(fn);
receive: (fn: (response: OperationResponse<K>) => void, id: string) => {
// @ts-expect-error: This should work although typescript doesn't let us write
registry[operationName][id] = fn;
window.clan[operationName] = (s: string) => {
obs[operationName].forEach((f) => deserialize(f)(s));
const f = (response: OperationResponse<K>) => {
if (response.op_key === id) {
registry[operationName][id](response);
}
};
deserialize(f)(s);
};
},
};
@@ -90,7 +88,7 @@ function createFunctions<K extends OperationNames>(
type PyApi = {
[K in OperationNames]: {
dispatch: (args: OperationArgs<K>) => void;
receive: (fn: (response: OperationResponse<K>) => void) => void;
receive: (fn: (response: OperationResponse<K>) => void, id: string) => void;
};
};
@@ -110,6 +108,23 @@ function download(filename: string, text: string) {
document.body.removeChild(element);
}
export const callApi = <K extends OperationNames>(
method: K,
args: OperationArgs<K>
) => {
return new Promise<OperationResponse<K>>((resolve, reject) => {
const id = nanoid();
pyApi[method].receive((response) => {
if (response.status === "error") {
reject(response);
}
resolve(response);
}, id);
pyApi[method].dispatch({ ...args, op_key: id });
});
};
const deserialize =
<T>(fn: (response: T) => void) =>
(str: string) => {