Skip to content

Commit

Permalink
Feat: Clean up the Actor crafting app salvage UI (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
misterpotts authored Nov 14, 2023
1 parent 23fa902 commit c46fb2e
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 69 deletions.
2 changes: 1 addition & 1 deletion docs/_config.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
title: Fabricate 0.10.14
title: Fabricate 0.10.15
email: [email protected]
description: >-
End user documentation for the Foundry Virtual Tabletop (VTT) Module, "Fabricate".
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fabricate",
"version": "0.10.14",
"version": "0.10.15",
"description": "A system-agnostic, flexible crafting module for FoundryVT",
"main": "index.js",
"type": "module",
Expand Down
26 changes: 23 additions & 3 deletions src/applications/actorCraftingApp/ActorCraftingApp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import type {FabricateAPI} from "../../scripts/api/FabricateAPI";
import type {ActorDetails} from "./ActorDetails";
import {PendingActorDetails} from "./ActorDetails";
import {onMount} from "svelte";
import {onMount, setContext} from "svelte";
import eventBus from "../common/EventBus";
import type {CraftingAssessment} from "./CraftingAssessment";
import {type CraftingProcess, DefaultCraftingProcess, NoCraftingProcess} from "./CraftingProcess";
import {NoSalvageProcess, type SalvageProcess} from "./SalvageProcess";
Expand All @@ -20,6 +21,9 @@
import RecipeCraftingProcess from "./RecipeCraftingProcess.svelte";
import ComponentSalvageProcess from "./ComponentSalvageProcess.svelte";
import ActorInventoryBrowser from "./ActorInventoryBrowser.svelte";
import {ComponentsStore} from "./ComponentsStore";
import {key} from "./ActorCraftingAppManager";
import {EssencesStore} from "./EssencesStore";
/*
* ===========================================================================
Expand Down Expand Up @@ -48,6 +52,10 @@
let craftingAssessments: CraftingAssessment[] = [];
let salvageAssessments: SalvageAssessment[] = [];
const componentsById = new ComponentsStore({ fabricateAPI });
const essencesById = new EssencesStore({ fabricateAPI, components: componentsById });
setContext(key, { componentsById, essencesById });
/*
* ===========================================================================
* Component member functions
Expand All @@ -70,6 +78,7 @@
async function load() {
await prepareRecipes();
await prepareComponents();
await componentsById.loadAll();
}
function startCraftingProcess(event: CustomEvent<CraftingAssessment>) {
Expand Down Expand Up @@ -125,13 +134,24 @@
}
}
function handleOwnedItemChanged(event: CustomEvent<{item: any, sourceId: string, actor:Actor}>) {
console.log("reload", event.detail);
if (event.detail.actor.id === sourceActorDetails.id) {
load();
}
}
onMount(load);
</script>

<svelte:window on:keydown={onKeyDown} on:keyup={onKeyUp} />

<div class="flex flex-col w-full h-full">
<div class="flex flex-col w-full h-full"
use:eventBus={["itemUpdated", "itemCreated", "itemDeleted"]}
on:itemUpdated={handleOwnedItemChanged}
on:itemCreated={handleOwnedItemChanged}
on:itemDeleted={handleOwnedItemChanged}>
<ActorCraftingAppHeader localization={localization}
targetActorDetails={targetActorDetails}
sourceActorDetails={sourceActorDetails}
Expand All @@ -143,7 +163,7 @@
<ComponentSalvageProcess localization={localization}
bind:salvageProcess={salvageProcess}
on:salvageComponent={salvageComponent}
batchSize={batchSize} />
batchSize={batchSize}/>
{:else}
<ActorInventoryBrowser localization={localization}
bind:craftingAssessments={craftingAssessments}
Expand Down
3 changes: 3 additions & 0 deletions src/applications/actorCraftingApp/ActorCraftingAppManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const key = Symbol();

export { key }
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
rounded="rounded-r-none rounded-l-md" />
<div class="flex flex-col p-2">
<p class="font-bold text-white mb-2">
{truncate(salvageAssessment.componentName, 18, 12)}
{truncate(salvageAssessment.componentName, 18, 12)} {#if salvageAssessment.quantity > 1}({salvageAssessment.quantity}){/if}
</p>
{#if salvageAssessment.hasEssences}
<div class="flex-row flex">
Expand Down
21 changes: 21 additions & 0 deletions src/applications/actorCraftingApp/AwaitedItemCard.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
/*
* ===========================================================================
* Imports
* ===========================================================================
*/
import {Avatar} from "@skeletonlabs/skeleton";
</script>

<div class="card snap-start h-full bg-surface-700 flex flex-row col-span-1 row-span-1">
<Avatar alt="Loading item..." initials="?" width="w-24" rounded="rounded-r-none rounded-l-md" />
<div class="flex flex-col w-2/3 p-2">
<div class="placeholder rounded-xl w-2/3 h-4 mb-2 animate-pulse"></div>
<div class="placeholder rounded-xl w-3/4 h-2 mt-2 animate-pulse"></div>
<div class="placeholder rounded-xl w-4/4 h-2 mt-2 animate-pulse"></div>
<div class="placeholder rounded-xl w-3/4 h-2 mt-2 animate-pulse"></div>
</div>
</div>
95 changes: 33 additions & 62 deletions src/applications/actorCraftingApp/ComponentSalvageProcess.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
import {NoSalvageProcess, type SalvageOption, type SalvageProcess} from "./SalvageProcess";
import type {LocalizationService} from "../common/LocalizationService";
import Properties from "../../scripts/Properties";
import truncate from "../common/Truncate";
import {createEventDispatcher} from "svelte";
import {createEventDispatcher, getContext} from "svelte";
import ItemCard from "./ItemCard.svelte";
import AwaitedItemCard from "./AwaitedItemCard.svelte";
import {key} from "./ActorCraftingAppManager";
import type {Essence} from "../../scripts/crafting/essence/Essence";
import type {Component} from "../../scripts/crafting/component/Component";
/*
* ===========================================================================
Expand Down Expand Up @@ -39,6 +43,14 @@
$: salvageProcessCatalysts = selectedSalvageOption.catalysts;
$: selectedOptionName = selectedSalvageOption.name;
const {
componentsById,
essencesById
}: {
componentsById: Map<string, Component>;
essencesById: Map<string, Essence>;
} = getContext(key);
/*
* ===========================================================================
* Private component functions
Expand Down Expand Up @@ -75,6 +87,10 @@
return `(${ownedQuantity})`
}
function dereferenceEssences(component: Component) {
return component.essences.convertElements(essence => $essencesById.get(essence.id));
}
</script>

<AppBar background="bg-surface-700 text-white">
Expand Down Expand Up @@ -153,26 +169,12 @@
<div class="grid grid-cols-2 grid-rows-4 h-full gap-4">
{#each salvageProcessProducts.units as productUnit}
{#await productUnit.element.load()}
Loading...
<AwaitedItemCard/>
{:then loadedComponent}
<div class="card snap-start h-full bg-surface-700 flex flex-row col-span-1 row-span-1 relative">
<Avatar class=""
src="{loadedComponent.imageUrl}"
alt="{loadedComponent.name}"
fallback="{Properties.ui.defaults.componentImageUrl}"
width="w-24"
rounded="rounded-r-none rounded-l-md"/>
{#if productUnit.quantity > 1}
<span class="text-black badge-icon text-lg font-light variant-filled-secondary w-7 h-7 absolute left-2 top-2 z-10">
{productUnit.quantity}
</span>
{/if}
<div class="flex flex-col p-2">
<p class="text-white mb-2 font-bold">
{truncate(loadedComponent.name, 18, 12)}
</p>
</div>
</div>
<ItemCard quantity={productUnit.quantity}
name={loadedComponent.name}
imageUrl={loadedComponent.imageUrl}
essences={dereferenceEssences(loadedComponent)}/>
{/await}
{/each}
</div>
Expand All @@ -185,29 +187,12 @@
<div class="grid grid-cols-1 grid-rows-4 h-full gap-4">
{#each salvageProcessCatalysts.units as catalystUnit}
{#await catalystUnit.target.element.load()}
Loading...
<AwaitedItemCard/>
{:then loadedComponent}
<div class="card snap-start h-full bg-surface-700 flex flex-row col-span-1 row-span-1 relative">
<Avatar src="{loadedComponent.imageUrl}"
alt="{loadedComponent.name}"
fallback="{Properties.ui.defaults.componentImageUrl}"
width="w-24"
rounded="rounded-r-none rounded-l-md"/>
{#if catalystUnit.isSufficient}
<span class="absolute bottom-0 left-0 rounded-bl-lg w-24 bg-success-500 text-center text-black font-bold h-5 leading-5">
{catalystUnit.actual.quantity} / {catalystUnit.target.quantity}
</span>
{:else}
<span class="absolute bottom-0 left-0 rounded-bl-lg w-24 bg-error-500 text-center text-black font-bold h-5 leading-5">
{catalystUnit.actual.quantity} / {catalystUnit.target.quantity}
</span>
{/if}
<div class="flex flex-col p-2">
<p class="text-white mb-2 font-bold">
{truncate(loadedComponent.name, 18, 12)}
</p>
</div>
</div>
<ItemCard quantity={catalystUnit.actual.quantity}
requiredQuantity={catalystUnit.target.quantity}
name={loadedComponent.name}
imageUrl={loadedComponent.imageUrl}/>
{/await}
{/each}
</div>
Expand All @@ -222,26 +207,12 @@
<div class="grid grid-cols-3 grid-rows-4 h-full gap-4">
{#each salvageProcessProducts.units as productUnit}
{#await productUnit.element.load()}
Loading...
<AwaitedItemCard/>
{:then loadedComponent}
<div class="card snap-start h-full bg-surface-700 flex flex-row col-span-1 row-span-1 relative">
<Avatar class=""
src="{loadedComponent.imageUrl}"
alt="{loadedComponent.name}"
fallback="{Properties.ui.defaults.componentImageUrl}"
width="w-24"
rounded="rounded-r-none rounded-l-md"/>
{#if productUnit.quantity > 1}
<span class="text-black badge-icon text-lg font-light variant-filled-secondary w-7 h-7 absolute left-2 top-2 z-10">
{productUnit.quantity}
</span>
{/if}
<div class="flex flex-col p-2">
<p class="text-white mb-2 font-bold">
{truncate(loadedComponent.name, 18, 12)}
</p>
</div>
</div>
<ItemCard quantity={productUnit.quantity}
name={loadedComponent.name}
imageUrl={loadedComponent.imageUrl}
essences={dereferenceEssences(loadedComponent)}/>
{/await}
{/each}
</div>
Expand Down
70 changes: 70 additions & 0 deletions src/applications/actorCraftingApp/ComponentsStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {Readable, Subscriber, Unsubscriber, writable, Writable} from "svelte/store";
import {Component} from "../../scripts/crafting/component/Component";
import {FabricateAPI} from "../../scripts/api/FabricateAPI";

class ComponentsStore implements Readable<Map<string, Component>> {

private readonly _components: Writable<Map<string, Component>>;
private readonly _fabricateAPI: FabricateAPI;

constructor({
fabricateAPI,
components = new Map<string, Component>(),
}: {
fabricateAPI: FabricateAPI;
components?: Map<string, Component>;
}) {
this._fabricateAPI = fabricateAPI;
this._components = writable(components);
}

public async loadAll() {
const allComponentsById = await this._fabricateAPI.components.getAll();
const allComponentsLoaded = await Promise.all(Array.from(allComponentsById.values()).map(component => component.load()));
allComponentsLoaded.forEach((component) => {
allComponentsById.set(component.id, component);
});
this._components.set(allComponentsById);
}

public async add(component: Component) {
const loadedComponent = await component.load(true);
this._components.update((storedComponents) => {
storedComponents.set(loadedComponent.id, loadedComponent);
return storedComponents;
});
}

public async addAll(componentsToAdd: Component[]) {
const loadedComponents = await Promise.all(componentsToAdd.map(component => component.load(true)));
this._components.update((storedComponents) => {
loadedComponents.forEach((component) => {
storedComponents.set(component.id, component);
});
return storedComponents;
});
}

public async remove(component: Component) {
this._components.update((storedComponents) => {
storedComponents.delete(component.id);
return storedComponents;
});
}

public async removeAll(componentsToDelete: Component[]) {
this._components.update((storedComponents) => {
componentsToDelete.forEach((component) => {
storedComponents.delete(component.id);
});
return storedComponents;
});
}

subscribe(subscriber: Subscriber<Map<string, Component>>): Unsubscriber {
return this._components.subscribe(subscriber);
}

}

export { ComponentsStore }
Loading

0 comments on commit c46fb2e

Please sign in to comment.