Skip to content

Commit 0bcf276

Browse files
TCA-755 #comment This commit adds the basic sprig html snippet and fixes some lint issues. #time 1h
1 parent 380e6ff commit 0bcf276

File tree

3 files changed

+83
-41
lines changed

3 files changed

+83
-41
lines changed

public/index.html

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22
<html lang="en">
33

44
<head>
5-
<meta charset="utf-8" />
6-
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
7-
<meta name="viewport" content="width=device-width, initial-scale=1" />
8-
<meta name="theme-color" content="#000000" />
9-
<meta name="description"
10-
content="Topcoder is home to the world’s largest community of designers, developers, and data scientists. Allowing you the freedom to start and execute faster." />
11-
<link rel="preconnect" href="https://fonts.googleapis.com" />
12-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13-
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Barlow+Condensed:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" />
14-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Barlow+Condensed:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" />
15-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo_512x512.png" />
16-
<link rel="stylesheet" href="%PUBLIC_URL%/global.css">
17-
<!--
5+
<meta charset="utf-8" />
6+
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1" />
8+
<meta name="theme-color" content="#000000" />
9+
<meta name="description"
10+
content="Topcoder is home to the world’s largest community of designers, developers, and data scientists. Allowing you the freedom to start and execute faster." />
11+
<link rel="preconnect" href="https://fonts.googleapis.com" />
12+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13+
<link rel="preload" as="style"
14+
href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Barlow+Condensed:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" />
15+
<link rel="stylesheet"
16+
href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Barlow+Condensed:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,600;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" />
17+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo_512x512.png" />
18+
<link rel="stylesheet" href="%PUBLIC_URL%/global.css">
19+
<!--
1820
manifest.json provides metadata used when your web app is installed on a
1921
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
2022
-->
21-
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
22-
<!--
23+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
24+
<!--
2325
Notice the use of %PUBLIC_URL% in the tags above.
2426
It will be replaced with the URL of the `public` folder during the build.
2527
Only files inside the `public` folder can be referenced from the HTML.
@@ -28,13 +30,13 @@
2830
work correctly both with client-side routing and a non-root public URL.
2931
Learn how to configure a non-root public URL by running `npm run build`.
3032
-->
31-
<title>Topcoder Top Technology Talent On-Demand</title>
33+
<title>Topcoder Top Technology Talent On-Demand</title>
3234
</head>
3335

3436
<body>
35-
<noscript>You need to enable JavaScript to run this app.</noscript>
36-
<div id="root" data-id="root"></div>
37-
<!--
37+
<noscript>You need to enable JavaScript to run this app.</noscript>
38+
<div id="root" data-id="root"></div>
39+
<!--
3840
This HTML file is a template.
3941
If you open it directly in the browser, you will see an empty page.
4042
@@ -44,6 +46,19 @@
4446
To begin the development, run `npm start` or `yarn start`.
4547
To create a production bundle, use `npm run build` or `yarn build`.
4648
-->
49+
50+
<script>
51+
(function (l, e, a, p) {
52+
if (window.Sprig) return;
53+
window.Sprig = function () { S._queue.push(arguments) }
54+
var S = window.Sprig; S.appId = a; S._queue = []; window.UserLeap = S;
55+
a = l.createElement('script');
56+
a.async = 1; a.src = e + '?id=' + S.appId;
57+
p = l.getElementsByTagName('script')[0];
58+
p.parentNode.insertBefore(a, p);
59+
})(document, 'https://cdn.sprig.com/shim.js', 'bUcousVQ0-yF');
60+
</script>
61+
4762
</body>
4863

4964
</html>

src-ts/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { routeContext, RouteContextData } from './lib'
77

88
const App: FC<{}> = () => {
99

10-
const [ready, setReady]: [boolean, Dispatch<SetStateAction<boolean>>] = useState(false)
10+
const [ready, setReady]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
1111
const { allRoutes, getRouteElement }: RouteContextData = useContext(routeContext)
1212

1313
const routeElements: Array<ReactElement> = allRoutes
@@ -19,9 +19,9 @@ const App: FC<{}> = () => {
1919

2020
useEffect(() => {
2121
if (ready) {
22-
document.getElementById('root')?.classList.add('app-ready');
22+
document.getElementById('root')?.classList.add('app-ready')
2323
}
24-
}, [ready]);
24+
}, [ready])
2525

2626
return (
2727
<>

src-ts/tools/learn/free-code-camp/FreeCodeCamp.tsx

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ import {
1717
LoadingSpinner,
1818
profileContext,
1919
ProfileContextData,
20+
textFormatGetSafeString,
2021
} from '../../../lib'
2122
import {
2223
CoursesProviderData,
2324
LearnLesson,
2425
LearnModule,
2526
LearnModuleProgress,
27+
LearnModuleStatus,
28+
LearnUserCertificationProgress,
2629
LessonProviderData,
2730
useGetCourses,
2831
useGetLesson,
@@ -52,11 +55,14 @@ const FreeCodeCamp: FC<{}> = () => {
5255

5356
const navigate: NavigateFunction = useNavigate()
5457
const routeParams: Params<string> = useParams()
55-
const providerParam: string = routeParams.provider ?? ''
58+
const providerParam: string = textFormatGetSafeString(routeParams.provider)
5659

57-
const [certificationParam, setCourseParam]: [string, Dispatch<SetStateAction<string>>] = useState(routeParams.certification ?? '')
58-
const [moduleParam, setModuleParam]: [string, Dispatch<SetStateAction<string>>] = useState(routeParams.module ?? '')
59-
const [lessonParam, setLessonParam]: [string, Dispatch<SetStateAction<string>>] = useState(routeParams.lesson ?? '')
60+
const [certificationParam, setCourseParam]: [string, Dispatch<SetStateAction<string>>]
61+
= useState(textFormatGetSafeString(routeParams.certification))
62+
const [moduleParam, setModuleParam]: [string, Dispatch<SetStateAction<string>>]
63+
= useState(textFormatGetSafeString(routeParams.module))
64+
const [lessonParam, setLessonParam]: [string, Dispatch<SetStateAction<string>>]
65+
= useState(textFormatGetSafeString(routeParams.lesson))
6066

6167
const {
6268
certificationProgress: certificateProgress,
@@ -83,11 +89,11 @@ const FreeCodeCamp: FC<{}> = () => {
8389

8490
const ready: boolean = profileReady && courseDataReady && lessonReady && (!isLoggedIn || progressReady)
8591

86-
const certification: string = lesson?.course.certification ?? ''
87-
const module: string = lesson?.module.title ?? ''
92+
const certification: string = textFormatGetSafeString(lesson?.course.certification)
93+
const module: string = textFormatGetSafeString(lesson?.module.title)
8894
const breadcrumb: Array<BreadcrumbItemModel> = useLearnBreadcrumb([
8995
{
90-
name: lesson?.course.title ?? '',
96+
name: textFormatGetSafeString(lesson?.course.title),
9197
url: getCoursePath(providerParam, certification),
9298
},
9399
{
@@ -96,7 +102,8 @@ const FreeCodeCamp: FC<{}> = () => {
96102
},
97103
])
98104

99-
const currentModuleData: LearnModule | undefined = useMemo(() => courseData?.modules.find(d => d.key === moduleParam), [courseData, moduleParam])
105+
const currentModuleData: LearnModule | undefined
106+
= useMemo(() => courseData?.modules.find(d => d.key === moduleParam), [courseData, moduleParam])
100107

101108
const currentStepIndex: number = useMemo(() => {
102109
if (!currentModuleData) {
@@ -155,7 +162,7 @@ const FreeCodeCamp: FC<{}> = () => {
155162
}
156163
}
157164

158-
function handleFccLessonReady(lessonPath: string): void {
165+
const handleFccLessonReady: (lessonPath: string) => void = useCallback((lessonPath: string) => {
159166

160167
const [nLessonPath, modulePath, coursePath]: Array<string> = lessonPath.replace(/\/$/, '')
161168
.split('/')
@@ -195,9 +202,10 @@ const FreeCodeCamp: FC<{}> = () => {
195202
.then(setCertificateProgress)
196203
}, 500)
197204
}
198-
}
205+
// eslint-disable-next-line react-hooks/exhaustive-deps
206+
}, [])
199207

200-
function handleFccLessonComplete(challengeUuid: string): void {
208+
const handleFccLessonComplete: (challengeUuid: string) => void = useCallback((challengeUuid: string) => {
201209
const currentLesson: { [key: string]: string } = {
202210
lesson: lessonParam,
203211
module: moduleParam,
@@ -209,15 +217,29 @@ const FreeCodeCamp: FC<{}> = () => {
209217
UserCertificationUpdateProgressActions.completeLesson,
210218
currentLesson,
211219
)
212-
.then(setCertificateProgress)
220+
.then((progress: LearnUserCertificationProgress) => {
221+
222+
setCertificateProgress(progress)
223+
224+
// if this is the last lesson of the first module, show the survey
225+
const firstModule: LearnModuleProgress = progress.modules[0]
226+
227+
if (moduleParam === firstModule.module
228+
&& firstModule.moduleStatus === LearnModuleStatus.completed) {
229+
230+
// TODO: use the Sprig SDK to send the event w/the user
231+
window.Sprig('track', 'TCA First Module Completed')
232+
}
233+
})
213234
}
214-
}
235+
// eslint-disable-next-line react-hooks/exhaustive-deps
236+
}, [])
215237

216238
/**
217239
* Handle the navigation away from the last step of the course in the FCC frame
218240
* @returns
219241
*/
220-
function handleFccLastLessonNavigation(): void {
242+
const handleFccLastLessonNavigation: () => void = useCallback(() => {
221243
if (!certificateProgress) {
222244
return
223245
}
@@ -236,15 +258,18 @@ const FreeCodeCamp: FC<{}> = () => {
236258
// course is not completed yet,
237259
// so we find the first incomplete lesson
238260
// and redirect user to it for a continuous flow
239-
const firstIncompleteModule: LearnModuleProgress | undefined = certificateProgress.modules.find(m => m.completedPercentage !== 100)
240-
const moduleLessons: Array<LearnLesson> | undefined = courseData?.modules.find(m => m.key === firstIncompleteModule?.module)?.lessons
261+
const firstIncompleteModule: LearnModuleProgress | undefined
262+
= certificateProgress.modules.find(m => m.completedPercentage !== 100)
263+
const moduleLessons: Array<LearnLesson> | undefined
264+
= courseData?.modules.find(m => m.key === firstIncompleteModule?.module)?.lessons
241265
if (!firstIncompleteModule || !moduleLessons) {
242266
// case unknown, return
243267
return
244268
}
245269

246270
const completedLessons: Array<string> = firstIncompleteModule.completedLessons.map(l => l.dashedName)
247-
const firstIncompleteLesson: LearnLesson | undefined = moduleLessons.find(l => !completedLessons.includes(l.dashedName))
271+
const firstIncompleteLesson: LearnLesson | undefined
272+
= moduleLessons.find(l => !completedLessons.includes(l.dashedName))
248273
if (!firstIncompleteLesson) {
249274
// case unknown, return
250275
return
@@ -258,7 +283,8 @@ const FreeCodeCamp: FC<{}> = () => {
258283
)
259284

260285
navigate(nextLessonPath)
261-
}
286+
// eslint-disable-next-line react-hooks/exhaustive-deps
287+
}, [])
262288

263289
useEffect(() => {
264290

@@ -300,7 +326,8 @@ const FreeCodeCamp: FC<{}> = () => {
300326

301327
useEffect(() => {
302328
if (courseDataReady && courseData) {
303-
const moduleParamData: LearnModule = courseData.modules.find(m => m.key === moduleParam) ?? courseData.modules[0]
329+
const moduleParamData: LearnModule = courseData.modules.find(m => m.key === moduleParam)
330+
?? courseData.modules[0]
304331
const lessonParamExists: boolean = !!moduleParamData?.lessons.find(l => l.dashedName === lessonParam)
305332

306333
if (!lessonParamExists) {

0 commit comments

Comments
 (0)