diff --git a/README.md b/README.md index b0c0c729b..2fbba6e10 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Select the appropriate boilerplate Vue file for your lesson from the `tutorials/ - `boilerplate-file-upload.vue` for a lesson with a coding exercise that requires a file upload - `boilerplate-no-exercise.vue` for a text-only lesson -Copy that boilerplate into your tutorial folder and rename it to the 2-digit number of the lesson. +Copy that boilerplate into your tutorial directory and rename it to the 2-digit number of the lesson. Example (while in `src/tutorials`): @@ -254,7 +254,7 @@ you need to override, as in this example: ```js } else if (result.error && result.error.message === 'No child name passed to addLink') { // Forgot the file name and just used a directory as the path - return { fail: 'Uh oh. It looks like you created a folder instead of a file. Did you forget to include a filename in your path?' } + return { fail: 'Uh oh. It looks like you created a directory instead of a file. Did you forget to include a filename in your path?' } } ``` Be sure to adapt your test case so that it works within the context of your other conditionals to meet your validation needs. What is required is that you return an object with the `fail` key and a string as its value; that string is what will be shown to the user. diff --git a/src/components/File-Lesson.vue b/src/components/File-Lesson.vue index ace4dcdc5..5adbb8952 100644 --- a/src/components/File-Lesson.vue +++ b/src/components/File-Lesson.vue @@ -25,7 +25,7 @@ export default { for (let f of Array.from(event.dataTransfer.items)) { let isFile = f.getAsEntry ? f.getAsEntry().isFile : (f.webkitGetAsEntry ? f.webkitGetAsEntry().isFile : true) if (!isFile) { - return alert("Folder upload is not supported. Please select a file or multiple files.") + return alert('Directory upload is not supported. Please select one or more files.') } } this.onFiles(files) @@ -35,7 +35,7 @@ export default { event.preventDefault() event.stopPropagation() let elem = document.createElement('input') - elem.setAttribute("type", "file") + elem.setAttribute('type', 'file') elem.setAttribute('multiple', true) elem.onchange = () => { this.onFiles(Array.from(elem.files)) diff --git a/src/components/Lesson.vue b/src/components/Lesson.vue index ff0471141..32e8d0979 100644 --- a/src/components/Lesson.vue +++ b/src/components/Lesson.vue @@ -62,14 +62,14 @@
Start Over -
+
  • {{file.name}}
  • @@ -151,8 +151,11 @@
- - + +
@@ -254,6 +257,7 @@ export default { cachedCode: !!localStorage['cached' + self.$route.path], code: localStorage[self.cacheKey] || self.$attrs.code || self.defaultCode, solution: self.$attrs.solution, + isSubmitting: false, viewSolution: false, overrideErrors: self.$attrs.overrideErrors, isFileLesson: self.isFileLesson, @@ -265,6 +269,7 @@ export default { lessonKey: 'passed' + self.$route.path, lessonPassed: !!localStorage['passed' + self.$route.path], lessonTitle: self.$attrs.lessonTitle, + createTestFile: self.$attrs.createTestFile, output: self.output, expandExercise: false, dragging: false, @@ -337,14 +342,17 @@ export default { }, methods: { run: async function (...args) { + this.isSubmitting = true if (oldIPFS) { oldIPFS.stop() oldIPFS = null } let output = this.output let ipfs = await this.createIPFS() + if (this.createTestFile) { + await this.createFile(ipfs) + } let code = this.editor.getValue() - let modules = {} if (this.$attrs.modules) modules = this.$attrs.modules if (this.isFileLesson) args.unshift(this.uploadedFiles) @@ -353,6 +361,7 @@ export default { if (!this.$attrs.overrideErrors && result && result.error) { Vue.set(output, 'test', result) this.lessonPassed = !!localStorage[this.lessonKey] + this.isSubmitting = false return } // Run the `validate` function in the lesson @@ -368,17 +377,27 @@ export default { localStorage[this.lessonKey] = 'passed' } this.lessonPassed = !!localStorage[this.lessonKey] + this.isSubmitting = false }, createIPFS: function () { if (this.$attrs.createIPFS) { return this.$attrs.createIPFS() } else { let ipfs = this.IPFSPromise.then(IPFS => { - return new IPFS({repo: Math.random().toString()}) + this.ipfsConstructor = IPFS + return new IPFS({ repo: Math.random().toString() }) }) return ipfs } }, + createFile: function (ipfs) { + new Promise((resolve, reject) => { + ipfs.on('ready', async () => { + await ipfs.add(this.ipfsConstructor.Buffer.from('You did it!')) + resolve() + }) + }) + }, resetCode: function () { // TRACK? User chose to reset code this.code = this.$attrs.code || defaultCode @@ -398,7 +417,6 @@ export default { resetFileUpload: function () { this.uploadedFiles = false this.dragging = false - console.log({uploadedFiles: this.uploadedFiles}) }, clearPassed: function () { delete localStorage[this.lessonKey] @@ -566,6 +584,49 @@ div#drop-area * { border-width: 5px 5px 5px; margin-top: 5px; } + +.loader, +.loader:before, +.loader:after { + border-radius: 50%; + width: 2em; + height: 2em; + animation-fill-mode: both; + animation: loadAnim 1.5s infinite ease-in-out; +} + +.loader { + display: block; + margin: 7px auto; + color: #ffffff; + font-size: 5px; + top: -10px; + position: relative; + animation-delay: -0.15s; + pointer-events: none; +} + +.loader:before { + content: ''; + position: absolute; + left: -3.5em; + animation-delay: -0.30s; +} + +.loader:after { + content: ''; + position: absolute; + left: 3.5em; +} + +@keyframes loadAnim { + 0%, 80%, 100% { + box-shadow: 0 2em 0 -1.3em; + } + 40% { + box-shadow: 0 2em 0 0; + } +}