Data Request API helper #10
11 changed files with 117 additions and 14 deletions
|
|
@ -15,6 +15,9 @@
|
|||
"PlayerSheet": "Player Sheet"
|
||||
},
|
||||
"Apps": {
|
||||
"QueryStatus": {
|
||||
"user-disconnected-tooltip": "This user is not logged in to Foundry"
|
||||
},
|
||||
"TAFDocumentSheetConfig": {
|
||||
"Sizing": "Sizing",
|
||||
"Width": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { __ID__, filePath } from "../consts.mjs";
|
||||
import { Logger } from "../utils/Logger.mjs";
|
||||
import { QueryManager } from "../utils/QueryManager.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
|
@ -10,15 +11,22 @@ export class QueryStatus extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
__ID__,
|
||||
`QueryStatus`,
|
||||
],
|
||||
position: {
|
||||
width: 300,
|
||||
height: `auto`,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
users: {
|
||||
template: filePath(`templates/QueryStatus/users.hbs`),
|
||||
},
|
||||
// controls: {
|
||||
// template: filePath(`templates/QueryStatus/controls.hbs`),
|
||||
// },
|
||||
controls: {
|
||||
template: filePath(`templates/QueryStatus/controls.hbs`),
|
||||
},
|
||||
};
|
||||
// #endregion Options
|
||||
|
||||
|
|
@ -27,6 +35,10 @@ export class QueryStatus extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
requestID,
|
||||
...opts
|
||||
}) {
|
||||
if (!requestID) {
|
||||
Logger.error(`A requestID must be provided for QueryStatus applications`);
|
||||
return null;
|
||||
};
|
||||
super(opts);
|
||||
this.requestID = requestID;
|
||||
};
|
||||
|
|
@ -60,7 +72,7 @@ export class QueryStatus extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
users.push({
|
||||
id: userID,
|
||||
name: user.name,
|
||||
colour: user.color,
|
||||
active: user.active,
|
||||
answers: query.responses[userID] ?? null,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
5
module/hooks/userConnected.mjs
Normal file
5
module/hooks/userConnected.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { QueryManager } from "../utils/QueryManager.mjs";
|
||||
|
||||
Hooks.on(`userConnected`, (user) => {
|
||||
QueryManager.userActivity(user.id);
|
||||
});
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
import "./api.mjs";
|
||||
import "./hooks/init.mjs";
|
||||
import "./hooks/userConnected.mjs";
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@
|
|||
* @property {Function} resolve
|
||||
* @property {Record<string, object>} responses
|
||||
* @property {(() => Promise<void>)|null} onSubmit
|
||||
* @property {QueryStatus|null} app
|
||||
*/
|
||||
|
||||
import { filePath } from "../consts.mjs";
|
||||
import { Logger } from "./Logger.mjs";
|
||||
import { QueryStatus } from "../apps/QueryStatus.mjs";
|
||||
|
||||
async function sendBasicNotification(userID, answers) {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
|
|
@ -34,7 +37,7 @@ export class QueryManager {
|
|||
delete cloned.onSubmit;
|
||||
delete cloned.resolve;
|
||||
|
||||
return cloned;
|
||||
return foundry.utils.deepFreeze(cloned);
|
||||
};
|
||||
|
||||
static async query(
|
||||
|
|
@ -42,7 +45,8 @@ export class QueryManager {
|
|||
{
|
||||
onSubmit = sendBasicNotification,
|
||||
users = null,
|
||||
config = undefined,
|
||||
showStatusApp = true,
|
||||
...config
|
||||
} = {},
|
||||
) {
|
||||
if (!request.id) {
|
||||
|
|
@ -68,13 +72,21 @@ export class QueryManager {
|
|||
this.#queries.set(
|
||||
request.id,
|
||||
{
|
||||
users: users ?? game.users.filter(u => u.id !== game.user.id),
|
||||
users: users ?? game.users.filter(u => u.id !== game.user.id).map(u => u.id),
|
||||
resolve,
|
||||
responses: {},
|
||||
onSubmit,
|
||||
app: null,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (showStatusApp) {
|
||||
const app = new QueryStatus({ requestID: request.id });
|
||||
app.render({ force: true });
|
||||
this.#queries.get(request.id).app = app;
|
||||
};
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
|
|
@ -86,7 +98,10 @@ export class QueryManager {
|
|||
|
||||
// Validate for responses from everyone
|
||||
if (data.users.length === Object.keys(data.responses).length) {
|
||||
data.app.close();
|
||||
data.resolve(data.responses);
|
||||
} else {
|
||||
data.app?.render({ parts: [ `users` ] });
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -110,4 +125,27 @@ export class QueryManager {
|
|||
payload: { id: requestID },
|
||||
});
|
||||
};
|
||||
|
||||
static async setApplication(requestID, app) {
|
||||
if (!this.#queries.has(requestID)) { return };
|
||||
if (!(app instanceof QueryStatus)) { return };
|
||||
const query = this.#queries.get(requestID);
|
||||
if (query.app) {
|
||||
Logger.error(`Cannot set an application for a query that has one already`);
|
||||
return;
|
||||
};
|
||||
query.app = app;
|
||||
};
|
||||
|
||||
static async userActivity(userID) {
|
||||
for (const query of this.#queries.values()) {
|
||||
if (query.users.includes(userID)) {
|
||||
query.app.render({ parts: [ `users` ] });
|
||||
|
||||
// TODO: if the user is connecting, we want to open
|
||||
// the ask modal on their browser so that they can
|
||||
// actually fill in the data
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
Oliver marked this conversation as resolved
Outdated
|
||||
|
|
|
|||
20
styles/Apps/QueryStatus.css
Normal file
20
styles/Apps/QueryStatus.css
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
.taf.QueryStatus {
|
||||
.user-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
border: 1px solid yellowgreen;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
.taf > .window-content span {
|
||||
&.loader {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
--size: 40px;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
animation: rotate 2s linear infinite;
|
||||
|
|
@ -38,7 +39,7 @@
|
|||
&::after{
|
||||
inset: 8px;
|
||||
transform: rotate3d(90, 90, 0, 180deg );
|
||||
border-color: #FF3D00; /* This can be the user colour */
|
||||
border-color: var(--spinner-inner-colour, #FF3D00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
styles/elements/utils.css
Normal file
3
styles/elements/utils.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.taf > .window-content {
|
||||
.grow { flex-grow: 1; }
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
@import url("./themes/light.css") layer(themes);
|
||||
|
||||
/* Elements */
|
||||
@import url("./elements/utils.css") layer(elements);
|
||||
@import url("./elements/headers.css") layer(elements);
|
||||
@import url("./elements/hr.css") layer(elements);
|
||||
@import url("./elements/input.css") layer(elements);
|
||||
|
|
@ -22,4 +23,5 @@
|
|||
@import url("./Apps/Ask.css") layer(apps);
|
||||
@import url("./Apps/AttributeManager.css") layer(apps);
|
||||
@import url("./Apps/PlayerSheet.css") layer(apps);
|
||||
@import url("./Apps/QueryStatus.css") layer(apps);
|
||||
@import url("./Apps/TAFDocumentSheetConfig.css") layer(apps);
|
||||
|
|
|
|||
8
templates/QueryStatus/controls.hbs
Normal file
8
templates/QueryStatus/controls.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div>
|
||||
<button data-action="">
|
||||
Cancel Request
|
||||
|
Oliver marked this conversation as resolved
Outdated
Oliver
commented
Localizing this would be nice Localizing this would be nice
|
||||
</button>
|
||||
<button data-action="">
|
||||
Finish Request Early
|
||||
|
Oliver marked this conversation as resolved
Outdated
Oliver
commented
Localizing this would be nice Localizing this would be nice
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
<ul>
|
||||
<ul class="user-list">
|
||||
{{#each users as | user |}}
|
||||
<li>
|
||||
{{ user.name }}
|
||||
<li style="--spinner-inner-colour: var(--user-color-{{user.id}})">
|
||||
<div class="grow">
|
||||
{{ user.name }}
|
||||
</div>
|
||||
{{#if user.answers}}
|
||||
<div class="chip-list">
|
||||
{{#each user.answers as | answer |}}
|
||||
|
|
@ -13,8 +15,16 @@
|
|||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{else if user.active}}
|
||||
<span class="loader"></span>
|
||||
{{else}}
|
||||
<taf-icon
|
||||
data-tooltip="taf.Apps.QueryStatus.user-disconnected-tooltip"
|
||||
name="icons/disconnected"
|
||||
var:size="40px"
|
||||
var:stroke="currentColor"
|
||||
var:fill="currentColor"
|
||||
></taf-icon>
|
||||
{{/if}}
|
||||
|
Oliver marked this conversation as resolved
Oliver
commented
Localize Localize
|
||||
</li>
|
||||
{{/each}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue
Add check to ensure that the query isn't undefined