Data Request API helper #10

Merged
Oliver merged 94 commits from feat/data-requests into main 2025-11-22 02:51:15 +00:00
4 changed files with 187 additions and 177 deletions
Showing only changes of commit df0c69c731 - Show all commits

View file

@ -1,6 +1,6 @@
import { __ID__, filePath } from "../consts.mjs"; import { __ID__, filePath } from "../consts.mjs";
import { get as getQuery, requery } from "../utils/QueryManager.mjs";
import { Logger } from "../utils/Logger.mjs"; import { Logger } from "../utils/Logger.mjs";
import { QueryManager } from "../utils/QueryManager.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -62,7 +62,7 @@ export class QueryStatus extends HandlebarsApplicationMixin(ApplicationV2) {
}; };
async _prepareUsers(ctx) { async _prepareUsers(ctx) {
const query = QueryManager.get(this.requestID); const query = getQuery(this.requestID);
if (!query) { return }; if (!query) { return };
const users = []; const users = [];
@ -85,7 +85,7 @@ export class QueryStatus extends HandlebarsApplicationMixin(ApplicationV2) {
static async promptUser($e, element) { static async promptUser($e, element) {
const userID = element.closest(`[data-user-id]`)?.dataset.userId; const userID = element.closest(`[data-user-id]`)?.dataset.userId;
if (!userID) { return }; if (!userID) { return };
QueryManager.requery(this.requestID, [ userID ]); requery(this.requestID, [ userID ]);
}; };
/** @this {QueryStatus} */ /** @this {QueryStatus} */

View file

@ -1,6 +1,6 @@
import { QueryManager } from "../utils/QueryManager.mjs"; import { userActivity } from "../utils/QueryManager.mjs";
Hooks.on(`userConnected`, (user, connected) => { Hooks.on(`userConnected`, (user, connected) => {
if (user.isSelf) { return }; if (user.isSelf) { return };
QueryManager.userActivity(user.id, connected); userActivity(user.id, connected);
}); });

View file

@ -1,4 +1,4 @@
import { QueryManager } from "../utils/QueryManager.mjs"; import { addResponse, has as hasQuery } from "../utils/QueryManager.mjs";
export function submitRequest(payload, user) { export function submitRequest(payload, user) {
const { const {
@ -17,6 +17,6 @@ export function submitRequest(payload, user) {
return; return;
}; };
if (!QueryManager.has(id)) { return }; if (!hasQuery(id)) { return };
QueryManager.addResponse(id, user.id, answers); addResponse(id, user.id, answers);
}; };

View file

@ -20,28 +20,29 @@ import { filePath } from "../consts.mjs";
import { Logger } from "./Logger.mjs"; import { Logger } from "./Logger.mjs";
import { QueryStatus } from "../apps/QueryStatus.mjs"; import { QueryStatus } from "../apps/QueryStatus.mjs";
/** @type {Map<string, QueryData>} */
const queries = new Map();
/** @type {Map<string, Promise>} */
const promises = new Map();
async function sendBasicNotification(userID, answers) { async function sendBasicNotification(userID, answers) {
const content = await foundry.applications.handlebars.renderTemplate( const content = await foundry.applications.handlebars.renderTemplate(
filePath(`templates/query-response.hbs`), filePath(`templates/query-response.hbs`),
{ answers }, { answers },
); );
QueryManager.notify(userID, content, { includeGM: false }); await notify(userID, content, { includeGM: false });
}; };
export class QueryManager { export function has(requestID) {
/** @type {Map<string, QueryData>} */ return queries.has(requestID);
static #queries = new Map();
static #promises = new Map();
static has(requestID) {
return this.#queries.has(requestID);
}; };
/** @returns {Omit<QueryData, "resolve"|"onSubmit"|"app">} */ /** @returns {Omit<QueryData, "resolve"|"onSubmit"|"app">} */
static get(requestID) { export function get(requestID) {
if (!this.#queries.has(requestID)) { return null }; if (!queries.has(requestID)) { return null };
const query = this.#queries.get(requestID); const query = queries.get(requestID);
const cloned = foundry.utils.deepClone(query); const cloned = foundry.utils.deepClone(query);
delete cloned.onSubmit; delete cloned.onSubmit;
@ -51,7 +52,7 @@ export class QueryManager {
return foundry.utils.deepFreeze(cloned); return foundry.utils.deepFreeze(cloned);
}; };
static async query( export async function query(
request, request,
{ {
onSubmit = sendBasicNotification, onSubmit = sendBasicNotification,
@ -75,7 +76,7 @@ export class QueryManager {
}, },
}); });
if (this.#promises.has(request.id)) { if (promises.has(request.id)) {
return null; return null;
}; };
@ -91,7 +92,7 @@ export class QueryManager {
status[user] = game.users.get(user).active ? `waiting` : `unprompted`; status[user] = game.users.get(user).active ? `waiting` : `unprompted`;
}; };
this.#queries.set( queries.set(
request.id, request.id,
{ {
users, users,
@ -109,14 +110,14 @@ export class QueryManager {
if (showStatusApp) { if (showStatusApp) {
const app = new QueryStatus({ requestID: request.id }); const app = new QueryStatus({ requestID: request.id });
app.render({ force: true }); app.render({ force: true });
this.#queries.get(request.id).app = app; queries.get(request.id).app = app;
}; };
return promise; return promise;
}; };
static async requery(requestID, users) { export async function requery(requestID, users) {
const query = this.#queries.get(requestID); const query = queries.get(requestID);
if (!query) { return }; if (!query) { return };
game.socket.emit(`system.taf`, { game.socket.emit(`system.taf`, {
@ -135,24 +136,24 @@ export class QueryManager {
query.app?.render({ parts: [ `users` ] }); query.app?.render({ parts: [ `users` ] });
}; };
static async addResponse(requestID, userID, answers) { export async function addResponse(requestID, userID, answers) {
const data = this.#queries.get(requestID); const data = queries.get(requestID);
data.responses[userID] = answers; data.responses[userID] = answers;
data.status[userID] = `finished`; data.status[userID] = `finished`;
await data.onSubmit?.(userID, answers); await data.onSubmit?.(userID, answers);
this.maybeResolve(requestID); await maybeResolve(requestID);
}; };
static async maybeResolve(requestID) { async function maybeResolve(requestID) {
const data = this.#queries.get(requestID); const query = queries.get(requestID);
// Determine how many users are considered "finished" // Determine how many users are considered "finished"
Oliver marked this conversation as resolved Outdated

Add check to ensure that the query isn't undefined

Add check to ensure that the query isn't undefined
let finishedUserCount = 0; let finishedUserCount = 0;
for (const user of data.users) { for (const user of query.users) {
const hasApp = data.app != null; const hasApp = query.app != null;
switch (data.status[user]) { switch (query.status[user]) {
case `finished`: { case `finished`: {
finishedUserCount++; finishedUserCount++;
break; break;
@ -169,15 +170,15 @@ export class QueryManager {
}; };
// Ensure that we have a finished response from everyone prompted // Ensure that we have a finished response from everyone prompted
if (data.users.length === finishedUserCount) { if (query.users.length === finishedUserCount) {
data.app?.close(); query.app?.close();
data.resolve(data.responses); query.resolve(query.responses);
} else { } else {
data.app?.render({ parts: [ `users` ] }); query.app?.render({ parts: [ `users` ] });
}; };
}; };
static async notify(userID, content, { includeGM = false } = {}) { export async function notify(userID, content, { includeGM = false } = {}) {
game.socket.emit(`system.taf`, { game.socket.emit(`system.taf`, {
event: `query.notify`, event: `query.notify`,
payload: { payload: {
@ -186,11 +187,11 @@ export class QueryManager {
includeGM, includeGM,
}, },
}); });
} };
static async cancel(requestID) { export async function cancel(requestID) {
// prevent cancelling other people's queries // prevent cancelling other people's queries
if (!this.#queries.has(requestID)) { return }; if (!queries.has(requestID)) { return };
game.socket.emit(`system.taf`, { game.socket.emit(`system.taf`, {
event: `query.cancel`, event: `query.cancel`,
@ -198,10 +199,10 @@ export class QueryManager {
}); });
}; };
static async setApplication(requestID, app) { export async function setApplication(requestID, app) {
if (!this.#queries.has(requestID)) { return }; if (!queries.has(requestID)) { return };
if (!(app instanceof QueryStatus)) { return }; if (!(app instanceof QueryStatus)) { return };
const query = this.#queries.get(requestID); const query = queries.get(requestID);
if (query.app) { if (query.app) {
Logger.error(`Cannot set an application for a query that has one already`); Logger.error(`Cannot set an application for a query that has one already`);
return; return;
@ -209,8 +210,8 @@ export class QueryManager {
query.app = app; query.app = app;
}; };
static async userActivity(userID, connected) { export async function userActivity(userID, connected) {
for (const [id, query] of this.#queries.entries()) { for (const [id, query] of queries.entries()) {
if (query.users.includes(userID)) { if (query.users.includes(userID)) {
// Update the user's status to allow for the app to re-prompt them // Update the user's status to allow for the app to re-prompt them
@ -220,11 +221,20 @@ export class QueryManager {
} else { } else {
query.status[userID] = `disconnected`; query.status[userID] = `disconnected`;
}; };
this.maybeResolve(id); maybeResolve(id);
}; };
query.app?.render({ parts: [ `users` ] }); query.app?.render({ parts: [ `users` ] });
}; };
}; };
}; };
export const QueryManager = {
has, get,
query, requery,
addResponse,
notify,
cancel,
setApplication,
userActivity,
}; };