From d63ea10a3a6549c6dffde1b1256a3c4760044e03 Mon Sep 17 00:00:00 2001 From: Siwat Techavoranant Date: Tue, 10 Mar 2020 00:52:45 +0700 Subject: [PATCH] Add list pages and video player --- .firebaserc | 14 + angular.json | 9 +- firebase.json | 19 ++ package-lock.json | 379 +++++++++++++++++++----- package.json | 21 +- src/app/app-routing.module.ts | 6 +- src/app/app.module.ts | 6 +- src/app/auth/auth.guard.spec.ts | 15 + src/app/auth/auth.guard.ts | 40 +++ src/app/home/course/course.page.html | 35 +++ src/app/home/course/course.page.scss | 1 + src/app/home/course/course.page.spec.ts | 24 ++ src/app/home/course/course.page.ts | 97 ++++++ src/app/home/home-routing.module.ts | 34 +++ src/app/home/home.module.ts | 13 +- src/app/home/home.page.html | 37 ++- src/app/home/home.page.scss | 32 +- src/app/home/home.page.ts | 28 +- src/app/home/list/list.page.html | 16 + src/app/home/list/list.page.scss | 0 src/app/home/list/list.page.spec.ts | 24 ++ src/app/home/list/list.page.ts | 35 +++ src/app/man.service.spec.ts | 12 + src/app/man.service.ts | 75 +++++ src/app/welcome/welcome.module.ts | 26 ++ src/app/welcome/welcome.page.html | 27 ++ src/app/welcome/welcome.page.scss | 14 + src/app/welcome/welcome.page.spec.ts | 27 ++ src/app/welcome/welcome.page.ts | 79 +++++ src/environments/environment.prod.ts | 2 +- src/environments/environment.ts | 2 +- src/index.html | 2 +- 32 files changed, 996 insertions(+), 155 deletions(-) create mode 100644 .firebaserc create mode 100644 firebase.json create mode 100644 src/app/auth/auth.guard.spec.ts create mode 100644 src/app/auth/auth.guard.ts create mode 100644 src/app/home/course/course.page.html create mode 100644 src/app/home/course/course.page.scss create mode 100644 src/app/home/course/course.page.spec.ts create mode 100644 src/app/home/course/course.page.ts create mode 100644 src/app/home/home-routing.module.ts create mode 100644 src/app/home/list/list.page.html create mode 100644 src/app/home/list/list.page.scss create mode 100644 src/app/home/list/list.page.spec.ts create mode 100644 src/app/home/list/list.page.ts create mode 100644 src/app/man.service.spec.ts create mode 100644 src/app/man.service.ts create mode 100644 src/app/welcome/welcome.module.ts create mode 100644 src/app/welcome/welcome.page.html create mode 100644 src/app/welcome/welcome.page.scss create mode 100644 src/app/welcome/welcome.page.spec.ts create mode 100644 src/app/welcome/welcome.page.ts diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..ef2f1cb --- /dev/null +++ b/.firebaserc @@ -0,0 +1,14 @@ +{ + "targets": { + "keendev-flick-a": { + "hosting": { + "app": [ + "keendev-flick-a" + ] + } + } + }, + "projects": { + "production": "keendev-flick-a" + } +} \ No newline at end of file diff --git a/angular.json b/angular.json index a19e33e..4a83f27 100644 --- a/angular.json +++ b/angular.json @@ -38,6 +38,12 @@ }, { "input": "src/global.scss" + }, + { + "input": "node_modules/video.js/dist/video-js.css" + }, + { + "input": "node_modules/videojs-seek-buttons/dist/videojs-seek-buttons.css" } ], "scripts": [] @@ -191,7 +197,8 @@ } }, "cli": { - "defaultCollection": "@ionic/angular-toolkit" + "defaultCollection": "@ionic/angular-toolkit", + "analytics": "a68a0485-6e9d-44a5-b42d-924ea1260442" }, "schematics": { "@ionic/angular-toolkit:component": { diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..01e4a6f --- /dev/null +++ b/firebase.json @@ -0,0 +1,19 @@ +{ + "hosting": [ + { + "target": "app", + "public": "www", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f7cd316..f86b3e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -486,12 +486,39 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "inquirer": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz", @@ -543,6 +570,21 @@ "sourcemap-codec": "^1.4.4" } }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "open": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/open/-/open-7.0.0.tgz", @@ -567,6 +609,16 @@ "wcwidth": "^1.0.1" } }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "rimraf": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", @@ -1582,7 +1634,6 @@ "version": "7.8.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" }, @@ -1590,8 +1641,7 @@ "regenerator-runtime": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", - "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", - "dev": true + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, @@ -2321,6 +2371,15 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -2359,6 +2418,15 @@ } } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "ora": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.2.tgz", @@ -2374,6 +2442,16 @@ "wcwidth": "^1.0.1" } }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "rxjs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", @@ -2517,6 +2595,20 @@ } } }, + "@videojs/http-streaming": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-1.10.6.tgz", + "integrity": "sha512-uPBuunHnxWeFRYxRX0j6h1IIWv3+QKvSkZGmW9TvqxWBqeNGSrQymR6tm1nVjQ2HhMVxVphQTUhUTTPDVWqmQg==", + "requires": { + "aes-decrypter": "3.0.0", + "global": "^4.3.0", + "m3u8-parser": "4.4.0", + "mpd-parser": "0.8.1", + "mux.js": "5.2.1", + "url-toolkit": "^2.1.3", + "video.js": "^6.8.0 || ^7.0.0" + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -2752,6 +2844,16 @@ "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==", "dev": true }, + "aes-decrypter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.0.0.tgz", + "integrity": "sha1-eEihwUW5/b9Xrj4rWxvHzwZEqPs=", + "requires": { + "commander": "^2.9.0", + "global": "^4.3.2", + "pkcs7": "^1.0.2" + } + }, "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", @@ -2832,13 +2934,10 @@ "dev": true }, "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true }, "ansi-html": { "version": "0.0.7", @@ -4123,12 +4222,12 @@ } }, "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^2.0.0" } }, "cli-spinners": { @@ -4334,8 +4433,7 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "commondir": { "version": "1.0.1", @@ -5483,6 +5581,11 @@ "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -6322,9 +6425,9 @@ "dev": true }, "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -7102,6 +7205,22 @@ "toxic": "^1.0.0" } }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -8374,6 +8493,11 @@ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, + "individual": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz", + "integrity": "sha1-gzsJfa0jKU52EXqY+zjg2a1hu5c=" + }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", @@ -8403,60 +8527,32 @@ "dev": true }, "inquirer": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz", - "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", + "ansi-escapes": "^3.2.0", "chalk": "^2.4.2", - "cli-cursor": "^3.1.0", + "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", "run-async": "^2.2.0", "rxjs": "^6.4.0", - "string-width": "^4.1.0", + "string-width": "^2.1.0", "strip-ansi": "^5.1.0", "through": "^2.3.6" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -8464,14 +8560,6 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } } } } @@ -8704,6 +8792,11 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -10140,6 +10233,11 @@ "source-map-support": "^0.5.5" } }, + "keycode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz", + "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ=" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -10523,6 +10621,14 @@ "es5-ext": "~0.10.2" } }, + "m3u8-parser": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.4.0.tgz", + "integrity": "sha512-iH2AygTFILtato+XAgnoPYzLHM4R3DjATj7Ozbk7EHdB2XoLF2oyOUguM7Kc4UVHbQHHL/QPaw98r7PbWzG0gg==", + "requires": { + "global": "^4.3.2" + } + }, "magic-string": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", @@ -10870,6 +10976,14 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, "mini-css-extract-plugin": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", @@ -11120,6 +11234,15 @@ "run-queue": "^1.0.3" } }, + "mpd-parser": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.8.1.tgz", + "integrity": "sha512-WBTJ1bKk8OLUIxBh6s1ju1e2yz/5CzhPbgi6P3F3kJHKhGy1Z+ElvEnuzEbtC/dnbRcJtMXazE3f93N5LLdp9Q==", + "requires": { + "global": "^4.3.2", + "url-toolkit": "^2.1.1" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -11143,11 +11266,16 @@ "dev": true }, "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mux.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-5.2.1.tgz", + "integrity": "sha512-1t2payD3Y8izfZRq7tfUQlhL2fKzjeLr9v1/2qNCTkEQnd9Abtn1JgzsBgGZubEXh6lM5L8B0iLGoWQiukjtbQ==" + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -11622,12 +11750,20 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^2.1.0" + "mimic-fn": "^1.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + } } }, "open": { @@ -11940,6 +12076,11 @@ "safe-buffer": "^5.1.1" } }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -12097,6 +12238,11 @@ "pinkie": "^2.0.0" } }, + "pkcs7": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.2.tgz", + "integrity": "sha1-ttulJ1KMKUK/wSLOLa/NteWQdOc=" + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -13521,12 +13667,12 @@ "dev": true }, "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^5.1.0", + "onetime": "^2.0.0", "signal-exit": "^3.0.2" } }, @@ -13685,6 +13831,14 @@ "aproba": "^1.1.1" } }, + "rust-result": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz", + "integrity": "sha1-NMdbLm3Dn+WHXlveyFteD5FTb3I=", + "requires": { + "individual": "^2.0.0" + } + }, "rxjs": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", @@ -13698,6 +13852,14 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-4.0.0.tgz", + "integrity": "sha1-fA9XjPzNEtM6ccDgVBPi7KFx6qw=", + "requires": { + "rust-result": "^1.0.0" + } + }, "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", @@ -16060,6 +16222,11 @@ "prepend-http": "^1.0.1" } }, + "url-toolkit": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.1.6.tgz", + "integrity": "sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -16198,6 +16365,48 @@ "extsprintf": "^1.2.0" } }, + "video.js": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.6.6.tgz", + "integrity": "sha512-AXzHwymhvMpS7c7rF29u0j0/3tSs+v2gIk5UY8OkiDHSEHL7T0+t3hid4JHW7aGvTruUUgwyf4C74cX2RDL1Pw==", + "requires": { + "@babel/runtime": "^7.4.5", + "@videojs/http-streaming": "1.10.6", + "global": "4.3.2", + "keycode": "^2.2.0", + "safe-json-parse": "4.0.0", + "videojs-font": "3.2.0", + "videojs-vtt.js": "^0.14.1", + "xhr": "2.4.0" + } + }, + "videojs-font": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", + "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" + }, + "videojs-hotkeys": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/videojs-hotkeys/-/videojs-hotkeys-0.2.25.tgz", + "integrity": "sha512-XgMjWiqGlmAjuHtpP529A2voVh++z46FSD0XeSy+65yeuTZOd+w2CJmfrL4jPpGm+MME5l9lOLfVpoEeDaBa1Q==" + }, + "videojs-seek-buttons": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/videojs-seek-buttons/-/videojs-seek-buttons-1.5.0.tgz", + "integrity": "sha512-X9d4dIngPBA2c0rQ+ap8nVqtDDR8VPi89MUS6RIKyR8TymM/IfAJz+A/vvyeAx2Hiv68elaCse+TV1H9bzv9Rw==", + "requires": { + "global": "^4.3.2", + "video.js": "^5.19.2 || ^6 || ^7" + } + }, + "videojs-vtt.js": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.14.1.tgz", + "integrity": "sha512-YxOiywx6N9t3J5nqsE5WN2Sw4CSqVe3zV+AZm2T4syOc2buNJaD6ZoexSdeszx2sHLU/RRo2r4BJAXFDQ7Qo2Q==", + "requires": { + "global": "^4.3.1" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -18029,6 +18238,17 @@ "os-homedir": "^1.0.0" } }, + "xhr": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.0.tgz", + "integrity": "sha1-4W5mpF+GmGHu76tBbV7/ci3ECZM=", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -18073,8 +18293,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.0", diff --git a/package.json b/package.json index 0f1ec36..a478a55 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,18 @@ "@ionic-native/core": "^5.0.0", "@ionic-native/splash-screen": "^5.0.0", "@ionic-native/status-bar": "^5.0.0", - "@ionic/angular": "^5.0.0", + "@ionic/angular": "^5.0.4", "core-js": "^2.5.4", + "firebase": "^7.8.0", "rxjs": "~6.5.1", "tslib": "^1.10.0", - "zone.js": "~0.10.2", - "firebase": "^7.8.0" + "video.js": "^7.6.6", + "videojs-hotkeys": "^0.2.25", + "videojs-seek-buttons": "^1.5.0", + "zone.js": "~0.10.2" }, "devDependencies": { + "@angular-devkit/architect": "^0.900.0-0 || ^0.900.0", "@angular-devkit/build-angular": "~0.900.5", "@angular/cli": "~9.0.5", "@angular/compiler": "~9.0.5", @@ -41,6 +45,10 @@ "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "codelyzer": "^5.1.2", + "firebase-tools": "^7.12.1", + "fuzzy": "^0.1.3", + "inquirer": "^6.2.2", + "inquirer-autocomplete-prompt": "^1.0.1", "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", "karma": "~4.1.0", @@ -51,12 +59,7 @@ "protractor": "~5.4.0", "ts-node": "~7.0.0", "tslint": "~5.15.0", - "typescript": "~3.7.5", - "@angular-devkit/architect": "^0.900.0-0 || ^0.900.0", - "firebase-tools": "^7.12.1", - "fuzzy": "^0.1.3", - "inquirer": "^6.2.2", - "inquirer-autocomplete-prompt": "^1.0.1" + "typescript": "~3.7.5" }, "description": "An Ionic project" } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3dfab28..62e9696 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,9 +1,11 @@ import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; +import {WelcomePage} from './welcome/welcome.page'; +import {AuthGuard} from './auth/auth.guard'; const routes: Routes = [ - { path: '', redirectTo: 'home', pathMatch: 'full' }, - { path: 'home', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)}, + {path: '', component: WelcomePage}, + {path: 'home', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule), canLoad: [AuthGuard]}, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 72732b3..5de1f9d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,6 +11,8 @@ import { AppRoutingModule } from './app-routing.module'; import {environment} from '../environments/environment'; import {AngularFireModule} from '@angular/fire'; import {AngularFireAuthModule} from '@angular/fire/auth'; +import {WelcomePageModule} from './welcome/welcome.module'; +import {HttpClientModule} from '@angular/common/http'; @NgModule({ declarations: [AppComponent], @@ -19,8 +21,10 @@ import {AngularFireAuthModule} from '@angular/fire/auth'; BrowserModule, IonicModule.forRoot(), AppRoutingModule, + HttpClientModule, AngularFireModule.initializeApp(environment.firebase), - AngularFireAuthModule + AngularFireAuthModule, + WelcomePageModule ], providers: [ StatusBar, diff --git a/src/app/auth/auth.guard.spec.ts b/src/app/auth/auth.guard.spec.ts new file mode 100644 index 0000000..66586d9 --- /dev/null +++ b/src/app/auth/auth.guard.spec.ts @@ -0,0 +1,15 @@ +import {inject, TestBed} from '@angular/core/testing'; + +import {AuthGuard} from './auth.guard'; + +describe('AuthGuard', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthGuard] + }); + }); + + it('should ...', inject([AuthGuard], (guard: AuthGuard) => { + expect(guard).toBeTruthy(); + })); +}); diff --git a/src/app/auth/auth.guard.ts b/src/app/auth/auth.guard.ts new file mode 100644 index 0000000..14fa4a7 --- /dev/null +++ b/src/app/auth/auth.guard.ts @@ -0,0 +1,40 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot} from '@angular/router'; +import {Observable} from 'rxjs'; +import {AngularFireAuth} from '@angular/fire/auth'; +import {first, map} from 'rxjs/operators'; +import {ManService} from '../man.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate, CanLoad { + public allowed: boolean; + + constructor(private afAuth: AngularFireAuth, private router: Router, private manService: ManService) { + + } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.isLoggedIn(); + } + + canLoad(route: Route): boolean | Observable | Promise { + return this.isLoggedIn(); + } + + isLoggedIn(): Observable { + return this.afAuth.idToken.pipe( + first(), + map(u => { + if (u) { + this.manService.setIdToken(u); + return true; + } else { + this.router.navigate(['/']); + return false; + } + }) + ); + } +} diff --git a/src/app/home/course/course.page.html b/src/app/home/course/course.page.html new file mode 100644 index 0000000..230a36d --- /dev/null +++ b/src/app/home/course/course.page.html @@ -0,0 +1,35 @@ + + + + + + + {{ course || 'Course'}} + + + + + + + + +

+ Hotkeys  Space: Pause, ▲/▼: Volume, ◄/►: Seek, F: Fullscreen | Set playback speed +

+
+ + + + + {{ lecture.value.date | date:"d MMM y" }} - {{ lecture.value.title }} + ({{ lecture.value.lecturer}}) + + + + +
+
+
diff --git a/src/app/home/course/course.page.scss b/src/app/home/course/course.page.scss new file mode 100644 index 0000000..626312d --- /dev/null +++ b/src/app/home/course/course.page.scss @@ -0,0 +1 @@ +//@import "~video.js/src/css/video-js.scss"; diff --git a/src/app/home/course/course.page.spec.ts b/src/app/home/course/course.page.spec.ts new file mode 100644 index 0000000..44df0b7 --- /dev/null +++ b/src/app/home/course/course.page.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { CoursePage } from './course.page'; + +describe('CoursePage', () => { + let component: CoursePage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CoursePage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(CoursePage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/home/course/course.page.ts b/src/app/home/course/course.page.ts new file mode 100644 index 0000000..dcabb2c --- /dev/null +++ b/src/app/home/course/course.page.ts @@ -0,0 +1,97 @@ +import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {EMPTY, Observable} from 'rxjs'; +import {ActivatedRoute, Router} from '@angular/router'; +import {CourseMembers, ManService} from '../../man.service'; +import {switchMap} from 'rxjs/operators'; +import videojs from '../../../../node_modules/video.js/dist/video.es'; +import 'videojs-seek-buttons'; +import 'videojs-hotkeys'; +import {ManEndpoint} from '../../../environments/environment'; +import {AlertController} from '@ionic/angular'; + +@Component({ + selector: 'app-course', + templateUrl: './course.page.html', + styleUrls: ['./course.page.scss'] +}) +export class CoursePage implements OnInit, AfterViewInit { + @ViewChild('videoPlayer') videoPlayerElement: ElementRef; + videoPlayer: any; + year: string; + course: string; + list$: Observable; + + constructor(private route: ActivatedRoute, private router: Router, + private manService: ManService, private alertController: AlertController) { + } + + ngOnInit() { + this.list$ = this.route.paramMap.pipe( + switchMap(s => { + const year = s.get('year'); + const course = s.get('course'); + this.year = year; + this.course = course; + if (year && course) { + return this.manService.getVideosInCourse(year, course); + } else if (year) { + this.router.navigate(['home/' + year]); + } else { + this.router.navigate(['home']); + } + return EMPTY; + }) + ); + } + + ngAfterViewInit() { + this.videoPlayer = videojs(this.videoPlayerElement.nativeElement); + this.videoPlayer.ready(function() { + this.hotkeys({ + volumeStep: 0.1, + seekStep: 5, + enableModifiersForNumbers: false, + enableVolumeScroll: false + }); + }); + } + + viewVideo(lecture) { + this.videoPlayer.src({ + src: ManEndpoint + 'videos/' + this.year + '/' + this.course + '/' + lecture + '/master.m3u8', + type: 'application/x-mpegURL' + }); + } + + async setPlaybackSpeed() { + const alert = await this.alertController.create({ + header: 'Please enter speed!', + inputs: [ + { + name: 'speed', + type: 'number', + min: 0.5, + max: 8 + } + ], + buttons: [ + { + text: 'Cancel', + role: 'cancel', + cssClass: 'secondary' + }, + { + text: 'Ok', + handler: (i) => { + if (i.speed > 0.3 && i.speed < 9) { + this.videoPlayer.playbackRate(i.speed); + } + } + } + ] + }); + + await alert.present(); + } + +} diff --git a/src/app/home/home-routing.module.ts b/src/app/home/home-routing.module.ts new file mode 100644 index 0000000..31a1dc9 --- /dev/null +++ b/src/app/home/home-routing.module.ts @@ -0,0 +1,34 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {AuthGuard} from '../auth/auth.guard'; +import {HomePage} from './home.page'; +import {ListPage} from './list/list.page'; +import {CoursePage} from './course/course.page'; + +const routes: Routes = [ + { + path: '', + canActivate: [AuthGuard], + children: [ + { + path: '', + component: HomePage + }, + { + path: ':year', + component: ListPage + }, + { + path: ':year/:course', + component: CoursePage + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class HomeRoutingModule { +} diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index 3ccd41c..9f2aca3 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -2,22 +2,19 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { FormsModule } from '@angular/forms'; -import { RouterModule } from '@angular/router'; import { HomePage } from './home.page'; +import {ListPage} from './list/list.page'; +import {HomeRoutingModule} from './home-routing.module'; +import {CoursePage} from './course/course.page'; @NgModule({ imports: [ CommonModule, FormsModule, IonicModule, - RouterModule.forChild([ - { - path: '', - component: HomePage - } - ]) + HomeRoutingModule ], - declarations: [HomePage] + declarations: [HomePage, ListPage, CoursePage] }) export class HomePageModule {} diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 911cab7..19c407e 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -1,20 +1,27 @@ - - - Blank - - - - - - - Blank + + MDCU Recorded Lecture + + + + Sign out + + + - + -
- Ready to create an app? -

Start with Ionic UI Components

-
+ + + + + + + {{ folder }} + + + + + diff --git a/src/app/home/home.page.scss b/src/app/home/home.page.scss index 806e6ee..83a2325 100644 --- a/src/app/home/home.page.scss +++ b/src/app/home/home.page.scss @@ -1,31 +1,3 @@ -#container { - text-align: center; - - position: absolute; - left: 0; - right: 0; - top: 50%; - transform: translateY(-50%); +ion-card { + min-height:4rem; } - -#container strong { - font-size: 20px; - line-height: 26px; -} - -#container p { - font-size: 16px; - line-height: 22px; - - color: #8c8c8c; - - margin: 0; -} - -#container a { - text-decoration: none; -} - -ion-content ion-toolbar { - --background: translucent; -} \ No newline at end of file diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index 83522d5..37954f7 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -1,12 +1,28 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {Observable} from 'rxjs'; +import {ManService} from '../man.service'; +import {map} from 'rxjs/operators'; +import {AngularFireAuth} from '@angular/fire/auth'; +import {Router} from '@angular/router'; @Component({ - selector: 'app-home', - templateUrl: 'home.page.html', - styleUrls: ['home.page.scss'], + selector: 'app-home', + templateUrl: 'home.page.html', + styleUrls: ['home.page.scss'] }) -export class HomePage { +export class HomePage implements OnInit { + folderList$: Observable; - constructor() {} + constructor(private manService: ManService, private router: Router, private afAuth: AngularFireAuth) { + } + ngOnInit() { + this.folderList$ = this.manService.getVideoList().pipe(map(a => Object.keys(a))); + } + + logout() { + this.afAuth.signOut().then(_ => { + this.router.navigate(['/']); + }).catch(e => console.log('Reject', e)); + } } diff --git a/src/app/home/list/list.page.html b/src/app/home/list/list.page.html new file mode 100644 index 0000000..ddaa210 --- /dev/null +++ b/src/app/home/list/list.page.html @@ -0,0 +1,16 @@ + + + + + + {{ year || 'Loading'}} + + + + + + + {{ course }} + + + diff --git a/src/app/home/list/list.page.scss b/src/app/home/list/list.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/home/list/list.page.spec.ts b/src/app/home/list/list.page.spec.ts new file mode 100644 index 0000000..b63f894 --- /dev/null +++ b/src/app/home/list/list.page.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ListPage } from './list.page'; + +describe('ListPage', () => { + let component: ListPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ListPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(ListPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/home/list/list.page.ts b/src/app/home/list/list.page.ts new file mode 100644 index 0000000..0b0e911 --- /dev/null +++ b/src/app/home/list/list.page.ts @@ -0,0 +1,35 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {EMPTY, Observable} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; +import {ManService} from '../../man.service'; + +@Component({ + selector: 'app-list', + templateUrl: './list.page.html', + styleUrls: ['./list.page.scss'] +}) +export class ListPage implements OnInit { + year: string; + list$: Observable; + + constructor(private route: ActivatedRoute, private router: Router, private manService: ManService) { + } + + ngOnInit() { + this.list$ = this.route.paramMap.pipe( + switchMap(s => { + const year = s.get('year'); + this.year = year; + if (!year) { + this.router.navigate(['home']); + return EMPTY; + } + return this.manService.getVideoList().pipe(map(list => { + return list[year]; + })); + }) + ); + } + +} diff --git a/src/app/man.service.spec.ts b/src/app/man.service.spec.ts new file mode 100644 index 0000000..c7e97fb --- /dev/null +++ b/src/app/man.service.spec.ts @@ -0,0 +1,12 @@ +import {TestBed} from '@angular/core/testing'; + +import {ManService} from './man.service'; + +describe('ManService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: ManService = TestBed.get(ManService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/man.service.ts b/src/app/man.service.ts new file mode 100644 index 0000000..372cdb3 --- /dev/null +++ b/src/app/man.service.ts @@ -0,0 +1,75 @@ +import {Injectable} from '@angular/core'; +import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {ManEndpoint} from '../environments/environment'; +import {Observable, of} from 'rxjs'; +import {AngularFireAuth} from '@angular/fire/auth'; +import {map, tap} from 'rxjs/operators'; + + +@Injectable({ + providedIn: 'root' +}) +export class ManService { + private videoList: object; + + constructor(private http: HttpClient, private afAuth: AngularFireAuth) { + // Get authentication data + this.afAuth.idToken.subscribe(token => { + this.setIdToken(token); + }); + } + + private httpOptions = { + headers: new HttpHeaders({ + Authorization: '' + }) + }; + + setIdToken(idToken: string) { + this.httpOptions.headers = + this.httpOptions.headers.set('Authorization', 'Bearer ' + idToken); + } + + getVideoList(): Observable { + return this.videoList ? of(this.videoList) : this.get('v1/video').pipe(tap(a => { + this.videoList = a; + })); + } + + getVideosInCourse(year: string, course: string) { + return this.get('v1/video/' + year + '/' + course); + } + + checkAuthorization(): Observable { + return this.get('v1/auth_check').pipe(map(a => a === 'OK')); + } + + /*updateCurrentStudent(requestBody) { + if (!this.email) { + console.error('ManService user email is not set.'); + } + return this.patch('students/' + this.email, requestBody); + }*/ + + get(path: string): Observable { + if (this.httpOptions.headers.get('Authorization').length < 5) { + console.error('ManService ID token is not set.'); + } + return this.http.get(ManEndpoint + path, this.httpOptions); + } + + /*patch(path: string, body): Observable { + if (this.httpOptions.headers.get('Authorization').length < 5) { + console.error('ManService ID token is not set.'); + } + return this.http.patch(ManEndpoint + path, body, this.httpOptions); + }*/ +} + +export interface CourseMembers { + [key: string]: { + title: string, + lecturer: string, + date: string + }; +} diff --git a/src/app/welcome/welcome.module.ts b/src/app/welcome/welcome.module.ts new file mode 100644 index 0000000..6f0c4b1 --- /dev/null +++ b/src/app/welcome/welcome.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { Routes, RouterModule } from '@angular/router'; + +import { IonicModule } from '@ionic/angular'; + +import { WelcomePage } from './welcome.page'; + +const routes: Routes = [ + { + path: '', + component: WelcomePage + } +]; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes) + ], + declarations: [WelcomePage] +}) +export class WelcomePageModule {} diff --git a/src/app/welcome/welcome.page.html b/src/app/welcome/welcome.page.html new file mode 100644 index 0000000..bcf567a --- /dev/null +++ b/src/app/welcome/welcome.page.html @@ -0,0 +1,27 @@ + + + + MDCU Recorded Lecture + + + +
+

Hello, {{ user.displayName }}!


+ + Initialize + + + Sign out + +
+ + +

Sign in with your DocChula account


+ + Sign in with Docchula + +
+
+
+

The Student Union of the Faculty of Medicine, Chulalongkorn University

+
diff --git a/src/app/welcome/welcome.page.scss b/src/app/welcome/welcome.page.scss new file mode 100644 index 0000000..e7cfc40 --- /dev/null +++ b/src/app/welcome/welcome.page.scss @@ -0,0 +1,14 @@ +ion-content { + --ion-background-color: #fafafa; + + ion-card { + background-color: white; + max-width: 450px; + margin: 20vh auto auto; + } + + .grey-text { + color: gray; + font-size: 0.75rem; + } +} diff --git a/src/app/welcome/welcome.page.spec.ts b/src/app/welcome/welcome.page.spec.ts new file mode 100644 index 0000000..a80045a --- /dev/null +++ b/src/app/welcome/welcome.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WelcomePage } from './welcome.page'; + +describe('WelcomePage', () => { + let component: WelcomePage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ WelcomePage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WelcomePage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/welcome/welcome.page.ts b/src/app/welcome/welcome.page.ts new file mode 100644 index 0000000..580033f --- /dev/null +++ b/src/app/welcome/welcome.page.ts @@ -0,0 +1,79 @@ +import {Component, OnInit} from '@angular/core'; +import {AngularFireAuth} from '@angular/fire/auth'; +import {AlertController, LoadingController} from '@ionic/angular'; +import {Router} from '@angular/router'; +import * as firebase from 'firebase/app'; +import {ManService} from '../man.service'; + +@Component({ + selector: 'app-welcome', + templateUrl: './welcome.page.html', + styleUrls: ['./welcome.page.scss'] +}) +export class WelcomePage implements OnInit { + isAuthChecked: boolean; + + constructor( + private router: Router, public afAuth: AngularFireAuth, + private manService: ManService, private loadingCtrl: LoadingController, + public alertCtrl: AlertController) { + } + + ngOnInit() { + this.loadingCtrl.create({ + message: 'Authenticating...', + duration: 7000 + }).then(loading => { + loading.present(); + this.afAuth.authState.subscribe((user) => { + if (user) { + // User is signed in. + // Set ID token for Man service + user.getIdToken().then(idToken => { + this.manService.setIdToken(idToken); + + // Get this user's student information + this.manService.checkAuthorization().toPromise().then((result) => { + if (result) { + this.isAuthChecked = true; + loading.dismiss(); + this.goToHome(); + } + }, (reason) => { + console.error('Student data request failed', reason); + this.alertUnregistered(); + loading.dismiss(); + }); + }); + } else { + loading.dismiss(); + } + }); + }).catch(e => console.log('Reject', e)); + } + + + login() { + const provider = new firebase.auth.GoogleAuthProvider(); + provider.setCustomParameters({hd: 'docchula.com'}); + this.afAuth.signInWithPopup(provider); + } + + logout() { + this.afAuth.signOut().then().catch(e => console.log('Reject', e)); + } + + goToHome() { + this.router.navigate(['home']); + } + + async alertUnregistered() { + const alert = await this.alertCtrl.create({ + header: 'Unregistered!', + message: 'You are not allowed to access this website.', + buttons: ['OK'] + }); + + await alert.present(); + } +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index d07ad47..23e4d77 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -12,4 +12,4 @@ export const environment = { } }; -export const ManEndpoint = 'https://flick-man.docchula.com/v1/'; +export const ManEndpoint = 'https://flick-man.docchula.com/'; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 4284864..2084794 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -16,7 +16,7 @@ export const environment = { } }; -export const ManEndpoint = 'https://flick-man.docchula.com/v1/'; +export const ManEndpoint = 'https://flick-man.docchula.com/'; /* * For easier debugging in development mode, you can import the following file diff --git a/src/index.html b/src/index.html index d2eb084..aa891e8 100644 --- a/src/index.html +++ b/src/index.html @@ -3,7 +3,7 @@ - Ionic App + MDCU Recorded Lecture