Fix asset store pagination when navigating folders and correctly installing visible assets (#8466)

This commit is contained in:
Clément Pasteau
2026-04-01 11:43:15 +02:00
committed by GitHub
parent 948b83e9db
commit a57b2bbee2
3 changed files with 104 additions and 31 deletions
+41 -9
View File
@@ -111,17 +111,28 @@ const getPageBreakAssetLowerIndex = (pageBreakIndex: number) =>
const getPageBreakAssetUpperIndex = (pageBreakIndex: number) =>
ASSETS_DISPLAY_LIMIT * (pageBreakIndex + 1);
export const filterAssetShortHeadersByFolders = (
allAssetShortHeaders: AssetShortHeader[],
selectedFolders: string[]
): AssetShortHeader[] => {
if (!selectedFolders.length) return allAssetShortHeaders;
return allAssetShortHeaders.filter(assetShortHeader =>
// Check that the asset has all the selected folders tags.
selectedFolders.every(folderTag =>
assetShortHeader.tags.includes(folderTag)
)
);
};
export const getAssetShortHeadersToDisplay = (
allAssetShortHeaders: AssetShortHeader[],
selectedFolders: string[],
pageBreakIndex: number = 0
): AssetShortHeader[] => {
let assetShortHeaders = allAssetShortHeaders.filter(assetShortHeader => {
if (!selectedFolders.length) return true;
const allAssetTags = assetShortHeader.tags;
// Check that the asset has all the selected folders tags.
return selectedFolders.every(folderTag => allAssetTags.includes(folderTag));
});
let assetShortHeaders = filterAssetShortHeadersByFolders(
allAssetShortHeaders,
selectedFolders
);
// Limit the number of displayed assets to avoid performance issues
const pageBreakAssetLowerIndex = getPageBreakAssetLowerIndex(pageBreakIndex);
const pageBreakAssetUpperIndex = Math.min(
@@ -154,6 +165,8 @@ const styles = {
scrollView: {
display: 'flex',
flexDirection: 'column',
// Prevent horizontal scrollbars.
overflowX: 'hidden',
},
previewImageContainer: {
display: 'flex',
@@ -341,6 +354,25 @@ const AssetsList: React.ComponentType<{
},
[currentPage]
);
React.useEffect(
() => {
setPageBreakIndex(0);
if (currentPage) currentPage.pageBreakIndex = 0;
},
// selectedFolders is a new array reference each time a folder is selected,
// so this resets pagination whenever the folder selection changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
[selectedFolders]
);
const folderFilteredAssetShortHeaders = React.useMemo<
Array<AssetShortHeader>
>(
() =>
assetShortHeaders
? filterAssetShortHeadersByFolders(assetShortHeaders, selectedFolders)
: [],
[assetShortHeaders, selectedFolders]
);
const { windowSize, isLandscape } = useResponsiveWindowSize();
const scrollView = React.useRef<?ScrollViewInterface>(null);
React.useImperativeHandle(ref, () => ({
@@ -973,13 +1005,13 @@ const AssetsList: React.ComponentType<{
!isAssetPackAudioOnly(openedAssetPack)) &&
noResultComponent}
{currentPage &&
assetShortHeaders &&
assetShortHeaders.length > getPageBreakAssetUpperIndex(0) && (
folderFilteredAssetShortHeaders.length >
getPageBreakAssetUpperIndex(0) && (
<PageBreakNavigation
currentPage={currentPage}
pageBreakIndex={pageBreakIndex}
setPageBreakIndex={setPageBreakIndex}
assetShortHeaders={assetShortHeaders}
assetShortHeaders={folderFilteredAssetShortHeaders}
scrollView={scrollView.current}
/>
)}
+26 -10
View File
@@ -308,9 +308,9 @@ function NewObjectDialog({
selectedFolders,
} = shopNavigationState.getCurrentPage();
const [
isAssetPackDialogInstallOpen,
setIsAssetPackDialogInstallOpen,
] = React.useState(false);
assetShortHeadersToInstall,
setAssetShortHeadersToInstall,
] = React.useState<?Array<AssetShortHeader>>(null);
// Avoid memoizing the result of enumerateAssetStoreIds, as it does not get updated
// when adding assets.
const existingAssetStoreIds = enumerateAssetStoreIds(
@@ -423,6 +423,22 @@ function NewObjectDialog({
[assetShortHeadersSearchResults, selectedFolders]
);
const openAssetPackInstallDialog = React.useCallback(
() => {
const currentPage = shopNavigationState.getCurrentPage();
setAssetShortHeadersToInstall(
assetShortHeadersSearchResults
? getAssetShortHeadersToDisplay(
assetShortHeadersSearchResults,
currentPage.selectedFolders,
currentPage.pageBreakIndex || 0
)
: []
);
},
[shopNavigationState, assetShortHeadersSearchResults]
);
const mainAction =
currentTab === 'asset-store' ? (
openedAssetPack ? (
@@ -436,7 +452,7 @@ function NewObjectDialog({
<Trans>Add these assets to my scene</Trans>
)
}
onClick={() => setIsAssetPackDialogInstallOpen(true)}
onClick={openAssetPackInstallDialog}
disabled={
!displayedAssetShortHeaders ||
displayedAssetShortHeaders.length === 0
@@ -543,7 +559,7 @@ function NewObjectDialog({
onRequestClose={handleClose}
onApply={
openedAssetPack
? () => setIsAssetPackDialogInstallOpen(true)
? openAssetPackInstallDialog
: openedAssetShortHeader
? async () => {
await onInstallAsset(openedAssetShortHeader);
@@ -603,16 +619,16 @@ function NewObjectDialog({
))}
</Dialog>
{isAssetBeingInstalled && <LoaderModal showImmediately />}
{isAssetPackDialogInstallOpen &&
displayedAssetShortHeaders &&
{assetShortHeadersToInstall &&
!!assetShortHeadersToInstall.length &&
openedAssetPack && (
<AssetPackInstallDialog
assetPack={openedAssetPack}
assetShortHeaders={displayedAssetShortHeaders}
assetShortHeaders={assetShortHeadersToInstall}
addedAssetIds={existingAssetStoreIds}
onClose={() => setIsAssetPackDialogInstallOpen(false)}
onClose={() => setAssetShortHeadersToInstall(null)}
onAssetsAdded={installAssetOutput => {
setIsAssetPackDialogInstallOpen(false);
setAssetShortHeadersToInstall(null);
onObjectsAddedFromAssets(installAssetOutput);
}}
project={project}
@@ -10,7 +10,10 @@ import { AssetStoreContext } from '../../../../AssetStore/AssetStoreContext';
import AssetPackInstallDialog from '../../../../AssetStore/AssetPackInstallDialog';
import { enumerateAssetStoreIds } from '../../../../AssetStore/EnumerateAssetStoreIds';
import { type PrivateGameTemplateListingData } from '../../../../Utils/GDevelopServices/Shop';
import { type Course } from '../../../../Utils/GDevelopServices/Asset';
import {
type AssetShortHeader,
type Course,
} from '../../../../Utils/GDevelopServices/Asset';
import ErrorBoundary from '../../../../UI/ErrorBoundary';
import { getAssetShortHeadersToDisplay } from '../../../../AssetStore/AssetsList';
import { AssetStoreNavigatorContext } from '../../../../AssetStore/AssetStoreNavigator';
@@ -42,9 +45,9 @@ const StoreSection = ({
getCourseCompletion,
}: Props) => {
const [
isAssetPackDialogInstallOpen,
setIsAssetPackDialogInstallOpen,
] = React.useState(false);
assetShortHeadersToInstall,
setAssetShortHeadersToInstall,
] = React.useState<?Array<AssetShortHeader>>(null);
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
const { assetShortHeadersSearchResults } = React.useContext(
AssetStoreContext
@@ -71,7 +74,7 @@ const StoreSection = ({
const existingAssetStoreIds = React.useMemo(
() => {
if (!project || !isAssetPackDialogInstallOpen) {
if (!project || !assetShortHeadersToInstall) {
return new Set<string>();
}
@@ -82,7 +85,29 @@ const StoreSection = ({
// Force recompute existing installed asset ids when the dialog to install them is
// opened/closed, so that this list of ids is always up-to-date (but not recomputed at each render,
// just when you're likely to need it).
isAssetPackDialogInstallOpen,
assetShortHeadersToInstall,
]
);
const openAssetPackInstallDialog = React.useCallback(
() => {
const currentPage = shopNavigationState.getCurrentPage();
setAssetShortHeadersToInstall(
openedAssetShortHeader
? [openedAssetShortHeader]
: assetShortHeadersSearchResults
? getAssetShortHeadersToDisplay(
assetShortHeadersSearchResults,
currentPage.selectedFolders,
currentPage.pageBreakIndex || 0
)
: []
);
},
[
shopNavigationState,
openedAssetShortHeader,
assetShortHeadersSearchResults,
]
);
@@ -107,7 +132,7 @@ const StoreSection = ({
return; // TODO: create a project, await, and then show dialog.
}
setIsAssetPackDialogInstallOpen(true);
openAssetPackInstallDialog();
}}
disabled={!project || !displayedAssetShortHeaders.length}
label={
@@ -131,15 +156,15 @@ const StoreSection = ({
</Line>
)}
{project &&
isAssetPackDialogInstallOpen &&
!!displayedAssetShortHeaders.length && (
assetShortHeadersToInstall &&
!!assetShortHeadersToInstall.length && (
<AssetPackInstallDialog
assetPack={openedAssetPack}
assetShortHeaders={displayedAssetShortHeaders}
assetShortHeaders={assetShortHeadersToInstall}
addedAssetIds={existingAssetStoreIds}
onClose={() => setIsAssetPackDialogInstallOpen(false)}
onClose={() => setAssetShortHeadersToInstall(null)}
onAssetsAdded={() => {
setIsAssetPackDialogInstallOpen(false);
setAssetShortHeadersToInstall(null);
}}
project={project}
objectsContainer={null}