@automattic/launchpad 1.2.4 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/dist/cjs/action-components/share-site-modal/index.js +2 -2
- package/dist/cjs/action-components/share-site-modal/index.js.map +1 -1
- package/dist/cjs/checklist-item/style.scss +3 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/launchpad-internal.js +2 -2
- package/dist/cjs/launchpad-internal.js.map +1 -1
- package/dist/cjs/launchpad.js +5 -9
- package/dist/cjs/launchpad.js.map +1 -1
- package/dist/cjs/setup-actions.js +3 -3
- package/dist/cjs/setup-actions.js.map +1 -1
- package/dist/cjs/site-type.js +3 -0
- package/dist/cjs/site-type.js.map +1 -0
- package/dist/cjs/use-launchpad.js +151 -0
- package/dist/cjs/use-launchpad.js.map +1 -0
- package/dist/cjs/use-site.js +38 -0
- package/dist/cjs/use-site.js.map +1 -0
- package/dist/cjs/wpcom-request.js +24 -0
- package/dist/cjs/wpcom-request.js.map +1 -0
- package/dist/esm/action-components/share-site-modal/index.js +1 -1
- package/dist/esm/action-components/share-site-modal/index.js.map +1 -1
- package/dist/esm/checklist-item/style.scss +3 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/launchpad-internal.js +1 -1
- package/dist/esm/launchpad-internal.js.map +1 -1
- package/dist/esm/launchpad.js +3 -6
- package/dist/esm/launchpad.js.map +1 -1
- package/dist/esm/setup-actions.js +1 -1
- package/dist/esm/setup-actions.js.map +1 -1
- package/dist/esm/site-type.js +2 -0
- package/dist/esm/site-type.js.map +1 -0
- package/dist/esm/use-launchpad.js +140 -0
- package/dist/esm/use-launchpad.js.map +1 -0
- package/dist/esm/use-site.js +33 -0
- package/dist/esm/use-site.js.map +1 -0
- package/dist/esm/wpcom-request.js +18 -0
- package/dist/esm/wpcom-request.js.map +1 -0
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/action-components/share-site-modal/index.d.ts +2 -2
- package/dist/types/action-components/share-site-modal/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/launchpad-internal.d.ts +3 -2
- package/dist/types/launchpad-internal.d.ts.map +1 -1
- package/dist/types/launchpad.d.ts +0 -1
- package/dist/types/launchpad.d.ts.map +1 -1
- package/dist/types/site-type.d.ts +10 -0
- package/dist/types/site-type.d.ts.map +1 -0
- package/dist/types/use-launchpad.d.ts +65 -0
- package/dist/types/use-launchpad.d.ts.map +1 -0
- package/dist/types/use-site.d.ts +8 -0
- package/dist/types/use-site.d.ts.map +1 -0
- package/dist/types/use-tracking/index.d.ts +2 -2
- package/dist/types/use-tracking/index.d.ts.map +1 -1
- package/dist/types/wpcom-request.d.ts +7 -0
- package/dist/types/wpcom-request.d.ts.map +1 -0
- package/package.json +12 -10
- package/src/action-components/share-site-modal/index.tsx +5 -5
- package/src/checklist-item/style.scss +3 -1
- package/src/index.ts +2 -0
- package/src/launchpad-internal.tsx +3 -3
- package/src/launchpad.tsx +3 -15
- package/src/setup-actions.ts +1 -1
- package/src/site-type.ts +9 -0
- package/src/test/launchpad.tsx +3 -1
- package/src/test/lib/fixtures.ts +1 -2
- package/src/test/setup-actions.ts +1 -1
- package/src/test/use-site.ts +60 -0
- package/src/use-launchpad.ts +249 -0
- package/src/use-site.ts +36 -0
- package/src/use-tracking/index.tsx +2 -2
- package/src/use-tracking/test/index.tsx +3 -3
- package/src/wpcom-request.ts +26 -0
- package/tsconfig.json +1 -5
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { recordTracksEvent } from '@automattic/calypso-analytics';
|
|
2
|
+
import * as oauthToken from '@automattic/oauth-token';
|
|
3
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
4
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
5
|
+
import { addQueryArgs } from '@wordpress/url';
|
|
6
|
+
import wpcomRequest, { canAccessWpcomApis } from './wpcom-request';
|
|
7
|
+
|
|
8
|
+
interface APIFetchOptions {
|
|
9
|
+
global: boolean;
|
|
10
|
+
path: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Task {
|
|
14
|
+
id: string;
|
|
15
|
+
completed: boolean;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
title: string;
|
|
18
|
+
subtitle?: string;
|
|
19
|
+
badgeText?: string;
|
|
20
|
+
actionDispatch?: () => void;
|
|
21
|
+
isLaunchTask?: boolean;
|
|
22
|
+
warning?: boolean;
|
|
23
|
+
order?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ChecklistStatuses {
|
|
27
|
+
links_edited?: boolean;
|
|
28
|
+
site_edited?: boolean;
|
|
29
|
+
site_launched?: boolean;
|
|
30
|
+
first_post_published?: boolean;
|
|
31
|
+
video_uploaded?: boolean;
|
|
32
|
+
publish_first_course?: boolean;
|
|
33
|
+
plan_selected?: boolean;
|
|
34
|
+
plan_completed?: boolean;
|
|
35
|
+
domain_upsell_deferred?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type LaunchpadScreen = 'full' | 'off' | 'skipped' | 'minimized';
|
|
39
|
+
|
|
40
|
+
export interface LaunchpadResponse {
|
|
41
|
+
site_intent?: string | null;
|
|
42
|
+
launchpad_screen?: LaunchpadScreen | boolean | null | undefined;
|
|
43
|
+
checklist?: Task[] | null;
|
|
44
|
+
checklist_statuses?: ChecklistStatuses;
|
|
45
|
+
is_enabled: boolean;
|
|
46
|
+
is_dismissed: boolean;
|
|
47
|
+
is_dismissible: boolean;
|
|
48
|
+
title?: string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type LaunchpadUpdateSettings = {
|
|
52
|
+
checklist_statuses?: Record< string, boolean >;
|
|
53
|
+
is_checklist_dismissed?: {
|
|
54
|
+
slug: string;
|
|
55
|
+
is_dismissed?: boolean;
|
|
56
|
+
dismissed_until?: number | null;
|
|
57
|
+
};
|
|
58
|
+
launchpad_screen?: 'off' | 'minimized' | 'full' | 'skipped';
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type UseLaunchpadOptions = {
|
|
62
|
+
onSuccess?: ( data: LaunchpadResponse ) => LaunchpadResponse;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const fetchLaunchpad = (
|
|
66
|
+
siteSlug: SiteSlug,
|
|
67
|
+
checklistSlug?: string | null,
|
|
68
|
+
launchpadContext?: string
|
|
69
|
+
): Promise< LaunchpadResponse > => {
|
|
70
|
+
const slug = encodeURIComponent( siteSlug as string );
|
|
71
|
+
const checklistSlugEncoded = checklistSlug ? encodeURIComponent( checklistSlug ) : null;
|
|
72
|
+
const launchpadContextEncoded = launchpadContext ? encodeURIComponent( launchpadContext ) : null;
|
|
73
|
+
const queryArgs = {
|
|
74
|
+
_locale: 'user',
|
|
75
|
+
...( checklistSlug && { checklist_slug: checklistSlugEncoded } ),
|
|
76
|
+
...( launchpadContext && { launchpad_context: launchpadContextEncoded } ),
|
|
77
|
+
};
|
|
78
|
+
const token = oauthToken.getToken();
|
|
79
|
+
return canAccessWpcomApis()
|
|
80
|
+
? wpcomRequest( {
|
|
81
|
+
path: addQueryArgs( `/sites/${ slug }/launchpad`, queryArgs ),
|
|
82
|
+
apiNamespace: 'wpcom/v2',
|
|
83
|
+
method: 'GET',
|
|
84
|
+
token: typeof token === 'string' ? token : undefined,
|
|
85
|
+
} )
|
|
86
|
+
: apiFetch( {
|
|
87
|
+
global: true,
|
|
88
|
+
path: addQueryArgs( '/wpcom/v2/launchpad', queryArgs ),
|
|
89
|
+
} as APIFetchOptions );
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getKey = ( siteSlug: SiteSlug, checklistSlug?: string | null ) => {
|
|
93
|
+
return [ 'launchpad', siteSlug, checklistSlug ];
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const addOrderToTask = ( task: Task, index: number ) => {
|
|
97
|
+
task.order = index;
|
|
98
|
+
return task;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export function sortLaunchpadTasksByCompletionStatus( response: LaunchpadResponse ) {
|
|
102
|
+
const tasks = response.checklist || [];
|
|
103
|
+
const completedTasks = tasks.filter( ( task: Task ) => task.completed );
|
|
104
|
+
const incompleteTasks = tasks.filter( ( task: Task ) => ! task.completed );
|
|
105
|
+
response.checklist = [ ...completedTasks, ...incompleteTasks ].map( addOrderToTask );
|
|
106
|
+
return response;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function defaultSuccessCallback( response: LaunchpadResponse ) {
|
|
110
|
+
const tasks = response.checklist || [];
|
|
111
|
+
response.checklist = tasks.map( addOrderToTask );
|
|
112
|
+
|
|
113
|
+
return response;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type SiteSlug = string | number | null;
|
|
117
|
+
|
|
118
|
+
export const useLaunchpad = (
|
|
119
|
+
siteSlug: SiteSlug,
|
|
120
|
+
checklistSlug?: string | null,
|
|
121
|
+
options?: UseLaunchpadOptions,
|
|
122
|
+
launchpad_context?: string | undefined
|
|
123
|
+
) => {
|
|
124
|
+
const key = getKey( siteSlug, checklistSlug );
|
|
125
|
+
const onSuccessCallback = options?.onSuccess || defaultSuccessCallback;
|
|
126
|
+
|
|
127
|
+
return useQuery( {
|
|
128
|
+
queryKey: key,
|
|
129
|
+
queryFn: () =>
|
|
130
|
+
fetchLaunchpad( siteSlug, checklistSlug, launchpad_context ).then( onSuccessCallback ),
|
|
131
|
+
retry: 3,
|
|
132
|
+
initialData: {
|
|
133
|
+
site_intent: '',
|
|
134
|
+
launchpad_screen: undefined,
|
|
135
|
+
checklist_statuses: {},
|
|
136
|
+
checklist: null,
|
|
137
|
+
is_enabled: false,
|
|
138
|
+
is_dismissed: false,
|
|
139
|
+
is_dismissible: false,
|
|
140
|
+
title: null,
|
|
141
|
+
},
|
|
142
|
+
enabled: Boolean( siteSlug ),
|
|
143
|
+
} );
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const useSortedLaunchpadTasks = (
|
|
147
|
+
siteSlug: SiteSlug,
|
|
148
|
+
checklistSlug?: string | null,
|
|
149
|
+
launchpadContext?: string
|
|
150
|
+
) => {
|
|
151
|
+
const launchpadOptions = {
|
|
152
|
+
onSuccess: sortLaunchpadTasksByCompletionStatus,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return useLaunchpad( siteSlug, checklistSlug, launchpadOptions, launchpadContext );
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const updateLaunchpadSettings = (
|
|
159
|
+
siteSlug: SiteSlug,
|
|
160
|
+
settings: LaunchpadUpdateSettings = {}
|
|
161
|
+
) => {
|
|
162
|
+
const slug = siteSlug ? encodeURIComponent( siteSlug ) : null;
|
|
163
|
+
const token = oauthToken.getToken();
|
|
164
|
+
return canAccessWpcomApis()
|
|
165
|
+
? wpcomRequest( {
|
|
166
|
+
path: `/sites/${ slug }/launchpad`,
|
|
167
|
+
apiNamespace: 'wpcom/v2',
|
|
168
|
+
method: 'PUT',
|
|
169
|
+
body: settings,
|
|
170
|
+
token: typeof token === 'string' ? token : undefined,
|
|
171
|
+
} )
|
|
172
|
+
: apiFetch( {
|
|
173
|
+
global: true,
|
|
174
|
+
path: '/wpcom/v2/launchpad',
|
|
175
|
+
method: 'PUT',
|
|
176
|
+
data: settings,
|
|
177
|
+
} as APIFetchOptions );
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export interface PermanentDismiss {
|
|
181
|
+
isDismissed: boolean;
|
|
182
|
+
}
|
|
183
|
+
export interface TemporaryDismiss {
|
|
184
|
+
dismissBy: '+ 1 day' | '+ 1 week';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
type DismissSettings = PermanentDismiss | TemporaryDismiss;
|
|
188
|
+
|
|
189
|
+
const isPermanentDismiss = ( settings: DismissSettings ): settings is PermanentDismiss =>
|
|
190
|
+
'isDismissed' in settings;
|
|
191
|
+
|
|
192
|
+
const isTemporaryDismiss = ( settings: DismissSettings ): settings is TemporaryDismiss =>
|
|
193
|
+
'dismissBy' in settings;
|
|
194
|
+
|
|
195
|
+
const getDismissParams = ( settings: DismissSettings ) => {
|
|
196
|
+
if ( isPermanentDismiss( settings ) ) {
|
|
197
|
+
return {
|
|
198
|
+
is_dismissed: settings.isDismissed,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if ( isTemporaryDismiss( settings ) ) {
|
|
203
|
+
return {
|
|
204
|
+
dismiss_by: settings.dismissBy,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export const useLaunchpadDismisser = (
|
|
210
|
+
siteSlug: SiteSlug,
|
|
211
|
+
checklistSlug: string,
|
|
212
|
+
launchpadContext: string
|
|
213
|
+
) => {
|
|
214
|
+
const queryClient = useQueryClient();
|
|
215
|
+
const key = getKey( siteSlug, checklistSlug );
|
|
216
|
+
|
|
217
|
+
return useMutation( {
|
|
218
|
+
mutationFn: ( settings: DismissSettings ) => {
|
|
219
|
+
return updateLaunchpadSettings( siteSlug, {
|
|
220
|
+
is_checklist_dismissed: {
|
|
221
|
+
slug: checklistSlug,
|
|
222
|
+
...getDismissParams( settings ),
|
|
223
|
+
},
|
|
224
|
+
} );
|
|
225
|
+
},
|
|
226
|
+
onMutate: async () => {
|
|
227
|
+
await queryClient.cancelQueries( { queryKey: key } );
|
|
228
|
+
const previous = queryClient.getQueryData< LaunchpadResponse >( key );
|
|
229
|
+
|
|
230
|
+
queryClient.setQueryData( key, {
|
|
231
|
+
...previous,
|
|
232
|
+
is_dismissed: true,
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
return { previous };
|
|
236
|
+
},
|
|
237
|
+
onSuccess: () => {
|
|
238
|
+
recordTracksEvent( 'calypso_launchpad_dismiss_guide', {
|
|
239
|
+
checklist_slug: checklistSlug,
|
|
240
|
+
context: launchpadContext,
|
|
241
|
+
} );
|
|
242
|
+
},
|
|
243
|
+
onError: ( _, _2, context ) => {
|
|
244
|
+
if ( context?.previous ) {
|
|
245
|
+
queryClient.setQueryData( key, context?.previous );
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
} );
|
|
249
|
+
};
|
package/src/use-site.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import wpcomRequest, { canAccessWpcomApis } from './wpcom-request';
|
|
3
|
+
import type { LaunchpadSiteDetails } from './site-type';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetches the site fields Launchpad reads. The response is the full site; only these
|
|
7
|
+
* fields are cached. Skipped where the proxy is unavailable, since there is no REST
|
|
8
|
+
* fallback, and kept for the session — both matching the site store this replaces.
|
|
9
|
+
*/
|
|
10
|
+
export const useSite = ( siteSlug: string | null ) => {
|
|
11
|
+
const { data } = useQuery< LaunchpadSiteDetails >( {
|
|
12
|
+
queryKey: [ 'launchpad-site', siteSlug ],
|
|
13
|
+
queryFn: async () => {
|
|
14
|
+
const site = await wpcomRequest< LaunchpadSiteDetails >( {
|
|
15
|
+
path: `/sites/${ encodeURIComponent( siteSlug as string ) }`,
|
|
16
|
+
apiVersion: '1.1',
|
|
17
|
+
query: 'force=wpcom',
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
slug: site?.slug,
|
|
22
|
+
URL: site?.URL,
|
|
23
|
+
options: {
|
|
24
|
+
site_intent: site?.options?.site_intent,
|
|
25
|
+
site_goals: site?.options?.site_goals,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
enabled: Boolean( siteSlug ) && canAccessWpcomApis(),
|
|
30
|
+
staleTime: Infinity,
|
|
31
|
+
gcTime: Infinity,
|
|
32
|
+
meta: { persist: false },
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
return data ?? null;
|
|
36
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { recordTracksEvent } from '@automattic/calypso-analytics';
|
|
2
2
|
import { useEffect, useMemo } from 'react';
|
|
3
|
+
import type { LaunchpadSiteDetails } from '../site-type';
|
|
3
4
|
import type { Task } from '../types';
|
|
4
|
-
import type { SiteDetails } from '@automattic/data-stores';
|
|
5
5
|
|
|
6
6
|
interface Params {
|
|
7
7
|
tasks: Task[];
|
|
8
8
|
checklistSlug: string | null;
|
|
9
9
|
context: string;
|
|
10
|
-
site:
|
|
10
|
+
site: LaunchpadSiteDetails | null | undefined;
|
|
11
11
|
flow?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -2,7 +2,7 @@ import { recordTracksEvent } from '@automattic/calypso-analytics';
|
|
|
2
2
|
import { renderHook } from '@testing-library/react';
|
|
3
3
|
import { useTracking } from '..';
|
|
4
4
|
import { buildTask } from '../../test/lib/fixtures';
|
|
5
|
-
import type {
|
|
5
|
+
import type { LaunchpadSiteDetails } from '../../site-type';
|
|
6
6
|
|
|
7
7
|
jest.mock( '@automattic/calypso-analytics' );
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ describe( 'useTracking', () => {
|
|
|
12
12
|
const buildDefaultProps = ( options = {} ) => ( {
|
|
13
13
|
checklistSlug: 'site-setup-checklist',
|
|
14
14
|
context: 'customer-home',
|
|
15
|
-
site: { options: { site_intent: 'build' } } as
|
|
15
|
+
site: { options: { site_intent: 'build' } } as LaunchpadSiteDetails,
|
|
16
16
|
tasks: [
|
|
17
17
|
buildTask( { id: 'task-1', completed: true } ),
|
|
18
18
|
buildTask( { id: 'task-2', completed: false } ),
|
|
@@ -76,7 +76,7 @@ describe( 'useTracking', () => {
|
|
|
76
76
|
|
|
77
77
|
expect( recordTracksEvent ).toHaveBeenCalledTimes( 0 );
|
|
78
78
|
|
|
79
|
-
rerender( { options: { site_intent: 'some-site-intent' } } as
|
|
79
|
+
rerender( { options: { site_intent: 'some-site-intent' } } as LaunchpadSiteDetails );
|
|
80
80
|
|
|
81
81
|
expect( recordTracksEvent ).toHaveBeenCalledWith(
|
|
82
82
|
'calypso_launchpad_tasklist_viewed',
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import wpcomProxyRequest, {
|
|
2
|
+
type WpcomRequestParams,
|
|
3
|
+
canAccessWpcomApis as originalCanAccessWpcomApis,
|
|
4
|
+
} from 'wpcom-proxy-request';
|
|
5
|
+
|
|
6
|
+
type WpcomRequester = < T >( params: WpcomRequestParams ) => Promise< T >;
|
|
7
|
+
|
|
8
|
+
let customRequester: WpcomRequester | null = null;
|
|
9
|
+
|
|
10
|
+
export function setRequester( requester: WpcomRequester ): void {
|
|
11
|
+
customRequester = requester;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function wpcomRequest< T >( params: WpcomRequestParams ): Promise< T > {
|
|
15
|
+
if ( customRequester ) {
|
|
16
|
+
return customRequester( params );
|
|
17
|
+
}
|
|
18
|
+
return wpcomProxyRequest( params );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function canAccessWpcomApis(): boolean {
|
|
22
|
+
if ( customRequester ) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return originalCanAccessWpcomApis();
|
|
26
|
+
}
|