Skip to content

Commit

Permalink
Update system for compatibility with fvtt dev 2
Browse files Browse the repository at this point in the history
  • Loading branch information
stwlam committed Feb 2, 2025
1 parent e9652cc commit 52d1dc6
Show file tree
Hide file tree
Showing 17 changed files with 331 additions and 203 deletions.
60 changes: 30 additions & 30 deletions src/module/apps/sidebar/chat-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,28 +339,28 @@ class ChatLogPF2e extends ChatLog<ChatMessagePF2e> {
}

protected override _getEntryContextOptions(): EntryContextOption[] {
const canApplyDamage: ContextOptionCondition = ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
const canApplyDamage: ContextOptionCondition = (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
return canvas.tokens.controlled.length > 0 && message.rolls.some((r) => r instanceof DamageRoll);
};

const canApplyTripleDamage: ContextOptionCondition = ($li: JQuery) =>
canApplyDamage($li) && game.pf2e.settings.critFumble.buttons;
const canApplyTripleDamage: ContextOptionCondition = (li) =>
canApplyDamage(li) && game.pf2e.settings.critFumble.buttons;

const canReroll: ContextOptionCondition = ($li: JQuery): boolean => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
const canReroll: ContextOptionCondition = (li): boolean => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
return message.isRerollable;
};

const canHeroPointReroll: ContextOptionCondition = ($li: JQuery): boolean => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
const canHeroPointReroll: ContextOptionCondition = (li): boolean => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
const messageActor = message.actor;
const actor = messageActor?.isOfType("familiar") ? messageActor.master : messageActor;
return message.isRerollable && !!actor?.isOfType("character") && actor.heroPoints.value > 0;
};

const canShowRollDetails: ContextOptionCondition = ($li: JQuery): boolean => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
const canShowRollDetails: ContextOptionCondition = (li): boolean => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
return game.user.isGM && !!message.flags.pf2e.context;
};

Expand All @@ -370,89 +370,89 @@ class ChatLogPF2e extends ChatLog<ChatMessagePF2e> {
name: "PF2E.ChatRollDetails.Select",
icon: fontAwesomeIcon("search").outerHTML,
condition: canShowRollDetails,
callback: ($li) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
message.showDetails();
},
},
{
name: "PF2E.DamageButton.FullContext",
icon: fontAwesomeIcon("heart-broken").outerHTML,
condition: canApplyDamage,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
applyDamageFromMessage({ message });
},
},
{
name: "PF2E.DamageButton.HalfContext",
icon: fontAwesomeIcon("heart-broken").outerHTML,
condition: canApplyDamage,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
applyDamageFromMessage({ message, multiplier: 0.5 });
},
},
{
name: "PF2E.DamageButton.DoubleContext",
icon: fontAwesomeIcon("heart-broken").outerHTML,
condition: canApplyDamage,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
applyDamageFromMessage({ message, multiplier: 2 });
},
},
{
name: "PF2E.DamageButton.TripleContext",
icon: fontAwesomeIcon("heart-broken").outerHTML,
condition: canApplyTripleDamage,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
applyDamageFromMessage({ message, multiplier: 3 });
},
},
{
name: "PF2E.DamageButton.HealingContext",
icon: fontAwesomeIcon("heart").outerHTML,
condition: canApplyDamage,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
applyDamageFromMessage({ message, multiplier: -1 });
},
},
{
name: "PF2E.RerollMenu.HeroPoint",
icon: fontAwesomeIcon("hospital-symbol").outerHTML,
condition: canHeroPointReroll,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
CheckPF2e.rerollFromMessage(message, { heroPoint: true });
},
},
{
name: "PF2E.RerollMenu.KeepNew",
icon: fontAwesomeIcon("dice").outerHTML,
condition: canReroll,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
CheckPF2e.rerollFromMessage(message);
},
},
{
name: "PF2E.RerollMenu.KeepLower",
icon: fontAwesomeIcon("dice-one").outerHTML,
condition: canReroll,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
CheckPF2e.rerollFromMessage(message, { keep: "lower" });
},
},
{
name: "PF2E.RerollMenu.KeepHigher",
icon: fontAwesomeIcon("dice-six").outerHTML,
condition: canReroll,
callback: ($li: JQuery) => {
const message = game.messages.get($li[0].dataset.messageId, { strict: true });
callback: (li) => {
const message = game.messages.get(li.dataset.messageId, { strict: true });
CheckPF2e.rerollFromMessage(message, { keep: "higher" });
},
},
Expand Down
108 changes: 66 additions & 42 deletions src/module/apps/sidebar/compendium-directory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ActorPF2e } from "@actor";
import type { ApplicationConfiguration } from "@fvtt-client/applications/_types.d.ts";
import { HandlebarsRenderOptions } from "@fvtt-client/applications/api/handlebars-application.ts";
import { ItemPF2e } from "@item";
import { ErrorPF2e, fontAwesomeIcon, htmlQuery } from "@util";
import { ErrorPF2e, createHTMLElement, fontAwesomeIcon, htmlQuery } from "@util";
import MiniSearch from "minisearch";
import { CompendiumMigrationStatus } from "../compendium-migration-status.ts";

Expand All @@ -10,6 +12,20 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {

static #searchEngine: MiniSearch<CompendiumIndexData> | null = null;

/** Include ability to search and drag document search results */
static override DEFAULT_OPTIONS: DeepPartial<ApplicationConfiguration> = {
actions: {
onOpenBrowser: CompendiumDirectoryPF2e.#onOpenBrowser,
},
};

declare matchDragDrop: DragDrop;

static override PARTS = {
...super.PARTS,
match: { template: "systems/pf2e/templates/sidebar/compendium-directory/search-result.hbs" },
};

get searchEngine(): MiniSearch<CompendiumIndexData> {
if (!CompendiumDirectoryPF2e.#searchEngine) {
const wordSegmenter =
Expand Down Expand Up @@ -42,18 +58,6 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
return CompendiumDirectoryPF2e.#searchEngine;
}

/** Include ability to search and drag document search results */
static override get defaultOptions(): ApplicationOptions {
const options = super.defaultOptions;
options.dragDrop.push({ dragSelector: "ol.document-matches > li.match" });

return {
...options,
filters: [{ inputSelector: "input[type=search]", contentSelector: "ol.directory-list" }],
template: "systems/pf2e/templates/sidebar/compendium-directory.hbs",
};
}

/** Create a drag preview that looks like the one generated from an open compendium */
get #dragPreview(): HTMLElement {
const preview = document.createElement("div");
Expand All @@ -66,43 +70,64 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
return preview;
}

override async getData(options?: Partial<ApplicationOptions>): Promise<CompendiumDirectoryDataPF2e> {
override async _prepareContext(options: HandlebarsRenderOptions): Promise<CompendiumDirectoryDataPF2e> {
return {
...(await super.getData(options)),
...(await super._prepareContext(options)),
searchContents: game.user.settings.searchPackContents,
isV13: game.release.generation === 13,
};
}

protected override _onRender(
context: CompendiumDirectoryDataPF2e,
options: HandlebarsRenderOptions,
): Promise<void> {
if (options.parts.includes("directory")) {
const matchesList = createHTMLElement("ol", { classes: ["document-matches"] });
const html = this.element;
html.querySelector("ol.directory-list")?.append(matchesList);
const browserButton = createHTMLElement("button", {
dataset: { action: "onOpenBrowser" },
children: [fontAwesomeIcon("magnifying-glass"), game.i18n.localize("PF2E.CompendiumBrowser.Title")],
});
html.querySelector(":scope > footer")?.append(browserButton);
this.matchDragDrop = new DragDrop({
dragSelector: ".directory-item",
dropSelector: ".directory-list",
permissions: {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this),
},
callbacks: {
dragover: this._onDragOver.bind(this),
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this),
},
}).bind(html);
}
return super._onRender(context, options);
}

/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */

override activateListeners($html: JQuery): void {
super.activateListeners($html);

// Hook in the compendium browser
$html[0].querySelector("footer > button")?.addEventListener("click", () => {
game.pf2e.compendiumBrowser.render(true);
});
}

protected override _getEntryContextOptions(): EntryContextOption[] {
const options = super._getEntryContextOptions();

options.push({
name: "COMPENDIUM.MigrationStatus",
icon: fontAwesomeIcon("info").outerHTML,
condition: ($li) => {
const compendium = game.packs.get($li.data("pack"), { strict: true });
condition: (li) => {
const compendium = game.packs.get(li.dataset.pack, { strict: true });
const actorOrItem =
compendium.documentClass === CONFIG.Actor.documentClass ||
compendium.documentClass === CONFIG.Item.documentClass;
const isSystemCompendium = compendium.metadata.packageType === "system";
return game.user.isGM && actorOrItem && !isSystemCompendium;
},
callback: async ($li) => {
const compendium = game.packs.get($li.data("pack"), { strict: true }) as CompendiumCollection<
callback: async (li) => {
const compendium = game.packs.get(li.dataset.pack, { strict: true }) as CompendiumCollection<
ActorPF2e<null> | ItemPF2e<null>
>;
new CompendiumMigrationStatus(compendium).render(true);
Expand All @@ -113,10 +138,10 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
}

/** Add a context menu for content search results */
protected override _contextMenu($html: JQuery): void {
super._contextMenu($html);
protected override _contextMenu(html: HTMLElement): void {
super._contextMenu(html);

ContextMenu.create(this, $html, "ol.document-matches > li", [
ContextMenu.create(this, html, "ol.document-matches > li", [
{
name: "COMPENDIUM.ImportEntry",
icon: fontAwesomeIcon("download").outerHTML,
Expand Down Expand Up @@ -152,15 +177,12 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
/** System compendium search */
protected override _onSearchFilter(event: KeyboardEvent, query: string, rgx: RegExp, listElem: HTMLElement): void {
super._onSearchFilter(event, query, rgx, listElem);
const html = this.element[0];
const html = this.element;

// Match documents within each compendium by name
const docMatches = query.length > 0 ? this.searchEngine.search(query) : [];
const activeFilters = this.activeFilters;
const filteredMatches =
this.activeFilters.length > 0
? docMatches.filter((m) => activeFilters.includes(m.documentType))
: docMatches;
const filters = this.activeFilters;
const filteredMatches = filters.size > 0 ? docMatches.filter((m) => filters.has(m.documentType)) : docMatches;

// Create a list of document matches
const matchTemplate = htmlQuery<HTMLTemplateElement>(html, ".compendium-search-match");
Expand Down Expand Up @@ -203,14 +225,12 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
const matchesList = htmlQuery(html, "ol.document-matches");
if (!matchesList) return;
matchesList.replaceChildren(...listElements);
for (const dragDrop of this._dragDrop) {
dragDrop.bind(matchesList);
}
this.matchDragDrop.bind(matchesList);
}

/** Anyone can drag from search results */
protected override _canDragStart(): boolean {
return true;
protected override _canDragStart(selector: string): boolean {
return selector === "ol.document-matches" || super._canDragStart(selector);
}

/** Replicate the functionality of dragging a compendium document from an open `Compendium` */
Expand Down Expand Up @@ -262,6 +282,10 @@ class CompendiumDirectoryPF2e extends CompendiumDirectory {
}
console.debug("PF2e System | Finished compiling search index");
}

static async #onOpenBrowser(this: CompendiumDirectoryPF2e): Promise<void> {
game.pf2e.compendiumBrowser.render({ force: true });
}
}

interface CompendiumDirectoryPF2e extends CompendiumDirectory {
Expand Down
10 changes: 4 additions & 6 deletions src/module/apps/sidebar/item-directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,16 @@ export class ItemDirectoryPF2e<TItem extends ItemPF2e<null>> extends ItemDirecto
options.push({
name: "PF2E.Item.Physical.Attach.SidebarContextMenuOption",
icon: fontAwesomeIcon("paperclip").outerHTML,
condition: ($li) => {
const row = $li[0];
const item = game.items.get(row.dataset.documentId, { strict: true });
condition: (li) => {
const item = game.items.get(li.dataset.documentId, { strict: true });
return (
item.isOwner &&
item.isOfType("physical") &&
game.items.some((i) => i !== item && i.isOwner && i.isOfType("physical") && i.acceptsSubitem(item))
);
},
callback: ($li) => {
const row = $li[0];
const item = game.items.get(row.dataset.documentId, { strict: true });
callback: (li) => {
const item = game.items.get(li.dataset.documentId, { strict: true });
if (
item.isOwner &&
item.isOfType("physical") &&
Expand Down
12 changes: 5 additions & 7 deletions src/scripts/hooks/render-settings.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { MigrationSummary } from "@module/apps/migration-summary.ts";
import { ErrorPF2e, createHTMLElement, fontAwesomeIcon } from "@util";
import { ErrorPF2e, createHTMLElement, fontAwesomeIcon, htmlQuery } from "@util";

/** Attach system buttons and other knickknacks to the settings sidebar */
export const RenderSettings = {
listen: (): void => {
Hooks.on("renderSettings", async (_app, $html) => {
const html = $html[0];
Hooks.on("renderSettings", async (_app, html) => {
// Additional system information resources
const systemRow = html.querySelector<HTMLLIElement>(".settings-sidebar li.system");
const systemRow = htmlQuery(html, "section.info .system");
const systemInfo = systemRow?.cloneNode(false);
if (!(systemInfo instanceof HTMLLIElement)) {
if (!(systemInfo instanceof HTMLElement)) {
throw ErrorPF2e("Unexpected error attaching system information to settings sidebar");
}

Expand All @@ -29,9 +28,8 @@ export const RenderSettings = {
label: "PF2E.SETTINGS.Sidebar.Discord",
},
].map((data): HTMLAnchorElement => {
const anchor = document.createElement("a");
const anchor = createHTMLElement("a", { children: [game.i18n.localize(data.label)] });
anchor.href = data.url;
anchor.innerText = game.i18n.localize(data.label);
anchor.target = "_blank";
return anchor;
});
Expand Down
Loading

0 comments on commit 52d1dc6

Please sign in to comment.