Skip to content

Commit afc5d4d

Browse files
Allow working on latest used app from demo home page (#1304)
1 parent 14727e3 commit afc5d4d

File tree

3 files changed

+81
-32
lines changed

3 files changed

+81
-32
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const TOOLPAD_LATEST_APP_KEY = 'toolpad-latest-app';
2+
export type LatestStoredAppValue = { appId: string; appName: string } | null;

packages/toolpad-app/src/toolpad/Home/index.tsx

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import config from '../../config';
6363
import { AppTemplateId } from '../../types';
6464
import { errorFrom } from '../../utils/errors';
6565
import { sendAppCreatedEvent } from '../../utils/ga';
66+
import { LatestStoredAppValue, TOOLPAD_LATEST_APP_KEY } from '../../storageKeys';
6667

6768
export const APP_TEMPLATE_OPTIONS: Map<
6869
AppTemplateId,
@@ -105,7 +106,9 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
105106
const [name, setName] = React.useState('');
106107
const [appTemplateId, setAppTemplateId] = React.useState<AppTemplateId>('blank');
107108
const [dom, setDom] = React.useState('');
108-
const [isNavigating, setIsNavigating] = React.useState(false);
109+
110+
const [isNavigatingToNewApp, setIsNavigatingToNewApp] = React.useState(false);
111+
const [isNavigatingToExistingApp, setIsNavigatingToExistingApp] = React.useState(false);
109112

110113
const handleAppTemplateChange = React.useCallback(
111114
(event: React.ChangeEvent<HTMLInputElement>) => {
@@ -122,12 +125,24 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
122125
const createAppMutation = client.useMutation('createApp', {
123126
onSuccess: (app) => {
124127
window.location.href = `/_toolpad/app/${app.id}`;
125-
setIsNavigating(true);
128+
setIsNavigatingToNewApp(true);
126129
},
127130
});
128131

132+
const handleContinueButtonClick = React.useCallback(() => {
133+
setIsNavigatingToExistingApp(true);
134+
}, []);
135+
136+
const [latestStoredApp, setLatestStoredApp] = useLocalStorageState<LatestStoredAppValue>(
137+
TOOLPAD_LATEST_APP_KEY,
138+
null,
139+
);
140+
129141
const isFormValid = Boolean(name);
130142

143+
const isSubmitting =
144+
createAppMutation.isLoading || isNavigatingToNewApp || isNavigatingToExistingApp;
145+
131146
return (
132147
<Dialog {...props} onClose={config.isDemo ? NO_OP : onClose} maxWidth="xs">
133148
<DialogForm
@@ -146,7 +161,7 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
146161
}
147162

148163
const appDom = dom.trim() ? JSON.parse(dom) : null;
149-
await createAppMutation.mutateAsync([
164+
const app = await createAppMutation.mutateAsync([
150165
name,
151166
{
152167
from: {
@@ -158,13 +173,18 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
158173
},
159174
]);
160175

161-
sendAppCreatedEvent(name, appTemplateId);
176+
setLatestStoredApp({
177+
appId: app.id,
178+
appName: app.name,
179+
});
180+
181+
sendAppCreatedEvent(app.name, appTemplateId);
162182
}}
163183
>
164184
<DialogTitle>Create a new App</DialogTitle>
165185
<DialogContent>
166186
{config.isDemo ? (
167-
<Alert severity="warning" sx={{ mb: 2 }}>
187+
<Alert severity="warning" sx={{ mb: 1 }}>
168188
<AlertTitle>For demo purposes only!</AlertTitle>
169189
Your application will be ephemeral and may be deleted at any time.
170190
</Alert>
@@ -182,6 +202,7 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
182202
createAppMutation.reset();
183203
setName(event.target.value);
184204
}}
205+
disabled={isSubmitting}
185206
/>
186207

187208
<TextField
@@ -191,12 +212,15 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
191212
fullWidth
192213
value={appTemplateId}
193214
onChange={handleAppTemplateChange}
215+
disabled={isSubmitting}
194216
>
195217
{Array.from(APP_TEMPLATE_OPTIONS).map(([value, { label, description }]) => (
196218
<MenuItem key={value} value={value}>
197219
<span>
198220
<Typography>{label}</Typography>
199-
<Typography variant="caption">{description || ''}</Typography>
221+
<Typography variant="caption" sx={{ fontWeight: 'normal' }}>
222+
{description || ''}
223+
</Typography>
200224
</span>
201225
</MenuItem>
202226
))}
@@ -211,30 +235,53 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
211235
maxRows={10}
212236
value={dom}
213237
onChange={handleDomChange}
238+
disabled={isSubmitting}
214239
/>
215240
) : null}
216-
{config.recaptchaSiteKey ? (
217-
<Typography variant="caption" color="text.secondary">
218-
This site is protected by reCAPTCHA and the Google{' '}
219-
<Link
220-
href="https://policies.google.com/privacy"
221-
underline="none"
222-
target="_blank"
223-
rel="noopener noreferrer"
241+
{config.isDemo && latestStoredApp ? (
242+
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
243+
<Typography variant="subtitle2" color="text.secondary" textAlign="center">
244+
or
245+
</Typography>
246+
<LoadingButton
247+
variant="outlined"
248+
size="medium"
249+
component="a"
250+
href={`/_toolpad/app/${latestStoredApp.appId}`}
251+
sx={{ mt: 0.5 }}
252+
loading={isNavigatingToExistingApp}
253+
onClick={handleContinueButtonClick}
254+
disabled={isSubmitting}
224255
>
225-
Privacy Policy
226-
</Link>{' '}
227-
and{' '}
228-
<Link
229-
href="https://policies.google.com/terms"
230-
underline="none"
231-
target="_blank"
232-
rel="noopener noreferrer"
233-
>
234-
Terms of Service
235-
</Link>{' '}
236-
apply.
237-
</Typography>
256+
Continue working on &ldquo;{latestStoredApp.appName}&rdquo;
257+
</LoadingButton>
258+
</Box>
259+
) : null}
260+
{config.recaptchaSiteKey ? (
261+
<Box mt={2}>
262+
<Divider sx={{ mb: 1 }} />
263+
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 'normal' }}>
264+
This site is protected by reCAPTCHA and the Google{' '}
265+
<Link
266+
href="https://policies.google.com/privacy"
267+
underline="none"
268+
target="_blank"
269+
rel="noopener noreferrer"
270+
>
271+
Privacy Policy
272+
</Link>{' '}
273+
and{' '}
274+
<Link
275+
href="https://policies.google.com/terms"
276+
underline="none"
277+
target="_blank"
278+
rel="noopener noreferrer"
279+
>
280+
Terms of Service
281+
</Link>{' '}
282+
apply.
283+
</Typography>
284+
</Box>
238285
) : null}
239286
</DialogContent>
240287
<DialogActions>
@@ -252,8 +299,8 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
252299
</Button>
253300
<LoadingButton
254301
type="submit"
255-
loading={createAppMutation.isLoading || isNavigating}
256-
disabled={!isFormValid}
302+
loading={createAppMutation.isLoading || isNavigatingToNewApp}
303+
disabled={!isFormValid || isSubmitting}
257304
>
258305
Create
259306
</LoadingButton>

packages/toolpad-app/src/utils/ga.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ export const reportWebVitalsToGA = ({ id, label, name, value }: NextWebVitalsMet
1919
}
2020
};
2121

22-
export const sendAppCreatedEvent = (name: string, templateId?: AppTemplateId): void => {
22+
export const sendAppCreatedEvent = (appName: string, appTemplateId?: AppTemplateId): void => {
2323
if (config.gaId) {
2424
window.gtag('event', 'toolpad_app_created', {
25-
app_name: name,
26-
app_template_id: templateId,
25+
app_name: appName,
26+
app_template_id: appTemplateId,
2727
});
2828
}
2929
};

0 commit comments

Comments
 (0)