Skip to content

Commit 35709f6

Browse files
committed
Unify extension installation workflow
1 parent ec15ae6 commit 35709f6

File tree

16 files changed

+984
-787
lines changed

16 files changed

+984
-787
lines changed

newIDE/app/src/AiGeneration/UseEnsureExtensionInstalled.js

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
import * as React from 'react';
33
import { type I18n as I18nType } from '@lingui/core';
44
import { ExtensionStoreContext } from '../AssetStore/ExtensionStore/ExtensionStoreContext';
5-
import EventsFunctionsExtensionsContext from '../EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsContext';
6-
import { installExtension } from '../AssetStore/ExtensionStore/InstallExtension';
5+
import {
6+
useInstallExtension,
7+
checkRequiredExtensionsUpdate,
8+
getRequiredExtensions,
9+
getExtensionHeader,
10+
} from '../AssetStore/ExtensionStore/InstallExtension';
11+
import { type ExtensionShortHeader } from '../Utils/GDevelopServices/Extension';
712

813
type EnsureExtensionInstalledOptions = {|
914
extensionName: string,
@@ -16,39 +21,46 @@ export const useEnsureExtensionInstalled = ({
1621
project: ?gdProject,
1722
i18n: I18nType,
1823
|}) => {
19-
const { translatedExtensionShortHeadersByName } = React.useContext(
20-
ExtensionStoreContext
21-
);
22-
const eventsFunctionsExtensionsState = React.useContext(
23-
EventsFunctionsExtensionsContext
24-
);
24+
const {
25+
translatedExtensionShortHeadersByName: extensionShortHeadersByName,
26+
} = React.useContext(ExtensionStoreContext);
27+
const installExtension = useInstallExtension();
2528

2629
return {
2730
ensureExtensionInstalled: React.useCallback(
2831
async ({ extensionName }: EnsureExtensionInstalledOptions) => {
2932
if (!project) return;
30-
if (project.getCurrentPlatform().isExtensionLoaded(extensionName))
31-
return;
3233

33-
const extensionShortHeader =
34-
translatedExtensionShortHeadersByName[extensionName];
35-
if (!extensionShortHeader) {
36-
throw new Error(`Can't find extension named ${extensionName}.`);
37-
}
38-
39-
await installExtension(
40-
i18n,
41-
project,
42-
eventsFunctionsExtensionsState,
43-
extensionShortHeader
34+
const extensionShortHeader = getExtensionHeader(
35+
extensionShortHeadersByName,
36+
extensionName
4437
);
38+
const extensionShortHeaders: Array<ExtensionShortHeader> = [
39+
extensionShortHeader,
40+
];
41+
const requiredExtensions = getRequiredExtensions(extensionShortHeaders);
42+
requiredExtensions.push({
43+
extensionName: extensionShortHeader.name,
44+
extensionVersion: extensionShortHeader.version,
45+
});
46+
const requiredExtensionInstallation = await checkRequiredExtensionsUpdate(
47+
{
48+
requiredExtensions,
49+
project,
50+
extensionShortHeadersByName,
51+
}
52+
);
53+
await installExtension({
54+
project,
55+
requiredExtensionInstallation,
56+
userSelectedExtensionNames: [],
57+
importedSerializedExtensions: [],
58+
// TODO
59+
onExtensionInstalled: () => {},
60+
updateMode: 'safeOnly',
61+
});
4562
},
46-
[
47-
eventsFunctionsExtensionsState,
48-
i18n,
49-
project,
50-
translatedExtensionShortHeadersByName,
51-
]
63+
[extensionShortHeadersByName, installExtension, project]
5264
),
5365
};
5466
};

newIDE/app/src/AssetStore/AssetPackInstallDialog.js

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ import RaisedButtonWithSplitMenu from '../UI/RaisedButtonWithSplitMenu';
1515
import { Column, Line } from '../UI/Grid';
1616
import {
1717
checkRequiredExtensionsUpdateForAssets,
18-
installRequiredExtensions,
1918
installPublicAsset,
20-
type RequiredExtensionInstallation,
2119
complyVariantsToEventsBasedObjectOf,
2220
} from './InstallAsset';
23-
import EventsFunctionsExtensionsContext from '../EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsContext';
21+
import { useInstallExtension } from './ExtensionStore/InstallExtension';
2422
import { showErrorBox } from '../UI/Messages/MessageBox';
2523
import LinearProgress from '../UI/LinearProgress';
2624
import PrivateAssetsAuthorizationContext from './PrivateAssets/PrivateAssetsAuthorizationContext';
@@ -32,12 +30,10 @@ import RadioGroup from '@material-ui/core/RadioGroup';
3230
import { mapFor } from '../Utils/MapFor';
3331
import FormControlLabel from '@material-ui/core/FormControlLabel';
3432
import AlertMessage from '../UI/AlertMessage';
35-
import {
36-
useExtensionUpdateAlertDialog,
37-
useFetchAssets,
38-
} from './NewObjectDialog';
33+
import { useFetchAssets } from './NewObjectDialog';
3934
import { type InstallAssetOutput } from './InstallAsset';
4035
import { type ObjectFolderOrObjectWithContext } from '../ObjectsList/EnumerateObjectFolderOrObject';
36+
import { ExtensionStoreContext } from './ExtensionStore/ExtensionStoreContext';
4137

4238
// We limit the number of assets that can be installed at once to avoid
4339
// timeouts especially with premium packs.
@@ -94,15 +90,15 @@ const AssetPackInstallDialog = ({
9490
[resourceManagementProps]
9591
);
9692

97-
const eventsFunctionsExtensionsState = React.useContext(
98-
EventsFunctionsExtensionsContext
99-
);
10093
const { installPrivateAsset } = React.useContext(
10194
PrivateAssetsAuthorizationContext
10295
);
96+
const {
97+
translatedExtensionShortHeadersByName: extensionShortHeadersByName,
98+
} = React.useContext(ExtensionStoreContext);
99+
const installExtension = useInstallExtension();
103100

104101
const fetchAssets = useFetchAssets();
105-
const showExtensionUpdateConfirmation = useExtensionUpdateAlertDialog();
106102

107103
const [selectedLayoutName, setSelectedLayoutName] = React.useState<string>(
108104
''
@@ -155,31 +151,25 @@ const AssetPackInstallDialog = ({
155151
setAreAssetsBeingInstalled(true);
156152
try {
157153
const assets = await fetchAssets(assetShortHeaders);
158-
const requiredExtensionInstallation: RequiredExtensionInstallation = await checkRequiredExtensionsUpdateForAssets(
154+
155+
const requiredExtensionInstallation = await checkRequiredExtensionsUpdateForAssets(
159156
{
160157
assets,
161158
project,
159+
extensionShortHeadersByName,
162160
}
163161
);
164-
const extensionUpdateAction =
165-
requiredExtensionInstallation.outOfDateExtensionShortHeaders
166-
.length === 0
167-
? 'skip'
168-
: await showExtensionUpdateConfirmation({
169-
project,
170-
outOfDateExtensionShortHeaders:
171-
requiredExtensionInstallation.outOfDateExtensionShortHeaders,
172-
});
173-
if (extensionUpdateAction === 'abort') {
174-
return;
175-
}
176-
await installRequiredExtensions({
177-
requiredExtensionInstallation,
178-
shouldUpdateExtension: extensionUpdateAction === 'update',
179-
eventsFunctionsExtensionsState,
162+
const wasExtensionsInstalled = await installExtension({
180163
project,
164+
requiredExtensionInstallation,
165+
userSelectedExtensionNames: [],
166+
importedSerializedExtensions: [],
181167
onExtensionInstalled,
168+
updateMode: 'all',
182169
});
170+
if (!wasExtensionsInstalled) {
171+
return;
172+
}
183173

184174
// Use a pool to avoid installing an unbounded amount of assets at the same time.
185175
const { errors, results } = await PromisePool.withConcurrency(6)
@@ -252,11 +242,11 @@ const AssetPackInstallDialog = ({
252242
[
253243
fetchAssets,
254244
project,
255-
showExtensionUpdateConfirmation,
256-
eventsFunctionsExtensionsState,
245+
extensionShortHeadersByName,
246+
installExtension,
247+
onExtensionInstalled,
257248
resourceManagementProps,
258249
onAssetsAdded,
259-
onExtensionInstalled,
260250
installPrivateAsset,
261251
targetObjectsContainer,
262252
targetObjectFolderOrObjectWithContext,

newIDE/app/src/AssetStore/ExtensionStore/ExtensionsSearchDialog.js

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
import { Trans } from '@lingui/macro';
3+
import { t } from '@lingui/macro';
34
import { I18n } from '@lingui/react';
45
import { type I18n as I18nType } from '@lingui/core';
56
import * as React from 'react';
@@ -8,7 +9,11 @@ import FlatButton from '../../UI/FlatButton';
89
import { ExtensionStore } from '.';
910
import EventsFunctionsExtensionsContext from '../../EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsContext';
1011
import HelpButton from '../../UI/HelpButton';
11-
import { importExtension, installExtension } from './InstallExtension';
12+
import {
13+
useImportExtension,
14+
useInstallExtension,
15+
getRequiredExtensions,
16+
} from './InstallExtension';
1217
import DismissableInfoBar from '../../UI/Messages/DismissableInfoBar';
1318
import { type ExtensionShortHeader } from '../../Utils/GDevelopServices/Extension';
1419
import AuthenticatedUserContext from '../../Profile/AuthenticatedUserContext';
@@ -20,7 +25,9 @@ import { useResponsiveWindowSize } from '../../UI/Responsive/ResponsiveWindowMea
2025
import Download from '../../UI/CustomSvgIcons/Download';
2126
import Add from '../../UI/CustomSvgIcons/Add';
2227
import ErrorBoundary from '../../UI/ErrorBoundary';
23-
import useAlertDialog from '../../UI/Alert/useAlertDialog';
28+
import { checkRequiredExtensionsUpdate } from '../../AssetStore/ExtensionStore/InstallExtension';
29+
import { showErrorBox } from '../../UI/Messages/MessageBox';
30+
import { ExtensionStoreContext } from './ExtensionStoreContext';
2431

2532
type Props = {|
2633
project: gdProject,
@@ -41,6 +48,11 @@ const ExtensionsSearchDialog = ({
4148
onCreateNew,
4249
}: Props) => {
4350
const { isMobile } = useResponsiveWindowSize();
51+
const installExtension = useInstallExtension();
52+
const {
53+
translatedExtensionShortHeadersByName: extensionShortHeadersByName,
54+
} = React.useContext(ExtensionStoreContext);
55+
const importExtension = useImportExtension();
4456
const [isInstalling, setIsInstalling] = React.useState(false);
4557
const [extensionWasInstalled, setExtensionWasInstalled] = React.useState(
4658
false
@@ -50,48 +62,75 @@ const ExtensionsSearchDialog = ({
5062
);
5163
const authenticatedUser = React.useContext(AuthenticatedUserContext);
5264

53-
const installDisplayedExtension = addCreateBadgePreHookIfNotClaimed(
65+
const createBadgeFistExtension = addCreateBadgePreHookIfNotClaimed(
5466
authenticatedUser,
5567
TRIVIAL_FIRST_EXTENSION,
56-
installExtension
68+
() => {}
5769
);
58-
const { showConfirmation, showAlert } = useAlertDialog();
5970

6071
const installOrImportExtension = async (
6172
i18n: I18nType,
6273
extensionShortHeader?: ExtensionShortHeader
63-
) => {
74+
): Promise<boolean> => {
6475
setIsInstalling(true);
6576
try {
66-
let installedOrImportedExtensionName: string | null = null;
67-
if (!!extensionShortHeader) {
77+
if (extensionShortHeader) {
78+
// TODO do it for all extensions
6879
onInstallExtension(extensionShortHeader.name);
69-
const wasExtensionInstalledOrImported = await installDisplayedExtension(
70-
i18n,
71-
project,
72-
eventsFunctionsExtensionsState,
73-
extensionShortHeader
74-
);
75-
installedOrImportedExtensionName = wasExtensionInstalledOrImported
76-
? extensionShortHeader.name
77-
: null;
80+
try {
81+
const extensionShortHeaders: Array<ExtensionShortHeader> = [
82+
extensionShortHeader,
83+
];
84+
const requiredExtensions = getRequiredExtensions(
85+
extensionShortHeaders
86+
);
87+
requiredExtensions.push({
88+
extensionName: extensionShortHeader.name,
89+
extensionVersion: extensionShortHeader.version,
90+
});
91+
const requiredExtensionInstallation = await checkRequiredExtensionsUpdate(
92+
{
93+
requiredExtensions,
94+
project,
95+
extensionShortHeadersByName,
96+
}
97+
);
98+
const wasExtensionInstalled = await installExtension({
99+
project,
100+
requiredExtensionInstallation,
101+
userSelectedExtensionNames: [extensionShortHeader.name],
102+
importedSerializedExtensions: [],
103+
onExtensionInstalled,
104+
updateMode: 'all',
105+
});
106+
if (!wasExtensionInstalled) {
107+
return false;
108+
}
109+
createBadgeFistExtension();
110+
setExtensionWasInstalled(true);
111+
} catch (rawError) {
112+
showErrorBox({
113+
message: i18n._(
114+
t`Unable to download and install the extension and its dependencies. Verify that your internet connection is working or try again later.`
115+
),
116+
rawError,
117+
errorId: 'download-extension-error',
118+
});
119+
return false;
120+
}
78121
} else {
79-
installedOrImportedExtensionName = await importExtension(
122+
const installedOrImportedExtensionNames = await importExtension({
80123
i18n,
81-
eventsFunctionsExtensionsState,
82124
project,
83-
onInstallExtension,
84-
showConfirmation,
85-
showAlert
86-
);
125+
onWillInstallExtension: onInstallExtension,
126+
onExtensionInstalled,
127+
});
128+
if (installedOrImportedExtensionNames.length > 0) {
129+
setExtensionWasInstalled(true);
130+
onExtensionInstalled(installedOrImportedExtensionNames);
131+
return true;
132+
}
87133
}
88-
89-
if (installedOrImportedExtensionName) {
90-
setExtensionWasInstalled(true);
91-
onExtensionInstalled([installedOrImportedExtensionName]);
92-
return true;
93-
}
94-
95134
return false;
96135
} finally {
97136
setIsInstalling(false);

0 commit comments

Comments
 (0)