Skip to content

Commit

Permalink
feat: add the useAssets hook
Browse files Browse the repository at this point in the history
  • Loading branch information
trezy committed Jul 18, 2024
1 parent 7fdb11c commit f32a0d6
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 3 deletions.
18 changes: 18 additions & 0 deletions src/helpers/getAssetKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { UnresolvedAsset } from '../typedefs/UnresolvedAsset';

/** Retrieves the key from an unresolved asset. */
export function getAssetKey<T>(asset: UnresolvedAsset<T>)
{
let assetKey;

if (typeof asset === 'string')
{
assetKey = asset;
}
else
{
assetKey = (asset.alias ?? asset.src) as string;
}

return assetKey;
}
9 changes: 6 additions & 3 deletions src/hooks/useAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
Assets,
Cache,
} from 'pixi.js';
import { getAssetKeyFromOptions } from '../helpers/getAssetKeyFromOptions.ts';
import { getAssetKey } from '../helpers/getAssetKey.ts';

import type {
ProgressCallback,
Expand All @@ -14,7 +14,10 @@ import type { ErrorCallback } from '../typedefs/ErrorCallback.ts';

const errorCache: Map<UnresolvedAsset | string, AssetRetryState> = new Map();

/** Loads assets, returning a hash of assets once they're loaded. */
/**
* Loads assets, returning a hash of assets once they're loaded.
* @deprecated Use `useAssets` instead.
*/
export function useAsset<T>(
/** @description Asset options. */
options: (UnresolvedAsset<T> & AssetRetryOptions) | string,
Expand Down Expand Up @@ -43,7 +46,7 @@ export function useAsset<T>(
retryOnFailure = true,
} = typeof options !== 'string' ? options : {};

const assetKey = getAssetKeyFromOptions(options);
const assetKey = getAssetKey(options);

if (!Cache.has(assetKey))
{
Expand Down
84 changes: 84 additions & 0 deletions src/hooks/useAssets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
Assets,
Cache,
} from 'pixi.js';
import { getAssetKey } from '../helpers/getAssetKey.ts';

import type { AssetRetryState } from '../typedefs/AssetRetryState.ts';
import type { UnresolvedAsset } from '../typedefs/UnresolvedAsset.ts';
import type { UseAssetsOptions } from '../typedefs/UseAssetsOptions.ts';

const errorCache: Map<UnresolvedAsset, AssetRetryState> = new Map();

/** Loads assets, returning a hash of assets once they're loaded. */
export function useAssets<T>(
/** @description Assets to be loaded. */
assets: UnresolvedAsset<T>[],
/** @description Asset options. */
options: UseAssetsOptions = {},
): T[]
{
if (typeof window === 'undefined')
{
throw Object.assign(Error('`useAsset` will only run on the client.'), {
digest: 'BAILOUT_TO_CLIENT_SIDE_RENDERING',
});
}

const {
maxRetries = 3,
onError,
onProgress,
retryOnFailure = true,
} = options;

const allAssetsAreLoaded = assets.some((asset: UnresolvedAsset<T>) => Cache.has(getAssetKey(asset)));

if (!allAssetsAreLoaded)
{
let state = errorCache.get(assets);

// Rethrow the cached error if we are not retrying on failure or have reached the max retries
if (state && (!retryOnFailure || state.retries > maxRetries))
{
if (typeof onError === 'function')
{
onError(state.error);
}
else
{
throw state.error;
}
}

throw Assets
.load<T>(assets, (progressValue) =>
{
if (typeof onProgress === 'function')
{
onProgress(progressValue);
}
})
.catch((error) =>
{
if (!state)
{
state = {
error,
retries: 0,
};
}

errorCache.set(assets, {
...state,
error,
retries: state.retries + 1,
});
});
}

const assetKeys = assets.map((asset: UnresolvedAsset<T>) => getAssetKey(asset));
const resolvedAssetsDictionary = Assets.get<T>(assetKeys) as Record<string, T>;

return assets.map((_asset: UnresolvedAsset<T>, index: number) => resolvedAssetsDictionary[index]);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from './global.ts';
export { extend } from './helpers/extend.ts';
export { useApp } from './hooks/useApp.ts';
export { useAsset } from './hooks/useAsset.ts';
export { useAssets } from './hooks/useAssets.ts';
export { useExtend } from './hooks/useExtend.ts';
export { useTick } from './hooks/useTick.ts';
3 changes: 3 additions & 0 deletions src/typedefs/UnresolvedAsset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { UnresolvedAsset as PixiUnresolvedAsset } from 'pixi.js';

export type UnresolvedAsset<T = any> = PixiUnresolvedAsset<T> | string;
18 changes: 18 additions & 0 deletions src/typedefs/UseAssetsOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type ErrorCallback } from './ErrorCallback.ts';

import type { ProgressCallback } from 'pixi.js';

export interface UseAssetsOptions
{
/** @description The maximum number of retries allowed before we give up on loading this asset. */
maxRetries?: number

/** @description A function to be called when if the asset loader encounters an error. */
onError?: ErrorCallback,

/** @description A function to be called when the asset loader reports loading progress. */
onProgress?: ProgressCallback,

/** @description Whether to try loading this asset again if it fails. */
retryOnFailure?: boolean
}

0 comments on commit f32a0d6

Please sign in to comment.