From b111973f0f64625c8213d0487f2187d6c7389d49 Mon Sep 17 00:00:00 2001 From: Teri Chadbourne Date: Mon, 10 Jun 2019 13:00:35 -0400 Subject: [PATCH] feat: add MFS tutorial (#200) * Surface MFS tutorials - Uncomment MFS lessons in `main.js` - Add MFS tutorial to courses in `courses.json` * Grammar fixes * update lesson validation wording * change _solution to solution * add missing solution code to mfs lesson 2 * add solution prop to MFS lessons and boilerplates * Rework lesson order in MFS tutorial Rework lesson order * Update stat lesson content * Fix syntax error and validation in stat lesson * update lesson on stating again * swap order of lessons 2 and 3 * Rework messaging for IPFS introk * Apply new logging feature to file upload demo * Validate directory creation for mkdir (MFS #7) * chore: simplify js object files logging * add validation for missing {parents:true} * add validation for use of ls in stat lesson * update text of lessons 3, 4, 5 * improve formatting of references to root directory * improve example for mkdir lesson * add validation for other errors in mkdir lesson 7 * add blank space around user code area * add validation for matching filenames in #7 * remove console logs * validate ls-ing wrong directory in #7 * improve error msg when returning wrong ls in #7 * add boilerplates for lessons 8-12 * correct relative filepaths in readme * fix typo lesson 7 * add lesson 8 text * fix typo and scope issues lsn 7 * update lesson 8 instructions * add globals to boilerplates * add code and first pass validation for lsn 8 * attempt to test for incorrect use of await * test for mistakenly moving directory * swap order of validation to fix bug * add first draft of files.cp lesson * tweak lesson 9 solution display * first draft of files.read lesson * lesson 10 validation edits * fix: lesson 7 validations * address await problems in lesson 8 * add text for lesson 11 * revamp lesson 8 mv solution * remove lesson 12 * add initial validation for lesson 11 rm * attempt to add test file to non-mfs ipfs * add notes on TBValidated lessons * chore: lessons cleanup * chore: update lesson 09 * feat: lesson 9 now copies file from ipfs * feat: make lesson 10 work * feat: finish lesson 11 * fix: appease linter * fix: LessonLink spacing * fix: grow file upload container * chore: apply suggestions from code review Co-Authored-By: Alan Shaw * chore: replace folder word with directory * chore: rephrase mfs editing * chore: typos * text edits * make user name file when copying * improve lesson 9 validation * read from success.txt in lesson 10 * improve validation for lesson 11 * update headline and lesson titles * chore: update mfs description * fix: add await to ipfs.files.mv * chore: tidying up * feat: add loading animation --- README.md | 4 +- src/components/File-Lesson.vue | 4 +- src/components/Lesson.vue | 75 ++++++- src/components/LessonLink.vue | 4 +- src/main.js | 34 ++-- src/static/courses.json | 4 +- src/static/tutorials.json | 20 +- src/tutorials/Data-Structures/04.md | 4 +- src/tutorials/Data-Structures/05.md | 4 +- src/tutorials/Mutable-File-System/01.md | 21 +- src/tutorials/Mutable-File-System/01.vue | 2 +- .../Mutable-File-System/02-exercise.md | 2 +- src/tutorials/Mutable-File-System/02.md | 29 ++- src/tutorials/Mutable-File-System/02.vue | 53 +++-- .../Mutable-File-System/03-exercise.md | 4 +- src/tutorials/Mutable-File-System/03.md | 36 +--- src/tutorials/Mutable-File-System/03.vue | 61 ++---- .../Mutable-File-System/04-exercise.md | 6 +- src/tutorials/Mutable-File-System/04.md | 45 +++-- src/tutorials/Mutable-File-System/04.vue | 90 ++++----- .../Mutable-File-System/05-exercise.md | 6 +- src/tutorials/Mutable-File-System/05.md | 42 ++-- src/tutorials/Mutable-File-System/05.vue | 75 +++---- .../Mutable-File-System/06-exercise.md | 1 + src/tutorials/Mutable-File-System/06.md | 16 ++ src/tutorials/Mutable-File-System/06.vue | 94 +++++++++ .../Mutable-File-System/07-exercise.md | 2 + src/tutorials/Mutable-File-System/07.md | 19 ++ src/tutorials/Mutable-File-System/07.vue | 187 ++++++++++++++++++ .../Mutable-File-System/08-exercise.md | 9 + src/tutorials/Mutable-File-System/08.md | 27 +++ src/tutorials/Mutable-File-System/08.vue | 156 +++++++++++++++ .../Mutable-File-System/09-exercise.md | 3 + src/tutorials/Mutable-File-System/09.md | 39 ++++ src/tutorials/Mutable-File-System/09.vue | 126 ++++++++++++ .../Mutable-File-System/10-exercise.md | 3 + src/tutorials/Mutable-File-System/10.md | 25 +++ src/tutorials/Mutable-File-System/10.vue | 91 +++++++++ .../Mutable-File-System/11-exercise.md | 1 + src/tutorials/Mutable-File-System/11.md | 24 +++ src/tutorials/Mutable-File-System/11.vue | 128 ++++++++++++ .../boilerplates/boilerplate-file-upload.vue | 6 +- .../boilerplates/boilerplate-standard.vue | 6 +- src/utils/files.js | 11 ++ 44 files changed, 1325 insertions(+), 274 deletions(-) create mode 100644 src/tutorials/Mutable-File-System/06-exercise.md create mode 100644 src/tutorials/Mutable-File-System/06.md create mode 100644 src/tutorials/Mutable-File-System/06.vue create mode 100644 src/tutorials/Mutable-File-System/07-exercise.md create mode 100644 src/tutorials/Mutable-File-System/07.md create mode 100644 src/tutorials/Mutable-File-System/07.vue create mode 100644 src/tutorials/Mutable-File-System/08-exercise.md create mode 100644 src/tutorials/Mutable-File-System/08.md create mode 100644 src/tutorials/Mutable-File-System/08.vue create mode 100644 src/tutorials/Mutable-File-System/09-exercise.md create mode 100644 src/tutorials/Mutable-File-System/09.md create mode 100644 src/tutorials/Mutable-File-System/09.vue create mode 100644 src/tutorials/Mutable-File-System/10-exercise.md create mode 100644 src/tutorials/Mutable-File-System/10.md create mode 100644 src/tutorials/Mutable-File-System/10.vue create mode 100644 src/tutorials/Mutable-File-System/11-exercise.md create mode 100644 src/tutorials/Mutable-File-System/11.md create mode 100644 src/tutorials/Mutable-File-System/11.vue create mode 100644 src/utils/files.js 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 b124f449e..5a439282b 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 } // Hide the solution @@ -370,17 +379,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 @@ -400,7 +419,6 @@ export default { resetFileUpload: function () { this.uploadedFiles = false this.dragging = false - console.log({uploadedFiles: this.uploadedFiles}) }, clearPassed: function () { delete localStorage[this.lessonKey] @@ -568,6 +586,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; + } +}