51
51
</div >
52
52
</h2 >
53
53
<div v-if =" exercise" v-html =" parsedExercise" class =' lh-copy' ></div >
54
+ <div v-if =" isFileLesson" >
55
+ <div class =" f5 fw7 mt4 mb2" > Step 1: Upload file(s)
56
+ <span class =" pl1" ><img v-if =" uploadedFiles" src =" ../static/images/complete.svg" alt =" complete" style =" height : 1.2rem ;" class =" v-mid" /></span >
57
+ </div >
58
+ <div id =" drop-area" v-if =" !uploadedFiles" v-on:drop =" onFileDrop"
59
+ v-on:click =" onFileClick"
60
+ @dragenter =" dragging=true" @dragend =" dragging=false" @dragleave =" dragging=false"
61
+ @dragover.prevent v-bind:class =" {dragging: dragging}" class =" dropfile mb2 pa2 w-100 br3 shadow-4 bg-white color-navy" >
62
+ <div class =" o-80 glow" >
63
+ <label for =" add-files" class =" flex items-center h4 pointer" >
64
+ <svg viewBox =" 0 0 100 100" class =" fill-aqua" height =" 60px" alt =" Add" ><path d =" M71.13 28.87a29.88 29.88 0 1 0 0 42.26 29.86 29.86 0 0 0 0-42.26zm-18.39 37.6h-5.48V52.74H33.53v-5.48h13.73V33.53h5.48v13.73h13.73v5.48H52.74z" ></path ></svg >
65
+ <div class =" f5 charcoal" >
66
+ <p ><strong >Drop one or more files here or click to select.</strong > Folder upload is not supported, but you may select multiple files using Ctrl+Click or Command+Click.</p >
67
+ </div >
68
+ </label >
69
+ </div >
70
+ </div >
71
+ <div v-else class =" mt2" >
72
+ <span v-on:click =" resetFileUpload" class =" textLink fr pb1" >Start Over</span >
73
+ <div class =" mb2 pl3 pa2 w-100 br3 h4 shadow-4 bg-white color-navy flex items-center" >
74
+ <svg xmlns =" http://www.w3.org/2000/svg" viewBox =" 0 0 100 100" class =" fill-aqua" height =" 60px" ><path d =" M55.94 19.17H30a4 4 0 0 0-4 4v53.65a4 4 0 0 0 4 4h40.1a4 4 0 0 0 4-4V38.06zm5.28 21.08c-4.33 0-7.47-2.85-7.47-6.77V21l18.13 19.25z" /></svg >
75
+ <ul class =" list pl0" >
76
+ <li v-for =" (file, idx) in uploadedFiles" :key =" `file-${idx}`" >{{file.name}}</li >
77
+ </ul >
78
+ </div >
79
+ </div >
80
+ <div class =" f5 fw7 mt4 mb2" >Step 2: Update code
81
+ <span class =" pl1" ><img v-if =" cachedCode" src =" ../static/images/complete.svg" alt =" complete" style =" height : 1.2rem ;" class =" v-mid" /></span >
82
+ </div >
83
+ </div >
54
84
</div >
55
85
<div >
56
86
<span v-if =" cachedCode" v-on:click =" resetCode" class =" textLink fr pb1" >Reset Code</span >
86
116
</div >
87
117
</div >
88
118
<div class =" lh-copy pv2 ph3" v-else >
89
- Update the code to complete the exercise. Click <strong >submit</strong > to check your answer.
119
+ <div v-if =" isFileLesson" >
120
+ Upload file(s) and update the code to complete the exercise. Click <strong >Submit</strong > to check your answer.
121
+ </div >
122
+ <div v-else >
123
+ Update the code to complete the exercise. Click <strong >Submit</strong > to check your answer.
124
+ </div >
90
125
</div >
91
126
</div >
92
127
<div class =" pt3 ph2 tr" >
97
132
<Button v-bind:click =" next" class =" bg-aqua white" >Next</Button >
98
133
</div >
99
134
<div v-else >
100
- <Button v-bind:click =" run" class =" bg-aqua white" >Submit</Button >
135
+ <span v-if =" isFileLesson && !uploadedFiles" class =" disabledButtonWrapper" ><Button v-bind:click =" next" class =" bg-aqua white" disabled >Submit</Button ></span >
136
+ <Button v-else v-bind:click =" run" class =" bg-aqua white" >Submit</Button >
137
+ <div v-if =" isFileLesson && !uploadedFiles" class =" red lh-copy pt2 o-0" >
138
+ You must upload a file before submitting.
139
+ </div >
101
140
</div >
102
141
</div >
103
142
</div >
@@ -129,8 +168,8 @@ import MonacoEditor from 'vue-monaco-editor'
129
168
import Explorer from ' ./Explorer.vue'
130
169
import Button from ' ./Button.vue'
131
170
import Header from ' ./Header.vue'
132
- const CID = require ( ' cids' )
133
- const marked = require ( ' marked' )
171
+ import CID from ' cids'
172
+ import marked from ' marked'
134
173
135
174
const hljs = require (' highlight.js/lib/highlight.js' )
136
175
hljs .registerLanguage (' js' , require (' highlight.js/lib/languages/javascript' ))
@@ -143,7 +182,7 @@ marked.setOptions({
143
182
}
144
183
})
145
184
146
- const _eval = async (text , ipfs , modules = {}) => {
185
+ const _eval = async (text , ipfs , modules = {}, args = [] ) => {
147
186
await new Promise (resolve => ipfs .on (' ready' , resolve))
148
187
149
188
let fn
@@ -161,7 +200,7 @@ const _eval = async (text, ipfs, modules = {}) => {
161
200
return modules[name]
162
201
}
163
202
try {
164
- result = await fn (ipfs, require)()
203
+ result = await fn (ipfs, require)(... args )
165
204
} catch (e) {
166
205
result = {error: e}
167
206
}
@@ -193,7 +232,9 @@ export default {
193
232
exercise: self .$attrs .exercise ,
194
233
concepts: self .$attrs .concepts ,
195
234
cachedCode: !! localStorage[' cached' + self .$route .path ],
196
- code: localStorage[self .cacheKey ] || self .$attrs .code || defaultCode,
235
+ code: localStorage[self .cacheKey ] || self .$attrs .code || self .defaultCode ,
236
+ overrideErrors: self .$attrs .overrideErrors ,
237
+ isFileLesson: self .isFileLesson ,
197
238
parsedText: marked (self .$attrs .text ),
198
239
parsedExercise: marked (self .$attrs .exercise || ' ' ),
199
240
parsedConcepts: marked (self .$attrs .concepts || ' ' ),
@@ -204,6 +245,8 @@ export default {
204
245
lessonTitle: self .$attrs .lessonTitle ,
205
246
output: self .output ,
206
247
expandExercise: false ,
248
+ dragging: false ,
249
+ uploadedFiles: window .uploadedFiles || false ,
207
250
options: {
208
251
selectOnLineNumbers: false ,
209
252
lineNumbersMinChars: 3 ,
@@ -259,7 +302,8 @@ export default {
259
302
},
260
303
beforeCreate : function () {
261
304
this .output = {}
262
- this .IPFSPromise = import (' ipfs' )
305
+ this .defaultCode = defaultCode
306
+ this .IPFSPromise = import (' ipfs' ).then (m => m .default )
263
307
// doesn't work to set lessonPassed in here because it can't recognize lessonKey yet
264
308
},
265
309
updated : function () {
@@ -269,7 +313,7 @@ export default {
269
313
// runs on every keystroke in editor, NOT on page load, NOT on code submit
270
314
},
271
315
methods: {
272
- run : async function () {
316
+ run : async function (... args ) {
273
317
if (oldIPFS) {
274
318
oldIPFS .stop ()
275
319
oldIPFS = null
@@ -279,14 +323,16 @@ export default {
279
323
let code = this .editor .getValue ()
280
324
let modules = {}
281
325
if (this .$attrs .modules ) modules = this .$attrs .modules
282
- let result = await _eval (code, ipfs, modules)
283
-
284
- if (result && result .error ) {
326
+ if (this .isFileLesson ) args .unshift (this .uploadedFiles )
327
+ // Output external errors or not depending on flag
328
+ let result = await _eval (code, ipfs, modules, args)
329
+ if (! this .$attrs .overrideErrors && result && result .error ) {
285
330
Vue .set (output, ' test' , result)
286
331
this .lessonPassed = !! localStorage[this .lessonKey ]
287
332
return
288
333
}
289
- let test = await this .$attrs .validate (result, ipfs)
334
+ // Run the `validate` function in the lesson
335
+ let test = await this .$attrs .validate (result, ipfs, args)
290
336
Vue .set (output, ' test' , test)
291
337
if (CID .isCID (result)) {
292
338
oldIPFS = ipfs
@@ -304,7 +350,7 @@ export default {
304
350
return this .$attrs .createIPFS ()
305
351
} else {
306
352
let ipfs = this .IPFSPromise .then (IPFS => {
307
- return IPFS . createNode ({repo: Math .random ().toString ()})
353
+ return new IPFS ({repo: Math .random ().toString ()})
308
354
})
309
355
return ipfs
310
356
}
@@ -316,6 +362,11 @@ export default {
316
362
this .editor .setValue (this .code )
317
363
this .clearPassed ()
318
364
},
365
+ resetFileUpload : function () {
366
+ this .uploadedFiles = false
367
+ this .dragging = false
368
+ console .log ({uploadedFiles: this .uploadedFiles })
369
+ },
319
370
clearPassed : function () {
320
371
delete localStorage[this .lessonKey ]
321
372
this .lessonPassed = !! localStorage[this .lessonKey ]
@@ -380,6 +431,18 @@ export default {
380
431
</script >
381
432
382
433
<style scoped>
434
+
435
+ button :disabled {
436
+ cursor : not-allowed ;
437
+ }
438
+
439
+ .disabledButtonWrapper :hover + div {
440
+ opacity : 1 ;
441
+ transition : opacity .2s ease-in ;
442
+ }
443
+ .dragging {
444
+ border : 5px solid #69c4cd ;
445
+ }
383
446
.editor {
384
447
height : 100% ;
385
448
min-height : 15rem ;
@@ -420,4 +483,14 @@ footer a {
420
483
margin-left : 93px ;
421
484
}
422
485
}
486
+
487
+ div .dropfile {
488
+ cursor : pointer ;
489
+ }
490
+ div .dropfile input {
491
+ display : none ;
492
+ }
493
+ div #drop-area * {
494
+ pointer-events : none ;
495
+ }
423
496
</style >
0 commit comments