12
12
<div class =" flex-l items-start bt border-aqua bw4" >
13
13
<section class =" pv3 indent-1" >
14
14
<h1 class =" f3 measure-wide" >{{lessonTitle}}</h1 >
15
- <span class =" green" ><span class =" b" >{{workshopShortname}}</span > | Lesson {{lessonNumber}} of {{lessonsInWorkshop}}</span >
15
+ <div class =" lh-solid v-mid" >
16
+ <span class =" green v-mid" ><span class =" b" >{{workshopShortname}}</span > | Lesson {{lessonNumber}} of {{lessonsInWorkshop}}</span >
17
+ <span class =" pl1" ><img v-if =" lessonPassed" src =" ./home/complete.svg" alt =" complete" style =" height : 1.2rem ;" class =" v-mid" /></span >
18
+ </div >
16
19
<div class =" lesson-text lh-copy measure-wide" v-html =" parsedText" ></div >
17
20
</section >
18
21
<section v-if =" concepts" class =' dn db-ns ba border-green ph4 ml3 ml5-l mt5 mb3 mr3 measure' style =" background : rgba (105 , 196 , 205 , 10% )" >
24
27
<section v-bind:class =" {expand: expandExercise}" class =" indent-1 exercise pb4 pt3 ph3 ph4-l mb3 mr5 flex flex-column" style =" background : #F6F7F9 ;" >
25
28
<div class =" flex-none" >
26
29
<h2 class =" mt0 mb2 green fw4 fill-current" >
27
- <svg viewBox =" 0 0 12 12" width =' 12' xmlns =" http://www.w3.org/2000/svg" style =' vertical-align :-1px ' >
28
- <circle cx =" 6" cy =" 6" r =" 6" />
29
- </svg >
30
- <span class =" green ttu f6 pl2 pr3" >Exercise</span >
31
- <span class =" navy fw5 f5" >{{lessonTitle}}</span >
30
+ <span style =' vertical-align :-1px ' >
31
+ <img v-if =" lessonPassed" src =" ./home/complete.svg" alt =" complete" style =" height : 1rem ;" />
32
+ <img v-else-if =" cachedCode" src =" ./home/in-progress.svg" alt =" complete" style =" height : 1rem ;" />
33
+ <img v-else src =" ./home/not-started.svg" alt =" not yet started" style =" height : 1rem ;" />
34
+ </span >
35
+ <span class =" green ttu f6 pl2 pr1 fw7 v-mid" >
36
+ <span v-if =" lessonPassed" >You did it!</span >
37
+ <span v-else-if =" cachedCode" >Keep working.</span >
38
+ <span v-else >Try it!</span >
39
+ </span >
40
+ <span class =" green f6 fw5 v-mid" >
41
+ <span v-if =" cachedCode && !lessonPassed" >{{cachedStateMsg}}</span >
42
+ </span >
32
43
<div class =" fr" >
33
44
<button
34
45
v-if =" expandExercise"
44
55
class =' b--transparent bg-transparent charcoal-muted hover-green pointer focus-outline' >
45
56
<svg xmlns =" http://www.w3.org/2000/svg" width =" 16" height =" 16" fill =" currentColor" viewBox =" 0 0 32 32" ><path d =" M16 4 L28 4 L28 16 L24 12 L20 16 L16 12 L20 8z M4 16 L8 20 L12 16 L16 20 L12 24 L16 28 L4 28z" ></path ></svg >
46
57
</button >
58
+
47
59
</div >
48
60
</h2 >
49
61
<div v-if =" exercise" v-html =" parsedExercise" class =' lh-copy' ></div >
50
62
</div >
63
+ <div >
64
+ <span v-if =" cachedCode" v-on:click =" resetCode" class =" textLink fr pb1" >Reset Code</span >
65
+ </div >
51
66
<div class =" bg-white flex-auto" style =' height :100% ;' >
52
67
<MonacoEditor
53
68
class =" editor"
63
78
</div >
64
79
<div class =' flex-none' >
65
80
<div class =" pv2" >
66
- <div v-if =" output.test" v-bind =" output.test" >
81
+ <div v-if =" output.test && this.cachedCode " v-bind =" output.test" >
67
82
<div class =" lh-copy pv2 ph3 bg-red white" v-if =" output.test.error" >
68
83
Error: {{output.test.error.message}}
69
84
</div >
70
85
<div class =" lh-copy pv2 ph3 bg-red white" v-if =" output.test.fail" >
71
86
{{output.test.fail}}
72
87
</div >
73
- <div class =" lh-copy pv2 ph3 bg-green white" v-if =" output.test.success" >
88
+ <div class =" lh-copy pv2 ph3 bg-green white" v-if =" output.test.success && lessonPassed " >
74
89
{{output.test.success}}
75
90
<a v-if =" output.test.cid"
76
91
class =" link fw7 underline-hover dib ph2 mh2 white" target =' explore-ipld' :href =' exploreIpldUrl' >
79
94
</div >
80
95
</div >
81
96
<div class =" lh-copy pv2 ph3" v-else >
82
- Update the code to complete the exercise. Click <strong >submit</strong > to check your answer.
97
+ Update the code to complete the exercise. Click <strong >submit</strong > to check your answer.
83
98
</div >
84
99
</div >
85
100
<div class =" pt3 ph2 tr" >
86
- <div v-if =" output.test && output.test.success && lessonNumber === lessonsInWorkshop" >
101
+ <div v-if =" (( output.test && output.test.success) || lessonPassed) && lessonNumber === lessonsInWorkshop" >
87
102
<Button v-bind:click =" workshopMenu" class =" bg-aqua white" >More Workshops</Button >
88
103
</div >
89
- <div v-else-if =" output.test && output.test.success " >
104
+ <div v-else-if =" lessonPassed " >
90
105
<Button v-bind:click =" next" class =" bg-aqua white" >Next</Button >
91
106
</div >
92
107
<div v-else >
@@ -173,10 +188,15 @@ export default {
173
188
text: self .$attrs .text ,
174
189
exercise: self .$attrs .exercise ,
175
190
concepts: self .$attrs .concepts ,
176
- code: self .$attrs .code || defaultCode,
191
+ cachedCode: !! localStorage[' cached' + self .$route .path ],
192
+ code: localStorage[self .cacheKey ] || self .$attrs .code || defaultCode,
177
193
parsedText: marked (self .$attrs .text ),
178
194
parsedExercise: marked (self .$attrs .exercise || ' ' ),
179
195
parsedConcepts: marked (self .$attrs .concepts || ' ' ),
196
+ cacheKey: ' cached' + self .$route .path ,
197
+ cachedStateMsg: " " ,
198
+ lessonKey: ' passed' + self .$route .path ,
199
+ lessonPassed: !! localStorage[' passed' + self .$route .path ],
180
200
lessonTitle: self .$attrs .lessonTitle ,
181
201
issueUrl: ` https://github.com/ipfs-shipyard/proto.school/issues/new?labels=question&title=Question+on+Lesson+${ self .$route .path .slice (self .$route .path .lastIndexOf (' /' ) + 1 )} :+${ self .$attrs .lessonTitle } +(${ self .$route .path } )&body=Have%20a%20question%20or%20suggestion%20regarding%20a%20ProtoSchool%20lesson%3F%20Please%20use%20this%0Atemplate%20to%20share%20it!%0A%0A1.%20URL%20of%20the%20lesson%20that's%20confusing%3A%0A%20https%3A%2F%2Fproto.school%2F%23${ self .$route .path } %0A%0A2.%20What%27s%20confusing%20about%20this%20lesson%3F%0A%0A3.%20What%20additional%20context%20could%20we%20provide%20to%20help%20you%20succeed%3F%0A%0A4.%20What%20other%20feedback%20would%20you%20like%20to%20share%20about%20ProtoSchool%3F%0A` ,
182
202
output: self .output ,
@@ -191,6 +211,7 @@ export default {
191
211
}
192
212
},
193
213
computed: {
214
+
194
215
exploreIpldUrl : function () {
195
216
let cid = this .output .test && this .output .test .cid && this .output .test .cid .toBaseEncodedString ()
196
217
cid = cid || ' '
@@ -225,6 +246,13 @@ export default {
225
246
},
226
247
beforeCreate : function () {
227
248
this .output = {}
249
+ // doesn't work to set lessonPassed in here because it can't recognize lessonKey yet
250
+ },
251
+ updated : function () {
252
+ // runs on page load AND every keystroke in editor AND submit
253
+ },
254
+ beforeUpdate : function () {
255
+ // runs on every keystroke in editor, NOT on page load, NOT on code submit
228
256
},
229
257
methods: {
230
258
run : async function () {
@@ -238,8 +266,10 @@ export default {
238
266
let modules = {}
239
267
if (this .$attrs .modules ) modules = this .$attrs .modules
240
268
let result = await _eval (code, ipfs, modules)
269
+
241
270
if (result && result .error ) {
242
271
Vue .set (output, ' test' , result)
272
+ this .lessonPassed = !! localStorage[this .lessonKey ]
243
273
return
244
274
}
245
275
let test = await this .$attrs .validate (result, ipfs)
@@ -250,6 +280,10 @@ export default {
250
280
} else {
251
281
ipfs .stop ()
252
282
}
283
+ if (output .test .success ) {
284
+ localStorage[this .lessonKey ] = ' passed'
285
+ }
286
+ this .lessonPassed = !! localStorage[this .lessonKey ]
253
287
},
254
288
createIPFS : function () {
255
289
if (this .$attrs .createIPFS ) {
@@ -258,11 +292,48 @@ export default {
258
292
return new IPFS ({repo: Math .random ().toString ()})
259
293
}
260
294
},
295
+ resetCode : function () {
296
+ // TRACK? User chose to reset code
297
+ this .code = this .$attrs .code || defaultCode
298
+ // this ^ triggers onCodeChange which will clear cache
299
+ this .editor .setValue (this .code )
300
+ this .clearPassed ()
301
+ },
302
+ clearPassed : function () {
303
+ delete localStorage[this .lessonKey ]
304
+ this .lessonPassed = !! localStorage[this .lessonKey ]
305
+ },
306
+ loadCodeFromCache : function () {
307
+ this .code = localStorage[this .cacheKey ]
308
+ this .editor .setValue (this .code )
309
+ },
261
310
onMounted : function (editor ) {
311
+ // runs on page load, NOT on every keystroke in editor
262
312
this .editor = editor
313
+ if (this .cachedCode ) {
314
+ // TRACK? returned to lesson previously visited
315
+ this .loadCodeFromCache ()
316
+ this .cachedStateMsg = " Pick up where you left off. We've saved your code for you!"
317
+ if (this .lessonPassed ) {
318
+ this .run ()
319
+ }
320
+ } else {
321
+ // TRACK? first time starting lesson
322
+ }
263
323
},
264
- onCodeChange : function (editor ) {
265
- // console.log(editor.getValue())
324
+ onCodeChange : function () {
325
+ if (this .editor .getValue () === (this .$attrs .code || defaultCode) ) {
326
+ // TRACK? edited back to default state by chance or by 'reset code'
327
+ delete localStorage[this .cacheKey ]
328
+ this .cachedCode = !! localStorage[this .cacheKey ]
329
+ } else if (this .code === this .editor .getValue ()) {
330
+ // TRACK? returned to cached lesson in progress
331
+ } else {
332
+ localStorage[this .cacheKey ] = this .editor .getValue ()
333
+ this .code = this .editor .getValue ()
334
+ this .cachedCode = !! localStorage[this .cacheKey ]
335
+ this .cachedStateMsg = " We're saving your code as you go."
336
+ }
266
337
},
267
338
next : function () {
268
339
Vue .set (this .output , ' test' , null )
@@ -307,6 +378,12 @@ export default {
307
378
.mw-900 {
308
379
max-width : 900px ;
309
380
}
381
+ span .textLink {
382
+ color : blue ;
383
+ cursor : pointer ;
384
+ text-decoration : underline ;
385
+ }
386
+
310
387
@media screen and (min-width : 60rem ) {
311
388
.indent-1 {
312
389
margin-left : 93px ;
0 commit comments