55 <section class =" mw7 center mt3 pa3" >
66 <div class =" flex flex-row justify-between green" >
77 <Breadcrumbs
8- :isResources =" isResources"
9- :tutorialShortname = " tutorialShortname "
10- :lessonNumber =" lessonNumber "
11- :lessonsInTutorial =" lessonsInTutorial"
12- :lessonPassed =" lessonPassed" />
8+ :isResources =" isResources"
9+ :tutorial = " tutorial "
10+ :lessonNumber =" lessonId "
11+ :lessonsInTutorial =" lessonsInTutorial"
12+ :lessonPassed =" lessonPassed" />
1313 <TypeIcon
1414 :lessonId =" isResources? 'resources' : lessonId"
15- :tutorialId =" tutorialId "
15+ :tutorialId =" tutorial.formattedId "
1616 class =" h2 ml3" />
17- </div >
17+ </div >
1818 <CongratulationsCallout
1919 v-if =" isResources && isTutorialPassed"
20- :tutorialId =" tutorialId"
2120 :tutorial =" tutorial"
2221 class =" mv4"
2322 />
4746 v-if =" exercise"
4847 :isFileLesson =" isFileLesson"
4948 :editorReady =" editorReady"
50- :code =" code "
49+ :code =" editorCode "
5150 :solution =" solution"
5251 :cachedCode =" cachedCode"
5352 :onMounted =" onMounted"
8483 :output =" output.test"
8584 :isResources =" isResources"
8685 :nextLessonIsResources =" nextLessonIsResources"
87- :lessonNumber =" lessonNumber "
86+ :lessonNumber =" lessonId "
8887 :lessonsInTutorial =" lessonsInTutorial"
8988 :expandExercise =" expandExercise"
9089 :isSubmitting =" isSubmitting"
@@ -111,6 +110,8 @@ import marked from 'marked'
111110import pTimeout from ' p-timeout'
112111import newGithubIssueUrl from ' new-github-issue-url'
113112
113+ import { getTutorialByUrl , isTutorialPassed , getLesson } from ' ../utils/tutorials'
114+ import { EVENTS } from ' ../static/countly'
114115import Header from ' ./Header.vue'
115116import Quiz from ' ./Quiz.vue'
116117import Resources from ' ./Resources.vue'
@@ -123,9 +124,6 @@ import Output from './Output.vue'
123124import Info from ' ./Info.vue'
124125import Validator from ' ./Validator.vue'
125126import CongratulationsCallout from ' ./CongratulationsCallout.vue'
126- import { EVENTS } from ' ../static/countly'
127- import { deriveShortname } from ' ../utils/paths'
128- import { getTutorial , isTutorialPassed , getLesson } from ' ../utils/tutorials'
129127import TypeIcon from ' ./TypeIcon.vue'
130128
131129const MAX_EXEC_TIMEOUT = 5000
@@ -215,101 +213,98 @@ export default {
215213 CongratulationsCallout,
216214 TypeIcon
217215 },
216+ props: {
217+ lessonId: Number ,
218+ isResources: Boolean ,
219+ resources: Array ,
220+ text: String ,
221+ exercise: String ,
222+ concepts: String ,
223+ solution: String ,
224+ modules: Object ,
225+ validate: Function ,
226+ code: String ,
227+ overrideErrors: Boolean ,
228+ isMultipleChoiceLesson: Boolean ,
229+ question: String ,
230+ choices: Array ,
231+ createTestFile: Boolean ,
232+ createTestTree: Boolean
233+ },
218234 data : self => {
219- const tutorial = getTutorial (self .$attrs .tutorialId )
220- const resourcesLesson = {
221- title: ' Resources' ,
222- type: ' resources'
223- }
224- const lesson = self .$attrs .isResources ? resourcesLesson : getLesson (self .$attrs .tutorialId , self .$attrs .lessonId )
225-
226235 return {
227- lesson,
228- tutorial,
229- lessonId: self .$attrs .lessonId ,
230- tutorialId: self .$attrs .tutorialId ,
231- isResources: self .$attrs .isResources ,
232- resources: self .$attrs .resources ,
233- text: self .$attrs .text ,
234- exercise: self .$attrs .exercise ,
235- concepts: self .$attrs .concepts ,
236- cachedChoice: !! localStorage[' cached' + self .$route .path ],
237- choice: localStorage[self .cacheKey ] || ' ' ,
238- cachedCode: !! localStorage[' cached' + self .$route .path ],
239- code: localStorage[self .cacheKey ] || self .$attrs .code || self .defaultCode ,
240- solution: self .$attrs .solution ,
241236 isSubmitting: false ,
242- viewSolution: false ,
243- overrideErrors: self .$attrs .overrideErrors ,
244- isFileLesson: self .isFileLesson ,
245- isMultipleChoiceLesson: self .isMultipleChoiceLesson ,
246- question: self .$attrs .question ,
247- choices: self .$attrs .choices ,
248- parsedText: marked (self .$attrs .text || ' ' ),
249- parsedExercise: marked (self .$attrs .exercise || ' ' ),
250- parsedConcepts: marked (self .$attrs .concepts || ' ' ),
237+ lessonPassed: !! localStorage[' passed' + self .$route .path ],
238+ lessonKey: ' passed' + self .$route .path ,
251239 cacheKey: ' cached' + self .$route .path ,
240+ cachedCode: !! localStorage[' cached' + self .$route .path ],
241+ viewSolution: false ,
252242 cachedStateMsg: ' ' ,
253- tutorialPath: tutorial .url ,
254- tutorialShortname: deriveShortname (self .$route .path ),
255- isTutorialPassed: isTutorialPassed (tutorial),
256- lessonKey: ' passed' + self .$route .path ,
257- lessonPassed: !! localStorage[' passed' + self .$route .path ],
258- createTestFile: self .$attrs .createTestFile ,
259- createTestTree: self .$attrs .createTestTree ,
260- output: self .output ,
261243 showUploadInfo: false ,
262244 expandExercise: false ,
245+ editorReady: false ,
246+ isFileLesson: self .isFileLesson ,
263247 uploadedFiles: window .uploadedFiles || false ,
264- editorReady: false
248+ choice: localStorage[self .cacheKey ] || ' ' ,
249+ cachedChoice: !! localStorage[' cached' + self .$route .path ]
265250 }
266251 },
267252 computed: {
268- lessonNumber : function () {
269- return parseInt (this .lessonId , 10 )
253+ tutorial : function () {
254+ return getTutorialByUrl (this .$route .params .tutorialUrl )
255+ },
256+ isTutorialPassed : function () {
257+ return isTutorialPassed (this .tutorial )
258+ },
259+ parsedText : function () {
260+ return marked (this .text || ' ' )
261+ },
262+ parsedExercise : function () {
263+ return marked (this .exercise || ' ' )
264+ },
265+ parsedConcepts : function () {
266+ return marked (this .concepts || ' ' )
267+ },
268+ lesson : function () {
269+ return getLesson (this .tutorial .formattedId , this .lessonId )
270270 },
271271 lessonIssueUrl : function () {
272- return encodeURI (` https://github.com/ProtoSchool/protoschool.github.io/issues/new?assignees=&labels=lesson-feedback&template=lesson-feedback.md&title=Lesson+Feedback%3A+${ this .tutorialShortname } +-+Lesson+${ this .lessonNumber } +(${ this .lesson .title } )` )
272+ return encodeURI (` https://github.com/ProtoSchool/protoschool.github.io/issues/new?assignees=&labels=lesson-feedback&template=lesson-feedback.md&title=Lesson+Feedback%3A+${ this .tutorial . shortTitle } +-+Lesson+${ this .lessonId } +(${ this .lesson .title } )` )
273273 },
274274 tutorialIssueUrl : function () {
275- return encodeURI (` https://github.com/ProtoSchool/protoschool.github.io/issues/new?assignees=&labels=tutorial-feedback&template=tutorial-feedback.md&title=Tutorial+Feedback%3A+${ this .tutorialShortname } ` )
275+ return encodeURI (` https://github.com/ProtoSchool/protoschool.github.io/issues/new?assignees=&labels=tutorial-feedback&template=tutorial-feedback.md&title=Tutorial+Feedback%3A+${ this .tutorial . shortTitle } ` )
276276 },
277277 lessonsInTutorial : function () {
278- const basePath = this .$route .path .slice (0 , - 2 )
279- let number = this .$route .path .slice (- 2 )
280- while (this .$router .resolve (basePath + number).route .name !== ' 404' ) {
281- number++
282- number = number .toString ().padStart (2 , ' 0' )
283- }
284- return parseInt (number) - 1
278+ return this .tutorial .lessons .length
285279 },
286280 nextLessonIsResources : function () {
287281 const basePath = this .$route .path .slice (0 , - 2 )
288282 const hasResources = this .$router .resolve (basePath + ' resources' ).route .name !== ' 404'
289- return this .lessonNumber === this .lessonsInTutorial && hasResources
283+ return this .lessonId === this .lessonsInTutorial && hasResources
284+ },
285+ editorCode: {
286+ get : function () {
287+ return localStorage[this .cacheKey ] || this .code || this .defaultCode
288+ },
289+ set : function (newCode ) {
290+ localStorage[this .cacheKey ] = newCode
291+ }
290292 }
291293 },
292294 beforeCreate : function () {
293295 this .output = {}
294296 this .defaultCode = defaultCode
295297 this .IPFSPromise = import (' ipfs' ).then (m => m .default )
296- // doesn't work to set lessonPassed in here because it can't recognize lessonKey yet
297298 },
298299 beforeMount : function () {
299300 this .choice = localStorage[this .cacheKey ] || ' '
300301 },
301- // updated: function () {
302- // runs on page load AND every keystroke in editor AND submit
303- // },
304- // beforeUpdate: function () {
305- // runs on every keystroke in editor, NOT on page load, NOT on code submit
306- // },
307302 methods: {
308303 validationIssueUrl : function (code , validationTimeout ) {
309304 return newGithubIssueUrl ({
310305 user: ' ProtoSchool' ,
311306 repo: ' protoschool.github.io' ,
312- title: ` Validation Error: ${ this .tutorialShortname } - Lesson ${ this .lessonNumber } (${ this .lesson .title } )` ,
307+ title: ` Validation Error: ${ this .tutorial . shortTitle } - Lesson ${ this .lessonId } (${ this .lesson .title } )` ,
313308 labels: [' lesson-feedback' , ' validation-error' ],
314309 body: ` If you submitted code for a lesson and received feedback indicating a validation error, you may have uncovered a bug in our lesson validation code. We've prepopulated the error type and the last code you submitted below as diagnostic clues. Feel free to add additional feedback about the lesson below before clicking "Submit new issue."
315310
@@ -355,11 +350,11 @@ export default {
355350 this .showUploadInfo = false
356351 }
357352
358- if (this .$attrs . modules ) modules = this . $attrs .modules
353+ if (this .modules ) modules = this .modules
359354 if (this .isFileLesson ) args .unshift (this .uploadedFiles )
360355 // Output external errors or not depending on flag
361356 const result = await _eval (code, ipfs, modules, args)
362- if (! this .$attrs . overrideErrors && result instanceof Error ) {
357+ if (! this .overrideErrors && result instanceof Error ) {
363358 Vue .set (output, ' test' , result)
364359 this .lessonPassed = !! localStorage[this .lessonKey ]
365360 this .isSubmitting = false
@@ -376,8 +371,9 @@ export default {
376371
377372 // Run the `validate` function in the lesson
378373 try {
379- test = await this .$attrs .validate (result, ipfs, args)
380- } catch (err) {
374+ test = await this .validate (result, ipfs, args)
375+ } catch (error) {
376+ console .error (error)
381377 // Something in our validation threw an error, it's probably a bug
382378 test = {
383379 fail: ` You may have uncovered a bug in our validation code. Please help us improve this lesson by [**opening an issue**](${ this .validationIssueUrl (code, true )} ) noting that you encountered a validation timeout error. Then you can click **Reset Code** above the code editor, review the instructions, and try again. Still having trouble? Click **View Solution** below the code editor to see the approach we recommend for this challenge.`
@@ -457,9 +453,9 @@ export default {
457453 },
458454 resetCode : function () {
459455 // TRACK? User chose to reset code
460- this .code = this . $attrs .code || defaultCode
456+ this .editorCode = this .code || defaultCode
461457 // this ^ triggers onCodeChange which will clear cache
462- this .editor .setValue (this .code )
458+ this .editor .setValue (this .editorCode )
463459 this .clearPassed ()
464460 delete this .output .test
465461 this .showUploadInfo = false
@@ -472,31 +468,30 @@ export default {
472468 },
473469 clearPassed : function () {
474470 delete localStorage[this .lessonKey ]
475- this .lessonPassed = !! localStorage[this .lessonKey ]
476- delete localStorage[` passed/${ this .tutorialPath } ` ]
471+ delete localStorage[` passed/${ this .tutorial .url } ` ]
477472 },
478473 loadCodeFromCache : function () {
479- this .code = localStorage[this .cacheKey ]
480- this .editor .setValue (this .code )
474+ this .editorCode = localStorage[this .cacheKey ]
475+ this .editor .setValue (this .editorCode )
481476 },
482477 updateTutorialState : function () {
483478 for (let i = 1 ; i <= this .lessonsInTutorial ; i++ ) {
484479 const lessonNr = i .toString ().padStart (2 , 0 )
485- const lsKey = ` passed/${ this .tutorialPath } /${ lessonNr} `
480+ const lsKey = ` passed/${ this .tutorial . url } /${ lessonNr} `
486481 if (localStorage[lsKey] !== ' passed' ) {
487482 return false
488483 }
489484 }
490- localStorage[` passed/${ this .tutorialPath } ` ] = ' passed'
485+ localStorage[` passed/${ this .tutorial . url } ` ] = ' passed'
491486 this .trackEvent (EVENTS .TUTORIAL_PASSED )
492487 return true
493488 },
494489 trackEvent : function (event , opts = {}) {
495490 window .Countly .q .push ([' add_event' , {
496491 key: event ,
497492 segmentation: {
498- tutorial: this .tutorialShortname ,
499- lessonNumber: this .lessonNumber ,
493+ tutorial: this .tutorial . shortTitle ,
494+ lessonNumber: this .lessonId ,
500495 path: this .$route .path ,
501496 ... opts
502497 }
@@ -522,14 +517,13 @@ export default {
522517 // TRACK? edited back to default state by chance or by 'reset code'
523518 delete localStorage[this .cacheKey ]
524519 this .cachedCode = !! localStorage[this .cacheKey ]
525- } else if (this .code === this .editor .getValue ()) {
520+ } else if (this .editorCode === this .editor .getValue ()) {
526521 // TRACK? returned to cached lesson in progress
527522 } else {
528- localStorage[this .cacheKey ] = this .editor .getValue ()
529- this .code = this .editor .getValue ()
523+ this .editorCode = this .editor .getValue ()
530524 this .cachedCode = !! localStorage[this .cacheKey ]
531525 this .cachedStateMsg = " We're saving your code as you go."
532- if (this .code !== this .solution ) {
526+ if (this .editorCode !== this .solution ) {
533527 this .clearPassed ()
534528 delete this .output .test
535529 }
@@ -560,14 +554,13 @@ export default {
560554 Vue .set (this .output , ' test' , null )
561555 } else {
562556 localStorage[this .lessonKey ] = ' passed'
563- this .lessonPassed = !! localStorage[this .lessonKey ]
564557 // track passed lesson if text only
565558 if (! this .isMultipleChoiceLesson ) {
566559 this .trackEvent (EVENTS .LESSON_PASSED )
567560 this .updateTutorialState ()
568561 }
569562 }
570- const current = this .lessonNumber
563+ const current = this .lessonId
571564
572565 const next = this .nextLessonIsResources
573566 ? ' resources'
@@ -588,7 +581,7 @@ export default {
588581 this .expandExercise = ! this .expandExercise
589582 },
590583 cyReplaceWithSolution : function () {
591- this .editor .setValue (this .$attrs . solution )
584+ this .editor .setValue (this .solution )
592585 },
593586 parseData : (data ) => marked (data)
594587 }
0 commit comments