├── .editorconfig ├── .gitignore ├── CNAME ├── LICENSE ├── README-en.md ├── README.md ├── _config.yml ├── admin ├── .editorconfig ├── .gitignore ├── .htmlhintrc ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── e2e │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── karma.conf.js ├── package.json ├── src │ ├── app │ │ ├── addroom │ │ │ ├── addroom.component.css │ │ │ ├── addroom.component.html │ │ │ ├── addroom.component.spec.ts │ │ │ └── addroom.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── auth.guard.spec.ts │ │ ├── auth.guard.ts │ │ ├── define.ts │ │ ├── misc │ │ │ ├── material.module.ts │ │ │ └── shared.module.ts │ │ ├── monitor │ │ │ ├── monitor.component.css │ │ │ ├── monitor.component.html │ │ │ ├── monitor.component.spec.ts │ │ │ └── monitor.component.ts │ │ ├── nav │ │ │ ├── nav.component.css │ │ │ ├── nav.component.html │ │ │ ├── nav.component.spec.ts │ │ │ └── nav.component.ts │ │ ├── room │ │ │ ├── room.component.css │ │ │ ├── room.component.html │ │ │ ├── room.component.spec.ts │ │ │ └── room.component.ts │ │ ├── roomlink │ │ │ ├── roomlink.component.css │ │ │ ├── roomlink.component.html │ │ │ ├── roomlink.component.spec.ts │ │ │ └── roomlink.component.ts │ │ ├── service │ │ │ ├── auth.service.spec.ts │ │ │ ├── auth.service.ts │ │ │ ├── eventbus.service.spec.ts │ │ │ ├── eventbus.service.ts │ │ │ ├── i18n.service.spec.ts │ │ │ ├── i18n.service.ts │ │ │ ├── logger.service.spec.ts │ │ │ └── logger.service.ts │ │ └── signin │ │ │ ├── signin.component.css │ │ │ ├── signin.component.html │ │ │ ├── signin.component.spec.ts │ │ │ └── signin.component.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── fonts │ │ │ ├── KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2 │ │ │ ├── KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2 │ │ │ ├── KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2 │ │ │ ├── material-icons.woff2 │ │ │ └── urls │ │ └── iconfont │ │ │ ├── MaterialIcons-Regular.eot │ │ │ ├── MaterialIcons-Regular.ijmap │ │ │ ├── MaterialIcons-Regular.svg │ │ │ ├── MaterialIcons-Regular.ttf │ │ │ ├── MaterialIcons-Regular.woff │ │ │ ├── MaterialIcons-Regular.woff2 │ │ │ ├── README.md │ │ │ ├── codepoints │ │ │ └── material-icons.css │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ └── theme.scss ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── app ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── config.xml ├── e2e │ ├── protractor-ci.conf.js │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── ionic.config.json ├── ngsw-config.json ├── package.json ├── resources │ ├── android │ │ ├── icon │ │ │ ├── drawable-hdpi-icon.png │ │ │ ├── drawable-ldpi-icon.png │ │ │ ├── drawable-mdpi-icon.png │ │ │ ├── drawable-xhdpi-icon.png │ │ │ ├── drawable-xxhdpi-icon.png │ │ │ └── drawable-xxxhdpi-icon.png │ │ └── splash │ │ │ ├── drawable-land-hdpi-screen.png │ │ │ ├── drawable-land-ldpi-screen.png │ │ │ ├── drawable-land-mdpi-screen.png │ │ │ ├── drawable-land-xhdpi-screen.png │ │ │ ├── drawable-land-xxhdpi-screen.png │ │ │ ├── drawable-land-xxxhdpi-screen.png │ │ │ ├── drawable-port-hdpi-screen.png │ │ │ ├── drawable-port-ldpi-screen.png │ │ │ ├── drawable-port-mdpi-screen.png │ │ │ ├── drawable-port-xhdpi-screen.png │ │ │ ├── drawable-port-xxhdpi-screen.png │ │ │ └── drawable-port-xxxhdpi-screen.png │ ├── icon.png │ ├── icon.psd │ ├── icon.psd.md5 │ ├── ios │ │ ├── icon │ │ │ ├── icon-1024.png │ │ │ ├── icon-40.png │ │ │ ├── icon-40@2x.png │ │ │ ├── icon-40@3x.png │ │ │ ├── icon-50.png │ │ │ ├── icon-50@2x.png │ │ │ ├── icon-60.png │ │ │ ├── icon-60@2x.png │ │ │ ├── icon-60@3x.png │ │ │ ├── icon-72.png │ │ │ ├── icon-72@2x.png │ │ │ ├── icon-76.png │ │ │ ├── icon-76@2x.png │ │ │ ├── icon-83.5@2x.png │ │ │ ├── icon-small.png │ │ │ ├── icon-small@2x.png │ │ │ ├── icon-small@3x.png │ │ │ ├── icon.png │ │ │ └── icon@2x.png │ │ └── splash │ │ │ ├── Default-568h@2x~iphone.png │ │ │ ├── Default-667h.png │ │ │ ├── Default-736h.png │ │ │ ├── Default-Landscape-736h.png │ │ │ ├── Default-Landscape@2x~ipad.png │ │ │ ├── Default-Landscape@~ipadpro.png │ │ │ ├── Default-Landscape~ipad.png │ │ │ ├── Default-Portrait@2x~ipad.png │ │ │ ├── Default-Portrait@~ipadpro.png │ │ │ ├── Default-Portrait~ipad.png │ │ │ ├── Default@2x~iphone.png │ │ │ ├── Default@2x~universal~anyany.png │ │ │ └── Default~iphone.png │ ├── screenshots │ │ ├── android-about.png │ │ ├── android-menu.png │ │ ├── android-schedule.png │ │ ├── android-speaker-detail.png │ │ ├── android-speakers.png │ │ ├── ios-about.png │ │ ├── ios-menu.png │ │ ├── ios-schedule.png │ │ ├── ios-speaker-detail.png │ │ └── ios-speakers.png │ ├── splash.png │ └── splash.png.md5 ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── app.scss │ │ ├── board │ │ │ ├── document │ │ │ │ ├── document.component.html │ │ │ │ ├── document.component.scss │ │ │ │ ├── document.component.spec.ts │ │ │ │ └── document.component.ts │ │ │ ├── mainvideo │ │ │ │ ├── mainvideo.component.html │ │ │ │ ├── mainvideo.component.scss │ │ │ │ ├── mainvideo.component.spec.ts │ │ │ │ └── mainvideo.component.ts │ │ │ ├── sharedesk │ │ │ │ ├── sharedesk.component.html │ │ │ │ ├── sharedesk.component.scss │ │ │ │ ├── sharedesk.component.spec.ts │ │ │ │ └── sharedesk.component.ts │ │ │ ├── sharemedia │ │ │ │ ├── sharemedia.component.html │ │ │ │ ├── sharemedia.component.scss │ │ │ │ ├── sharemedia.component.spec.ts │ │ │ │ └── sharemedia.component.ts │ │ │ └── whiteboard │ │ │ │ ├── whiteboard.component.html │ │ │ │ ├── whiteboard.component.scss │ │ │ │ ├── whiteboard.component.spec.ts │ │ │ │ └── whiteboard.component.ts │ │ ├── chat │ │ │ ├── chat.component.html │ │ │ ├── chat.component.scss │ │ │ ├── chat.component.spec.ts │ │ │ └── chat.component.ts │ │ ├── config.ts │ │ ├── config_env.ts │ │ ├── defines.ts │ │ ├── drawtool │ │ │ ├── drawtool.component.html │ │ │ ├── drawtool.component.scss │ │ │ ├── drawtool.component.spec.ts │ │ │ └── drawtool.component.ts │ │ ├── guard │ │ │ └── login.guard.ts │ │ ├── main │ │ │ ├── main.component.html │ │ │ ├── main.component.scss │ │ │ └── main.component.ts │ │ ├── member │ │ │ ├── member.component.html │ │ │ ├── member.component.scss │ │ │ ├── member.component.spec.ts │ │ │ └── member.component.ts │ │ ├── pages │ │ │ └── login │ │ │ │ ├── login-routing.module.ts │ │ │ │ ├── login.html │ │ │ │ ├── login.module.ts │ │ │ │ ├── login.scss │ │ │ │ └── login.ts │ │ ├── pagetool │ │ │ ├── pagetool.component.html │ │ │ ├── pagetool.component.scss │ │ │ ├── pagetool.component.spec.ts │ │ │ └── pagetool.component.ts │ │ ├── popover │ │ │ ├── docselect │ │ │ │ ├── docselect.component.html │ │ │ │ ├── docselect.component.scss │ │ │ │ ├── docselect.component.spec.ts │ │ │ │ └── docselect.component.ts │ │ │ ├── emoji │ │ │ │ ├── emoji.component.html │ │ │ │ ├── emoji.component.scss │ │ │ │ ├── emoji.component.spec.ts │ │ │ │ ├── emoji.component.ts │ │ │ │ └── emoji.ts │ │ │ ├── information │ │ │ │ ├── information.component.html │ │ │ │ ├── information.component.scss │ │ │ │ ├── information.component.spec.ts │ │ │ │ └── information.component.ts │ │ │ ├── more │ │ │ │ ├── more.component.html │ │ │ │ ├── more.component.scss │ │ │ │ ├── more.component.spec.ts │ │ │ │ └── more.component.ts │ │ │ ├── netstat │ │ │ │ ├── netstat.component.html │ │ │ │ ├── netstat.component.scss │ │ │ │ ├── netstat.component.spec.ts │ │ │ │ └── netstat.component.ts │ │ │ ├── pencil │ │ │ │ ├── pencil.component.html │ │ │ │ ├── pencil.component.scss │ │ │ │ ├── pencil.component.spec.ts │ │ │ │ └── pencil.component.ts │ │ │ ├── setting │ │ │ │ ├── setting.component.html │ │ │ │ ├── setting.component.scss │ │ │ │ ├── setting.component.spec.ts │ │ │ │ └── setting.component.ts │ │ │ └── sharepopover │ │ │ │ ├── sharepopover.component.html │ │ │ │ ├── sharepopover.component.scss │ │ │ │ ├── sharepopover.component.spec.ts │ │ │ │ └── sharepopover.component.ts │ │ ├── service │ │ │ ├── auth.service.spec.ts │ │ │ ├── auth.service.ts │ │ │ ├── chat.service.spec.ts │ │ │ ├── chat.service.ts │ │ │ ├── classroom.service.spec.ts │ │ │ ├── classroom.service.ts │ │ │ ├── document.service.spec.ts │ │ │ ├── document.service.ts │ │ │ ├── drawtool.service.spec.ts │ │ │ ├── drawtool.service.ts │ │ │ ├── eventbus.service.spec.ts │ │ │ ├── eventbus.service.ts │ │ │ ├── i18n.service.spec.ts │ │ │ ├── i18n.service.ts │ │ │ ├── logger.service.spec.ts │ │ │ ├── logger.service.ts │ │ │ ├── media.service.spec.ts │ │ │ ├── media.service.ts │ │ │ ├── peer.service.spec.ts │ │ │ ├── peer.service.ts │ │ │ ├── profile.service.spec.ts │ │ │ ├── profile.service.ts │ │ │ ├── sharemedia.service.spec.ts │ │ │ ├── sharemedia.service.ts │ │ │ ├── signaling.service.spec.ts │ │ │ ├── signaling.service.ts │ │ │ ├── stats.service.spec.ts │ │ │ ├── stats.service.ts │ │ │ ├── wlhttp.service.spec.ts │ │ │ └── wlhttp.service.ts │ │ ├── thumbnail │ │ │ ├── thumbnail.component.html │ │ │ ├── thumbnail.component.scss │ │ │ ├── thumbnail.component.spec.ts │ │ │ └── thumbnail.component.ts │ │ └── videoplayer │ │ │ ├── videoplayer.component.html │ │ │ ├── videoplayer.component.scss │ │ │ ├── videoplayer.component.spec.ts │ │ │ └── videoplayer.component.ts │ ├── assets │ │ ├── data │ │ │ └── data.json │ │ └── img │ │ │ ├── about │ │ │ ├── Archive.zip │ │ │ ├── austin.jpg │ │ │ ├── chicago.jpg │ │ │ ├── madison.jpg │ │ │ └── seattle.jpg │ │ │ ├── appicon.png │ │ │ ├── appicon.svg │ │ │ ├── ica-slidebox-img-1.png │ │ │ ├── ica-slidebox-img-2.png │ │ │ ├── ica-slidebox-img-3.png │ │ │ ├── ica-slidebox-img-4.png │ │ │ ├── ionic-logo-white.svg │ │ │ ├── signin.svg │ │ │ ├── signup.svg │ │ │ ├── speaker-background.png │ │ │ ├── speakers │ │ │ ├── bear.jpg │ │ │ ├── cheetah.jpg │ │ │ ├── duck.jpg │ │ │ ├── eagle.jpg │ │ │ ├── elephant.jpg │ │ │ ├── giraffe.jpg │ │ │ ├── iguana.jpg │ │ │ ├── kitten.jpg │ │ │ ├── lion.jpg │ │ │ ├── mouse.jpg │ │ │ ├── puppy.jpg │ │ │ ├── rabbit.jpg │ │ │ └── turtle.jpg │ │ │ └── videocam-off.svg │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── global.scss │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── manifest.json │ ├── polyfills.ts │ ├── test.ts │ ├── theme │ │ └── variables.scss │ └── zone-flags.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── build.sh ├── res ├── appreciate.png └── wi2.gif └── server ├── certs ├── rtc.liweix.com.key └── rtc.liweix.com.pem ├── config └── config.ts ├── lib ├── Peer.ts ├── Room.ts └── defines.ts ├── log4js.json ├── model └── model.ts ├── package.json ├── route ├── avatar.ts ├── document.ts └── room.ts ├── server.ts ├── start.sh ├── tsconfig.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | dist 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | 45 | .history/ 46 | log/ 47 | database.sqlite 48 | package-lock.json 49 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | io.wisting.cn -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /admin/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | .history/ 45 | log/ 46 | -------------------------------------------------------------------------------- /admin/.htmlhintrc: -------------------------------------------------------------------------------- 1 | { 2 | "tag-pair" : true, 3 | "attr-no-duplication": true 4 | } 5 | -------------------------------------------------------------------------------- /admin/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 linewei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /admin/README.md: -------------------------------------------------------------------------------- 1 | # WiLearning Admin 2 | the web admin of WiLearning. build by Angular + Material 3 | -------------------------------------------------------------------------------- /admin/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /admin/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('classvideo-admin app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /admin/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /admin/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /admin/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/classvideo-admin'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wilearning-admin", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --base-href /admin/", 7 | "build": "ng build --prod --base-href /admin/", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~9.0.1", 15 | "@angular/cdk": "^9.0.0", 16 | "@angular/common": "~9.0.1", 17 | "@angular/compiler": "~9.0.1", 18 | "@angular/core": "~9.0.1", 19 | "@angular/flex-layout": "^9.0.0-beta.29", 20 | "@angular/forms": "~9.0.1", 21 | "@angular/material": "^9.0.0", 22 | "@angular/platform-browser": "~9.0.1", 23 | "@angular/platform-browser-dynamic": "~9.0.1", 24 | "@angular/router": "~9.0.1", 25 | "@types/clipboard": "^2.0.1", 26 | "@types/debug": "^4.1.5", 27 | "clipboard": "^2.0.4", 28 | "debug": "^4.1.1", 29 | "ngx-quill": "^8.1.3", 30 | "quill": "^1.3.7", 31 | "quill-emoji": "^0.1.7", 32 | "quill-image-resize": "^3.0.9", 33 | "quill-image-resize-module": "^3.0.0", 34 | "quill-mention": "^2.2.5", 35 | "rxjs": "~6.5.4", 36 | "tslib": "^1.10.0", 37 | "zone.js": "~0.10.2" 38 | }, 39 | "devDependencies": { 40 | "@angular-devkit/build-angular": "~0.900.2", 41 | "@angular/cli": "~9.0.2", 42 | "@angular/compiler-cli": "~9.0.1", 43 | "@angular/language-service": "~9.0.1", 44 | "@types/node": "^12.11.1", 45 | "@types/jasmine": "~3.5.0", 46 | "@types/jasminewd2": "~2.0.3", 47 | "codelyzer": "^5.1.2", 48 | "jasmine-core": "~3.5.0", 49 | "jasmine-spec-reporter": "~4.2.1", 50 | "karma": "~4.3.0", 51 | "karma-chrome-launcher": "~3.1.0", 52 | "karma-coverage-istanbul-reporter": "~2.1.0", 53 | "karma-jasmine": "~2.0.1", 54 | "karma-jasmine-html-reporter": "^1.4.2", 55 | "protractor": "~5.4.3", 56 | "ts-node": "~8.3.0", 57 | "tslint": "~5.18.0", 58 | "typescript": "~3.7.5" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /admin/src/app/addroom/addroom.component.css: -------------------------------------------------------------------------------- 1 | .display-block { 2 | display: block; 3 | } -------------------------------------------------------------------------------- /admin/src/app/addroom/addroom.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{i18n.lang.createRoom}}

5 |

{{i18n.lang.updateRoom}}

8 |
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | {{i18n.lang.roomDesc}} : 27 | 32 |
33 |
34 |
35 | 41 | 47 | 48 |
49 | -------------------------------------------------------------------------------- /admin/src/app/addroom/addroom.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AddroomComponent } from './addroom.component'; 4 | 5 | describe('AddroomComponent', () => { 6 | let component: AddroomComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AddroomComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AddroomComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /admin/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { NgModule } from '@angular/core'; 16 | import { Routes, RouterModule } from '@angular/router'; 17 | import { RoomComponent } from './room/room.component'; 18 | import { SigninComponent } from './signin/signin.component'; 19 | import { AuthGuard } from './auth.guard'; 20 | import { NavComponent } from './nav/nav.component'; 21 | import { MonitorComponent } from './monitor/monitor.component'; 22 | 23 | 24 | const routes: Routes = [ 25 | {path: 'sigin', component: SigninComponent}, 26 | { 27 | path: 'nav', component: NavComponent, canActivate: [AuthGuard], 28 | children: [ 29 | {path: '', redirectTo: 'room', pathMatch: 'full'}, 30 | {path: 'room', component: RoomComponent}, 31 | {path: 'monitor', component: MonitorComponent}, 32 | ] 33 | }, 34 | {path: '**', component: RoomComponent, canActivate: [AuthGuard]} 35 | ]; 36 | 37 | @NgModule({ 38 | imports: [ 39 | RouterModule.forRoot(routes, 40 | ) 41 | ], 42 | exports: [RouterModule] 43 | }) 44 | export class AppRoutingModule { } 45 | -------------------------------------------------------------------------------- /admin/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/app/app.component.css -------------------------------------------------------------------------------- /admin/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /admin/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'wimeeting-admin'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.componentInstance; 26 | expect(app.title).toEqual('wimeeting-admin'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('wimeeting-admin app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /admin/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Component } from '@angular/core'; 16 | import { NavComponent } from './nav/nav.component'; 17 | 18 | @Component({ 19 | selector: 'app-root', 20 | templateUrl: './app.component.html', 21 | styleUrls: ['./app.component.css'] 22 | }) 23 | export class AppComponent { 24 | title = 'WiLearning Admin'; 25 | } 26 | -------------------------------------------------------------------------------- /admin/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { BrowserModule } from '@angular/platform-browser'; 16 | import { NgModule } from '@angular/core'; 17 | 18 | import { AppRoutingModule } from './app-routing.module'; 19 | import { AppComponent } from './app.component'; 20 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 21 | import { NavComponent } from './nav/nav.component'; 22 | import { LayoutModule } from '@angular/cdk/layout'; 23 | import { SharedModule } from './misc/shared.module'; 24 | import { QuillModule } from 'ngx-quill'; 25 | import { RoomComponent } from './room/room.component'; 26 | import { AddroomComponent } from './addroom/addroom.component'; 27 | import { RoomlinkComponent } from './roomlink/roomlink.component'; 28 | import { SigninComponent } from './signin/signin.component'; 29 | import { MonitorComponent } from './monitor/monitor.component'; 30 | 31 | @NgModule({ 32 | declarations: [ 33 | AppComponent, 34 | NavComponent, 35 | RoomComponent, 36 | AddroomComponent, 37 | RoomlinkComponent, 38 | SigninComponent, 39 | MonitorComponent 40 | ], 41 | imports: [ 42 | BrowserModule, 43 | AppRoutingModule, 44 | BrowserAnimationsModule, 45 | LayoutModule, 46 | QuillModule.forRoot(), 47 | SharedModule, 48 | ], 49 | entryComponents: [ 50 | AddroomComponent, 51 | RoomlinkComponent, 52 | ], 53 | providers: [], 54 | bootstrap: [AppComponent] 55 | }) 56 | export class AppModule { } 57 | -------------------------------------------------------------------------------- /admin/src/app/auth.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthGuard } from './auth.guard'; 4 | 5 | describe('AuthGuard', () => { 6 | let guard: AuthGuard; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | guard = TestBed.inject(AuthGuard); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(guard).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /admin/src/app/auth.guard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Injectable } from '@angular/core'; 16 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; 17 | import { Observable } from 'rxjs'; 18 | import { AuthService } from './service/auth.service'; 19 | 20 | @Injectable({ 21 | providedIn: 'root' 22 | }) 23 | export class AuthGuard implements CanActivate { 24 | constructor( 25 | private auth: AuthService, 26 | private route: Router, 27 | ) { 28 | } 29 | 30 | canActivate( 31 | next: ActivatedRouteSnapshot, 32 | state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { 33 | if ( this.auth.isLoggedIn ) { 34 | return true; 35 | } 36 | 37 | this.route.navigate(['/sigin']); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /admin/src/app/define.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { environment } from '../environments/environment'; 16 | 17 | export function getHost() { 18 | if ((environment.hasOwnProperty('host') && environment.host?.length)) { 19 | return environment.host; 20 | } 21 | 22 | return ''; 23 | } 24 | -------------------------------------------------------------------------------- /admin/src/app/misc/shared.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { NgModule } from '@angular/core'; 16 | import { CommonModule } from '@angular/common'; 17 | import { FormsModule } from '@angular/forms'; 18 | import { FlexLayoutModule } from '@angular/flex-layout'; 19 | import { LayoutModule } from '@angular/cdk/layout'; 20 | import {OverlayModule} from '@angular/cdk/overlay'; 21 | import {PortalModule} from '@angular/cdk/portal'; 22 | import {DragDropModule} from '@angular/cdk/drag-drop'; 23 | import { ReactiveFormsModule } from '@angular/forms'; 24 | import { HttpClientModule } from '@angular/common/http'; 25 | import { MaterialComponentsModule } from './material.module'; 26 | 27 | @NgModule({ 28 | imports: [ 29 | CommonModule, 30 | FormsModule, 31 | FlexLayoutModule, 32 | LayoutModule, 33 | OverlayModule, 34 | PortalModule, 35 | DragDropModule, 36 | ReactiveFormsModule, 37 | HttpClientModule, 38 | MaterialComponentsModule 39 | ], 40 | exports: [ 41 | CommonModule, 42 | FormsModule, 43 | FlexLayoutModule, 44 | LayoutModule, 45 | OverlayModule, 46 | PortalModule, 47 | DragDropModule, 48 | ReactiveFormsModule, 49 | HttpClientModule, 50 | MaterialComponentsModule 51 | ] 52 | }) 53 | export class SharedModule {} 54 | -------------------------------------------------------------------------------- /admin/src/app/monitor/monitor.component.css: -------------------------------------------------------------------------------- 1 | mat-panel-title{ 2 | width: 15vw; 3 | } 4 | 5 | .peerinfo { 6 | width: 8vw; 7 | } -------------------------------------------------------------------------------- /admin/src/app/monitor/monitor.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ID 6 | 7 | 8 | {{i18n.lang.classStatus}} 9 | 10 | 11 | {{i18n.lang.peersNumber}} 12 | 13 | 14 | {{i18n.lang.durationTime}} 15 | 16 | 17 | {{i18n.lang.lastActionTime}} 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{room.id}} 25 | 26 | 27 | {{room.status}} 28 | 29 | 30 | {{room.peers.length}} 31 | 32 | 33 | {{room.duration/60|number:'1.0-0'}} 34 | 35 | 36 | {{room.lastActive/60|number: '1.0-0'}} 37 | 38 | 39 | 40 | {{peer.displayName}} 41 | {{peer.id}} 42 | {{peer.roler === 1 ? 'Speaker': 'Attendee'}} 43 | {{peer.platform}} 44 | {{peer.address}} 45 | {{peer.durationTime / 60 | number:'1.0-0'}} 46 | Transport:{{peer.transports.length}} 47 | Producer:{{peer.producers.length}} 48 | Consmer:{{peer.consumers.length}} 49 | 50 | 51 | -------------------------------------------------------------------------------- /admin/src/app/monitor/monitor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MonitorComponent } from './monitor.component'; 4 | 5 | describe('MonitorComponent', () => { 6 | let component: MonitorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ MonitorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MonitorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /admin/src/app/monitor/monitor.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Component, OnInit } from '@angular/core'; 16 | import { HttpClient } from '@angular/common/http'; 17 | import { getHost } from '../define'; 18 | import { LoggerService } from '../service/logger.service'; 19 | import { MatSnackBar } from '@angular/material/snack-bar'; 20 | import { I18nService } from '../service/i18n.service'; 21 | 22 | @Component({ 23 | selector: 'app-monitor', 24 | templateUrl: './monitor.component.html', 25 | styleUrls: ['./monitor.component.css'] 26 | }) 27 | export class MonitorComponent implements OnInit { 28 | rooms$; 29 | roomDetail; 30 | 31 | constructor( 32 | public i18n: I18nService, 33 | private http: HttpClient, 34 | private logger: LoggerService, 35 | private snackbar: MatSnackBar, 36 | ) { 37 | 38 | } 39 | 40 | ngOnInit(): void { 41 | this.getActiveRooms(); 42 | } 43 | 44 | getActiveRooms() { 45 | const url = getHost() + '/room/active'; 46 | this.rooms$ = this.http.get(url); 47 | } 48 | 49 | openDetail(room) { 50 | this.roomDetail = null; 51 | const url = getHost() + '/room/activeDetail/' + room.id; 52 | 53 | this.http.get(url).subscribe(res => { 54 | this.roomDetail = res; 55 | this.logger.debug(JSON.stringify(res)); 56 | }, error => { 57 | this.snackbar.open('Get room detail error! ' + error.message, 'close', {duration: 5000}); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /admin/src/app/nav/nav.component.css: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | } 8 | 9 | .sidenav .mat-toolbar { 10 | background: inherit; 11 | } 12 | 13 | .mat-toolbar.mat-primary { 14 | position: sticky; 15 | top: 0; 16 | z-index: 1; 17 | } 18 | 19 | .active { 20 | background-color: lightgray; 21 | border-radius: 0 48px 48px 0; 22 | } -------------------------------------------------------------------------------- /admin/src/app/nav/nav.component.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | WiLearning Admin 7 | 8 | 9 | home 10 | 11 | {{i18n.lang.roomManage}} 12 | 13 | 14 | 15 | visibility 16 | 17 | {{i18n.lang.roomMonitor}} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /admin/src/app/nav/nav.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { LayoutModule } from '@angular/cdk/layout'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { MatButtonModule } from '@angular/material/button'; 5 | import { MatIconModule } from '@angular/material/icon'; 6 | import { MatListModule } from '@angular/material/list'; 7 | import { MatSidenavModule } from '@angular/material/sidenav'; 8 | import { MatToolbarModule } from '@angular/material/toolbar'; 9 | 10 | import { NavComponent } from './nav.component'; 11 | 12 | describe('NavComponent', () => { 13 | let component: NavComponent; 14 | let fixture: ComponentFixture; 15 | 16 | beforeEach(async(() => { 17 | TestBed.configureTestingModule({ 18 | declarations: [NavComponent], 19 | imports: [ 20 | NoopAnimationsModule, 21 | LayoutModule, 22 | MatButtonModule, 23 | MatIconModule, 24 | MatListModule, 25 | MatSidenavModule, 26 | MatToolbarModule, 27 | ] 28 | }).compileComponents(); 29 | })); 30 | 31 | beforeEach(() => { 32 | fixture = TestBed.createComponent(NavComponent); 33 | component = fixture.componentInstance; 34 | fixture.detectChanges(); 35 | }); 36 | 37 | it('should compile', () => { 38 | expect(component).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /admin/src/app/nav/nav.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Component } from '@angular/core'; 16 | import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; 17 | import { Observable } from 'rxjs'; 18 | import { map, shareReplay } from 'rxjs/operators'; 19 | import { I18nService } from '../service/i18n.service'; 20 | import { Router, NavigationEnd } from '@angular/router'; 21 | import { LoggerService } from '../service/logger.service'; 22 | 23 | @Component({ 24 | selector: 'app-nav', 25 | templateUrl: './nav.component.html', 26 | styleUrls: ['./nav.component.css'] 27 | }) 28 | export class NavComponent { 29 | 30 | redirectedUrl: string; 31 | 32 | isHandset$: Observable = this.breakpointObserver.observe(Breakpoints.Handset) 33 | .pipe( 34 | map(result => result.matches), 35 | shareReplay() 36 | ); 37 | 38 | constructor( 39 | public i18n: I18nService, 40 | private breakpointObserver: BreakpointObserver, 41 | private router: Router, 42 | private logger: LoggerService, 43 | ) { 44 | this.router.events.subscribe(value => { 45 | if ( value instanceof NavigationEnd) { 46 | this.redirectedUrl = value.urlAfterRedirects; 47 | } 48 | }); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /admin/src/app/room/room.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | margin: 24px; 4 | } 5 | 6 | button { 7 | margin: 0 16px 16px 0; 8 | } 9 | 10 | table { 11 | width: 100%; 12 | } 13 | 14 | .mat-column-select { 15 | overflow: initial; 16 | } -------------------------------------------------------------------------------- /admin/src/app/room/room.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RoomComponent } from './room.component'; 4 | 5 | describe('RoomComponent', () => { 6 | let component: RoomComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RoomComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RoomComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /admin/src/app/roomlink/roomlink.component.css: -------------------------------------------------------------------------------- 1 | button { 2 | margin-left: 20px; 3 | } 4 | 5 | mat-card { 6 | margin-top : 20px; 7 | margin-bottom: 20px; 8 | } -------------------------------------------------------------------------------- /admin/src/app/roomlink/roomlink.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{room.name}}

3 |
4 |
5 | 6 | 7 | 8 | {{i18n.lang.speaker}} 9 | 10 | 11 | 12 | 13 | {{speakerUrl}} 14 | 15 | 16 | 17 | 18 | 19 | {{i18n.lang.password}}: {{room.speakerPassword}} 20 | 21 | 22 | 23 | 24 | 25 | 26 | {{i18n.lang.attendee}} 27 | 28 | 29 | 30 | 31 | {{attendeeUrl}} 32 | 33 | 34 | 35 | 36 | 37 | {{i18n.lang.password}}: {{room.attendeePassword}} 38 | 39 | 40 |
41 |
42 | 43 |
44 | -------------------------------------------------------------------------------- /admin/src/app/roomlink/roomlink.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RoomlinkComponent } from './roomlink.component'; 4 | 5 | describe('RoomlinkComponent', () => { 6 | let component: RoomlinkComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RoomlinkComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RoomlinkComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /admin/src/app/roomlink/roomlink.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Component, OnInit, Inject } from '@angular/core'; 16 | import { I18nService } from '../service/i18n.service'; 17 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 18 | import * as Clipboard from 'clipboard'; 19 | 20 | @Component({ 21 | selector: 'app-roomlink', 22 | templateUrl: './roomlink.component.html', 23 | styleUrls: ['./roomlink.component.css'] 24 | }) 25 | export class RoomlinkComponent implements OnInit { 26 | 27 | room; 28 | speakerUrl; 29 | attendeeUrl; 30 | clipboardSpeaker = new Clipboard('.btn-speaker'); 31 | clipboardAttendee = new Clipboard('.btn-attendee'); 32 | 33 | constructor( 34 | public i18n: I18nService, 35 | public dialogRef: MatDialogRef, 36 | @Inject(MAT_DIALOG_DATA) public data, 37 | ) { 38 | this.room = data.room; 39 | this.speakerUrl = `${location.origin}/app/?room=${this.room.id}`; 40 | this.attendeeUrl = `${location.origin}/app/?roler=2&room=${this.room.id}`; 41 | } 42 | 43 | ngOnInit(): void { 44 | } 45 | 46 | openSpeakerUrl() { 47 | open(this.speakerUrl); 48 | } 49 | 50 | openAttendeeUrl() { 51 | open(this.attendeeUrl); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /admin/src/app/service/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | let service: AuthService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AuthService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /admin/src/app/service/auth.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Injectable } from '@angular/core'; 16 | 17 | @Injectable({ 18 | providedIn: 'root' 19 | }) 20 | export class AuthService { 21 | 22 | isLoggedIn = false; 23 | 24 | constructor() { } 25 | } 26 | -------------------------------------------------------------------------------- /admin/src/app/service/eventbus.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { EventbusService } from './eventbus.service'; 4 | 5 | describe('EventbusService', () => { 6 | let service: EventbusService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(EventbusService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /admin/src/app/service/eventbus.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Injectable } from '@angular/core'; 16 | import { Subject } from 'rxjs'; 17 | 18 | export interface IEventType { 19 | type: string|number; 20 | data?: any; 21 | error?: string; 22 | } 23 | 24 | export enum EventType { 25 | room_created = 1, 26 | } 27 | 28 | @Injectable({ 29 | providedIn: 'root' 30 | }) 31 | export class EventbusService { 32 | public room$ = new Subject(); 33 | 34 | constructor() { } 35 | } 36 | -------------------------------------------------------------------------------- /admin/src/app/service/i18n.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { I18nService } from './i18n.service'; 4 | 5 | describe('I18nService', () => { 6 | let service: I18nService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(I18nService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /admin/src/app/service/logger.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { LoggerService } from './logger.service'; 4 | 5 | describe('LoggerService', () => { 6 | let service: LoggerService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(LoggerService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /admin/src/app/service/logger.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Injectable } from '@angular/core'; 16 | import debug from 'debug'; 17 | 18 | const APP_NAME = 'wimeeting-admin'; 19 | localStorage.debug = 'wimeeting-admin:*'; 20 | 21 | @Injectable({ 22 | providedIn: 'root' 23 | }) 24 | export class LoggerService { 25 | private pDebug: any; 26 | private pWarn: any; 27 | private pError: any; 28 | 29 | constructor( 30 | ) { 31 | this.pDebug = debug(`${APP_NAME}:DEBUG`); 32 | this.pWarn = debug(`${APP_NAME}:WARN`); 33 | this.pError = debug(`${APP_NAME}:ERROR`); 34 | 35 | // tslint:disable-next-line: no-console 36 | this.pDebug.log = console.info.bind(console); 37 | this.pWarn.log = console.warn.bind(console); 38 | this.pError.log = console.error.bind(console); 39 | } 40 | 41 | get debug() { 42 | return this.pDebug; 43 | } 44 | 45 | get warn() { 46 | return this.pWarn; 47 | } 48 | 49 | get error() { 50 | return this.pError; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /admin/src/app/signin/signin.component.css: -------------------------------------------------------------------------------- 1 | .signin-container { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | 6 | 7 | .signin-content { 8 | padding: 70px 60px; 9 | border-left: 1px solid #d9ddf6; 10 | background: #f3fcff; 11 | } 12 | 13 | .display-block { 14 | display: block; 15 | } 16 | -------------------------------------------------------------------------------- /admin/src/app/signin/signin.component.html: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /admin/src/app/signin/signin.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SigninComponent } from './signin.component'; 4 | 5 | describe('SigninComponent', () => { 6 | let component: SigninComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SigninComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SigninComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /admin/src/app/signin/signin.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Component, OnInit } from '@angular/core'; 16 | import { I18nService } from '../service/i18n.service'; 17 | import { FormGroup, FormBuilder, Validators } from '@angular/forms'; 18 | import { Router } from '@angular/router'; 19 | import { AuthService } from '../service/auth.service'; 20 | 21 | @Component({ 22 | selector: 'app-signin', 23 | templateUrl: './signin.component.html', 24 | styleUrls: ['./signin.component.css'] 25 | }) 26 | export class SigninComponent implements OnInit { 27 | 28 | userForm: FormGroup; 29 | selectedLang = 'cn'; 30 | username; 31 | password; 32 | 33 | constructor( 34 | public i18n: I18nService, 35 | private fb: FormBuilder, 36 | private router: Router, 37 | private auth: AuthService, 38 | ) { } 39 | 40 | ngOnInit(): void { 41 | this.userForm = this.fb.group({ 42 | user: ['admin', Validators.required], 43 | password: [ 44 | 'admin', 45 | [ 46 | Validators.minLength(3), 47 | Validators.maxLength(10) 48 | ] 49 | ], 50 | }); 51 | } 52 | 53 | login() { 54 | this.username = this.userForm.get('user'); 55 | this.password = this.userForm.get('password'); 56 | this.auth.isLoggedIn = true; 57 | 58 | this.router.navigate(['/nav']); 59 | 60 | } 61 | 62 | langSelect() { 63 | this.i18n.setLocale(this.selectedLang); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /admin/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/.gitkeep -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/material-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/fonts/material-icons.woff2 -------------------------------------------------------------------------------- /admin/src/assets/fonts/urls: -------------------------------------------------------------------------------- 1 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCRc4AMP6lbBP.woff2 2 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fABc4AMP6lbBP.woff2 3 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCBc4AMP6lbBP.woff2 4 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBxc4AMP6lbBP.woff2 5 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCxc4AMP6lbBP.woff2 6 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2 7 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2 8 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2 9 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2 10 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2 11 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2 12 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2 13 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2 14 | https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2 15 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCRc4AMP6lbBP.woff2 16 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fABc4AMP6lbBP.woff2 17 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCBc4AMP6lbBP.woff2 18 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBxc4AMP6lbBP.woff2 19 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCxc4AMP6lbBP.woff2 20 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2 21 | https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2 22 | -------------------------------------------------------------------------------- /admin/src/assets/iconfont/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/iconfont/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /admin/src/assets/iconfont/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/iconfont/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /admin/src/assets/iconfont/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/iconfont/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /admin/src/assets/iconfont/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/assets/iconfont/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /admin/src/assets/iconfont/README.md: -------------------------------------------------------------------------------- 1 | The recommended way to use the Material Icons font is by linking to the web font hosted on Google Fonts: 2 | 3 | ```html 4 | 6 | ``` 7 | 8 | Read more in our full usage guide: 9 | http://google.github.io/material-design-icons/#icon-font-for-the-web 10 | -------------------------------------------------------------------------------- /admin/src/assets/iconfont/material-icons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(MaterialIcons-Regular.eot); /* For IE6-8 */ 6 | src: local('Material Icons'), 7 | local('MaterialIcons-Regular'), 8 | url(MaterialIcons-Regular.woff2) format('woff2'), 9 | url(MaterialIcons-Regular.woff) format('woff'), 10 | url(MaterialIcons-Regular.ttf) format('truetype'); 11 | } 12 | 13 | .material-icons { 14 | font-family: 'Material Icons'; 15 | font-weight: normal; 16 | font-style: normal; 17 | font-size: 24px; /* Preferred icon size */ 18 | display: inline-block; 19 | line-height: 1; 20 | text-transform: none; 21 | letter-spacing: normal; 22 | word-wrap: normal; 23 | white-space: nowrap; 24 | direction: ltr; 25 | 26 | /* Support for all WebKit browsers. */ 27 | -webkit-font-smoothing: antialiased; 28 | /* Support for Safari and Chrome. */ 29 | text-rendering: optimizeLegibility; 30 | 31 | /* Support for Firefox. */ 32 | -moz-osx-font-smoothing: grayscale; 33 | 34 | /* Support for IE. */ 35 | font-feature-settings: 'liga'; 36 | } 37 | -------------------------------------------------------------------------------- /admin/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | host: '' 4 | }; 5 | -------------------------------------------------------------------------------- /admin/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | host: 'http://rtc.liweix.com' 8 | }; 9 | 10 | /* 11 | * For easier debugging in development mode, you can import the following file 12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 13 | * 14 | * This import should be commented out in production mode because it will have a negative impact 15 | * on performance if an error is thrown. 16 | */ 17 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /admin/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/admin/src/favicon.ico -------------------------------------------------------------------------------- /admin/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WiLearning Admin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /admin/src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { enableProdMode } from '@angular/core'; 16 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 17 | 18 | import { AppModule } from './app/app.module'; 19 | import { environment } from './environments/environment'; 20 | 21 | if (environment.production) { 22 | enableProdMode(); 23 | } 24 | 25 | platformBrowserDynamic().bootstrapModule(AppModule) 26 | .catch(err => console.error(err)); 27 | -------------------------------------------------------------------------------- /admin/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /admin/src/theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the common styles for Angular Material. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | // **Be sure that you only ever include this mixin once!** 7 | @include mat-core(); 8 | 9 | // Define the default theme (same as the example above). 10 | $candy-app-primary: mat-palette($mat-indigo); 11 | $candy-app-accent: mat-palette($mat-pink, A200, A100, A400); 12 | $candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent); 13 | 14 | // Include the default theme styles. 15 | @include angular-material-theme($candy-app-theme); 16 | 17 | 18 | // Define an alternate dark theme. 19 | $dark-primary: mat-palette($mat-blue-grey); 20 | $dark-accent: mat-palette($mat-amber, A200, A100, A400); 21 | $dark-warn: mat-palette($mat-deep-orange); 22 | $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); 23 | 24 | // Include the alternative theme styles inside of a block with a CSS class. You can make this 25 | // CSS class whatever you want. In this example, any component inside of an element with 26 | // `.unicorn-dark-theme` will be affected by this alternate dark theme instead of the default theme. 27 | .unicorn-dark-theme { 28 | @include angular-material-theme($dark-theme); 29 | } 30 | -------------------------------------------------------------------------------- /admin/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/admin", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /admin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /admin/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-var-requires": false, 64 | "object-literal-key-quotes": [ 65 | true, 66 | "as-needed" 67 | ], 68 | "object-literal-sort-keys": false, 69 | "ordered-imports": false, 70 | "quotemark": [ 71 | true, 72 | "single" 73 | ], 74 | "trailing-comma": false, 75 | "no-conflicting-lifecycle": true, 76 | "no-host-metadata-property": true, 77 | "no-input-rename": true, 78 | "no-inputs-metadata-property": true, 79 | "no-output-native": true, 80 | "no-output-on-prefix": true, 81 | "no-output-rename": true, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "use-lifecycle-interface": true, 86 | "use-pipe-transform-interface": true 87 | }, 88 | "rulesDirectory": [ 89 | "codelyzer" 90 | ] 91 | } -------------------------------------------------------------------------------- /app/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | www/ 4 | 5 | *~ 6 | *.sw[mnpcod] 7 | *.log 8 | *.tmp 9 | *.tmp.* 10 | log.txt 11 | *.sublime-project 12 | *.sublime-workspace 13 | .vscode/ 14 | npm-debug.log* 15 | .firebase/ 16 | .idea/ 17 | .sourcemaps/ 18 | .sass-cache/ 19 | .tmp/ 20 | .versions/ 21 | coverage/ 22 | dist/ 23 | node_modules/ 24 | tmp/ 25 | temp/ 26 | hooks/ 27 | platforms/ 28 | plugins/ 29 | plugins/android.json 30 | plugins/ios.json 31 | $RECYCLE.BIN/ 32 | 33 | .DS_Store 34 | Thumbs.db 35 | UserInterfaceState.xcuserstate 36 | -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # WiLearning 2 | WiLearning Web 客户端,使用Ionic + Angular构建。 3 | 4 | # 编译 5 | 安装依赖包: 6 | ``` 7 | cnpm i 8 | ``` 9 | 编译: 10 | ``` 11 | npm run build 12 | ``` 13 | 14 | # 开发理念 15 | * 媒体流与房间管理分开控制 16 | * 计算相关的繁重工作优先在客户端完成,让服务器端保持简单 17 | 18 | -------------------------------------------------------------------------------- /app/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /app/e2e/protractor-ci.conf.js: -------------------------------------------------------------------------------- 1 | const config = require('./protractor.conf').config; 2 | 3 | config.capabilities = { 4 | browserName: 'chrome', 5 | chromeOptions: { 6 | args: ['--headless', '--no-sandbox', '--disable-gpu'], 7 | binary: require('puppeteer').executablePath(), 8 | }, 9 | }; 10 | 11 | exports.config = config; 12 | -------------------------------------------------------------------------------- /app/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /app/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('new App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display the menu', () => { 11 | page.navigateTo(); 12 | expect(page.getMenu()).toBeTruthy(); 13 | }); 14 | 15 | it('should get the slides text', () => { 16 | page.navigateTo(); 17 | expect(page.getFirstSlide()).toBe('ion-slide'); 18 | }); 19 | 20 | it('should create a router outlet', () => { 21 | page.navigateTo(); 22 | expect(page.getRouter()).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element, ElementFinder, ExpectedConditions } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | async getMenu() { 9 | const el = this.getElement('app-root ion-menu'); 10 | await this.waitForSelector(el); 11 | return el; 12 | } 13 | 14 | async getFirstSlide() { 15 | const el = this.getElement('app-root ion-slides ion-slide:first-child'); 16 | await this.waitForSelector(el); 17 | return el.getTagName(); 18 | } 19 | 20 | async getRouter() { 21 | const el = this.getElement('app-root ion-router-outlet'); 22 | await this.waitForSelector(el); 23 | return el; 24 | } 25 | 26 | async waitForSelector(el: ElementFinder) { 27 | return browser.wait(ExpectedConditions.presenceOf(el), 3000); 28 | } 29 | 30 | getElement(selector) { 31 | return element(by.css(selector)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-conference-app", 3 | "type": "angular", 4 | "integrations": { 5 | "cordova": {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/ngsw-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "/index.html", 3 | "assetGroups": [ 4 | { 5 | "name": "app", 6 | "installMode": "prefetch", 7 | "resources": { 8 | "files": [ 9 | "/favicon.ico", 10 | "/index.html", 11 | "/*.css", 12 | "/*.js" 13 | ] 14 | } 15 | }, { 16 | "name": "assets", 17 | "installMode": "lazy", 18 | "updateMode": "prefetch", 19 | "resources": { 20 | "files": [ 21 | "/assets/**" 22 | ] 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-hdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-hdpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-ldpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-ldpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-mdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-mdpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-xhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-xhdpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-xxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-xxhdpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/icon/drawable-xxxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/icon/drawable-xxxhdpi-icon.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-hdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-ldpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-mdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-xhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-xxhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-land-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-land-xxxhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-hdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-ldpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-mdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-xhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-xxhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/android/splash/drawable-port-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/android/splash/drawable-port-xxxhdpi-screen.png -------------------------------------------------------------------------------- /app/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/icon.png -------------------------------------------------------------------------------- /app/resources/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/icon.psd -------------------------------------------------------------------------------- /app/resources/icon.psd.md5: -------------------------------------------------------------------------------- 1 | c1760af57a8089fda7537fc043804bc8 -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-1024.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-40@3x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-83.5@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon.png -------------------------------------------------------------------------------- /app/resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Landscape@~ipadpro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Landscape@~ipadpro.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Portrait@~ipadpro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Portrait@~ipadpro.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default@2x~universal~anyany.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default@2x~universal~anyany.png -------------------------------------------------------------------------------- /app/resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /app/resources/screenshots/android-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/android-about.png -------------------------------------------------------------------------------- /app/resources/screenshots/android-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/android-menu.png -------------------------------------------------------------------------------- /app/resources/screenshots/android-schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/android-schedule.png -------------------------------------------------------------------------------- /app/resources/screenshots/android-speaker-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/android-speaker-detail.png -------------------------------------------------------------------------------- /app/resources/screenshots/android-speakers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/android-speakers.png -------------------------------------------------------------------------------- /app/resources/screenshots/ios-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/ios-about.png -------------------------------------------------------------------------------- /app/resources/screenshots/ios-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/ios-menu.png -------------------------------------------------------------------------------- /app/resources/screenshots/ios-schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/ios-schedule.png -------------------------------------------------------------------------------- /app/resources/screenshots/ios-speaker-detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/ios-speaker-detail.png -------------------------------------------------------------------------------- /app/resources/screenshots/ios-speakers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/screenshots/ios-speakers.png -------------------------------------------------------------------------------- /app/resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/resources/splash.png -------------------------------------------------------------------------------- /app/resources/splash.png.md5: -------------------------------------------------------------------------------- 1 | 0dcf1df8c92c1ece4382d3357d9f8562 -------------------------------------------------------------------------------- /app/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Component } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { MemberComponent } from './member/member.component'; 4 | import { MainComponent } from './main/main.component'; 5 | import { LoginGuard } from './guard/login.guard'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | pathMatch: 'full', 11 | component: MainComponent, 12 | canActivate: [LoginGuard] 13 | }, 14 | { 15 | path: 'member', 16 | component: MemberComponent, 17 | }, 18 | { 19 | path: 'login', 20 | loadChildren: () => import('./pages/login/login.module').then(m => m.LoginModule) 21 | }, 22 | ]; 23 | 24 | @NgModule({ 25 | imports: [RouterModule.forRoot(routes)], 26 | exports: [RouterModule] 27 | }) 28 | export class AppRoutingModule {} 29 | -------------------------------------------------------------------------------- /app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/app.component.scss -------------------------------------------------------------------------------- /app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'], 7 | encapsulation: ViewEncapsulation.None 8 | }) 9 | export class AppComponent implements OnInit { 10 | constructor( 11 | ) { 12 | } 13 | 14 | async ngOnInit() { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/app/app.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/app.scss -------------------------------------------------------------------------------- /app/src/app/board/document/document.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
6 | 7 | {{page.page}} 8 |
9 | 10 |
11 | 12 |
-------------------------------------------------------------------------------- /app/src/app/board/document/document.component.scss: -------------------------------------------------------------------------------- 1 | .drawtool { 2 | width: 100%; 3 | height: 40px; 4 | background-color: aqua; 5 | } 6 | 7 | .canvas-container { 8 | width: 100%; 9 | height: 100%; 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: space-evenly; 13 | align-items: center; 14 | } 15 | 16 | .doclist { 17 | width: 220px; 18 | height: 100%; 19 | overflow: scroll; 20 | } 21 | 22 | .imagecontainer { 23 | display: flex; 24 | flex-direction: column; 25 | justify-content: center; 26 | align-items: center; 27 | margin-bottom: 5px; 28 | } 29 | 30 | .imagecontainer:hover { 31 | -moz-box-shadow: 0 0 10px #ccc; 32 | -webkit-box-shadow: 0 0 10px #ccc; 33 | box-shadow: 0 0 10px #ccc; 34 | } 35 | 36 | .imageselected { 37 | -moz-box-shadow: 0 0 10px #ccc; 38 | -webkit-box-shadow: 0 0 10px #ccc; 39 | box-shadow: 0 0 10px #ccc; 40 | border: 1px solid blue; 41 | } 42 | 43 | canvas { 44 | -moz-box-shadow: 0 0 10px #ccc; 45 | -webkit-box-shadow: 0 0 10px #ccc; 46 | box-shadow: 0 0 10px #ccc; 47 | overflow: scroll; 48 | } -------------------------------------------------------------------------------- /app/src/app/board/document/document.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { DocumentComponent } from './document.component'; 5 | 6 | describe('DocumentComponent', () => { 7 | let component: DocumentComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ DocumentComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(DocumentComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/board/mainvideo/mainvideo.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

11 | {{i18n.lang.welcome}}!{{profile.me.displayName}}~ 12 |

13 |
14 |
15 | {{i18n.lang.welcomePrompt}} 16 |
17 |
18 | {{i18n.lang.setAsPresenter}} 19 |
20 |
21 |
22 |
-------------------------------------------------------------------------------- /app/src/app/board/mainvideo/mainvideo.component.scss: -------------------------------------------------------------------------------- 1 | ion-grid { 2 | height: 100% !important; 3 | } 4 | 5 | .container { 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | width: 100%; 10 | height: 100%; 11 | } -------------------------------------------------------------------------------- /app/src/app/board/mainvideo/mainvideo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { MainvideoComponent } from './mainvideo.component'; 5 | 6 | describe('MainvideoComponent', () => { 7 | let component: MainvideoComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ MainvideoComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(MainvideoComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/board/mainvideo/mainvideo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ProfileService } from '../../service/profile.service'; 3 | import { PeerService } from '../../service/peer.service'; 4 | import { I18nService } from '../../service/i18n.service'; 5 | import { AlertController } from '@ionic/angular'; 6 | import { AuthService } from '../../service/auth.service'; 7 | import { LoggerService } from '../../service/logger.service'; 8 | import {ROLE} from '../../defines'; 9 | 10 | @Component({ 11 | selector: 'app-mainvideo', 12 | templateUrl: './mainvideo.component.html', 13 | styleUrls: ['./mainvideo.component.scss'], 14 | }) 15 | export class MainvideoComponent implements OnInit { 16 | 17 | constructor( 18 | public profile: ProfileService, 19 | public peer: PeerService, 20 | public i18n: I18nService, 21 | private alert: AlertController, 22 | private auth: AuthService, 23 | private logger: LoggerService, 24 | ) { } 25 | 26 | async ngOnInit() { 27 | setTimeout(() => { 28 | this.peer.cameraToggleSide(false); 29 | }); 30 | 31 | } 32 | 33 | async setAsPrompt() { 34 | const alert = await this.alert.create({ 35 | inputs: [ 36 | { 37 | name: 'password', 38 | type: 'password', 39 | placeholder: this.i18n.lang.inputPresenterPassword 40 | } 41 | ], 42 | buttons: [ 43 | { 44 | text: this.i18n.lang.cancel, 45 | role: 'cancel', 46 | }, 47 | { 48 | text: this.i18n.lang.ok, 49 | handler: (value) => { 50 | this.setAsPresenter(value.password); 51 | } 52 | } 53 | ] 54 | }); 55 | 56 | await alert.present(); 57 | } 58 | 59 | async setAsPresenter(password) { 60 | this.auth.login({ 61 | username: this.profile.me.displayName, 62 | password, 63 | roomId: this.profile.roomId, 64 | roler: ROLE.MASTER + '' 65 | }).then(() => { 66 | this.peer.setAsPresenter(); 67 | }).catch(() => { 68 | 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/app/board/sharedesk/sharedesk.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/app/board/sharedesk/sharedesk.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/board/sharedesk/sharedesk.component.scss -------------------------------------------------------------------------------- /app/src/app/board/sharedesk/sharedesk.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { SharedeskComponent } from './sharedesk.component'; 5 | 6 | describe('SharedeskComponent', () => { 7 | let component: SharedeskComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ SharedeskComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(SharedeskComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/board/sharedesk/sharedesk.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { PeerService } from '../../service/peer.service'; 3 | import { ProfileService } from '../../service/profile.service'; 4 | 5 | @Component({ 6 | selector: 'app-sharedesk', 7 | templateUrl: './sharedesk.component.html', 8 | styleUrls: ['./sharedesk.component.scss'], 9 | }) 10 | export class SharedeskComponent implements OnInit { 11 | 12 | constructor( 13 | public peer: PeerService, 14 | public profile: ProfileService 15 | ) { } 16 | 17 | ngOnInit() { 18 | setTimeout(() => { 19 | this.peer.cameraToggleSide(true); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/app/board/sharemedia/sharemedia.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
-------------------------------------------------------------------------------- /app/src/app/board/sharemedia/sharemedia.component.scss: -------------------------------------------------------------------------------- 1 | video { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | #sharemedia { 7 | width: 100%; 8 | height: 100%; 9 | 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | } -------------------------------------------------------------------------------- /app/src/app/board/sharemedia/sharemedia.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { SharemediaComponent } from './sharemedia.component'; 5 | 6 | describe('SharemediaComponent', () => { 7 | let component: SharemediaComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ SharemediaComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(SharemediaComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/board/sharemedia/sharemedia.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ElementRef } from '@angular/core'; 2 | import { PeerService } from '../../service/peer.service'; 3 | import { SharemediaService } from '../../service/sharemedia.service'; 4 | import { ProfileService } from '../../service/profile.service'; 5 | 6 | @Component({ 7 | selector: 'app-sharemedia', 8 | templateUrl: './sharemedia.component.html', 9 | styleUrls: ['./sharemedia.component.scss'], 10 | }) 11 | export class SharemediaComponent implements OnInit { 12 | 13 | constructor( 14 | public peer: PeerService, 15 | public sharemedia: SharemediaService, 16 | public profile: ProfileService, 17 | ) { } 18 | 19 | ngOnInit() { 20 | setTimeout(() => { 21 | this.peer.cameraToggleSide(true); 22 | 23 | if (this.sharemedia.videoElement) { 24 | document.getElementById('sharemedia').appendChild(this.sharemedia.videoElement); 25 | } 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/app/board/whiteboard/whiteboard.component.html: -------------------------------------------------------------------------------- 1 |

2 | whiteboard works! 3 |

4 | -------------------------------------------------------------------------------- /app/src/app/board/whiteboard/whiteboard.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/board/whiteboard/whiteboard.component.scss -------------------------------------------------------------------------------- /app/src/app/board/whiteboard/whiteboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { WhiteboardComponent } from './whiteboard.component'; 5 | 6 | describe('WhiteboardComponent', () => { 7 | let component: WhiteboardComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ WhiteboardComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(WhiteboardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/board/whiteboard/whiteboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { PeerService } from '../../service/peer.service'; 3 | 4 | @Component({ 5 | selector: 'app-whiteboard', 6 | templateUrl: './whiteboard.component.html', 7 | styleUrls: ['./whiteboard.component.scss'], 8 | }) 9 | export class WhiteboardComponent implements OnInit { 10 | 11 | constructor( 12 | private peer: PeerService, 13 | ) { } 14 | 15 | ngOnInit() { 16 | setTimeout(() => { 17 | this.peer.cameraToggleSide(true); 18 | }); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/app/chat/chat.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 | {{timeFormat(msg.recvTime)}} 7 | {{msg.peer.displayName}} 8 |
9 |
{{msg.message}}
10 |
11 |
12 | 13 |
14 |
15 | {{msg.peer?.displayName}} 16 | {{timeFormat(msg.recvTime)}} 17 |
18 |
{{msg.message}}
19 |
20 | 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /app/src/app/chat/chat.component.scss: -------------------------------------------------------------------------------- 1 | ion-content { 2 | --offset-bottom: 500px; 3 | --offset-top: 500px; 4 | } 5 | 6 | .timeStyle { 7 | font-size: 12px; 8 | font-weight: 400; 9 | margin-right: 12px; 10 | text-align: center; 11 | color: rgba(0,0,0,.5); 12 | } 13 | 14 | .messagePartner { 15 | background: #E0E0E0; 16 | margin-right: 12px; 17 | padding: 8px; 18 | white-space:pre-wrap; 19 | margin: 5px; 20 | word-break: break-all; 21 | } 22 | 23 | .messageMe { 24 | white-space:pre-wrap; 25 | background: #b8f185; 26 | padding: 8px; 27 | margin: 5px; 28 | width: 100%; 29 | word-break: break-all; 30 | } 31 | 32 | .messageMeStyle { 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: center; 36 | align-items: flex-end; 37 | } -------------------------------------------------------------------------------- /app/src/app/chat/chat.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { ChatComponent } from './chat.component'; 5 | 6 | describe('ChatComponent', () => { 7 | let component: ChatComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ ChatComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(ChatComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/chat/chat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ChatService } from '../service/chat.service'; 3 | 4 | @Component({ 5 | selector: 'app-chat', 6 | templateUrl: './chat.component.html', 7 | styleUrls: ['./chat.component.scss'], 8 | }) 9 | export class ChatComponent implements OnInit { 10 | 11 | constructor( 12 | public chat: ChatService, 13 | ) { } 14 | 15 | ngOnInit() {} 16 | 17 | timeFormat(time: string) { 18 | const tt = new Date(time); 19 | const hours = tt.getHours(); 20 | const hoursString = hours < 10 ? '0' + hours : hours.toString(); 21 | const minutes = tt.getMinutes(); 22 | const minutesString = minutes < 10 ? '0' + minutes : minutes.toString(); 23 | 24 | return hoursString + ':' + minutesString; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/app/config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { environment } from '../environments/environment'; 16 | 17 | export const MediaServer = { 18 | address: 'rtc.liweix.com', 19 | }; 20 | 21 | export const AdminServer = { 22 | address: 'rtc.liweix.com', 23 | }; 24 | 25 | export const DocServer = { 26 | address: 'rtc.liweix.com' 27 | }; 28 | 29 | export const RequestConnectVideoTimeout = 60; 30 | export const RoomLogoHeight = 50; 31 | 32 | export const RequestTimeout = 10000; 33 | export const DocImagesUrl = `https://${DocServer.address}/docs/images`; 34 | 35 | export const videoConstrain = { 36 | frameRate: { 37 | ideal: 18, 38 | max: 25, 39 | min: 12 40 | }, 41 | }; 42 | export const audioConstrain = { 43 | autoGainControl: true, 44 | echoCancellation: true, 45 | noiseSuppression: true 46 | }; 47 | -------------------------------------------------------------------------------- /app/src/app/config_env.ts: -------------------------------------------------------------------------------- 1 | export const MediaServer = { 2 | address: 'rtc.liweix.com' 3 | }; 4 | -------------------------------------------------------------------------------- /app/src/app/drawtool/drawtool.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | 12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
-------------------------------------------------------------------------------- /app/src/app/drawtool/drawtool.component.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 40px; 3 | width: 100%; 4 | display: flex; 5 | justify-content: center; 6 | justify-items: center; 7 | } 8 | 9 | ion-toolbar { 10 | --min-height: 100%; 11 | } 12 | 13 | .color_red { 14 | background: red; 15 | height: 10px; 16 | width: 50px; 17 | } 18 | 19 | .color_black { 20 | background: black; 21 | height: 10px; 22 | width: 50px; 23 | } 24 | 25 | .color_green { 26 | background:green; 27 | height: 10px; 28 | width: 50px; 29 | } 30 | 31 | .color_yellow { 32 | background:yellow; 33 | height: 10px; 34 | width: 50px; 35 | } 36 | 37 | .color_blue { 38 | background:blue; 39 | height: 10px; 40 | width: 50px; 41 | } 42 | 43 | .lineweight_1{ 44 | background: black; 45 | width: 50px; 46 | height: 1px; 47 | } 48 | 49 | .lineweight_5{ 50 | background: black; 51 | width: 50px; 52 | height: 5px; 53 | } 54 | 55 | .lineweight_10{ 56 | background: black; 57 | width: 50px; 58 | height: 10px; 59 | } 60 | 61 | .lineweight_20{ 62 | background: black; 63 | width: 50px; 64 | height: 20px; 65 | } -------------------------------------------------------------------------------- /app/src/app/drawtool/drawtool.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { DrawtoolComponent } from './drawtool.component'; 5 | 6 | describe('DrawtoolComponent', () => { 7 | let component: DrawtoolComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ DrawtoolComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(DrawtoolComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/guard/login.guard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | 16 | import { Injectable } from '@angular/core'; 17 | import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; 18 | import { Observable } from 'rxjs'; 19 | import { AuthService } from '../service/auth.service'; 20 | import { LoggerService } from '../service/logger.service'; 21 | 22 | @Injectable({ 23 | providedIn: 'root' 24 | }) 25 | export class LoginGuard implements CanActivate { 26 | constructor( 27 | private auth: AuthService, 28 | private route: Router, 29 | private logger: LoggerService, 30 | ) { 31 | 32 | } 33 | canActivate( 34 | next: ActivatedRouteSnapshot, 35 | state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree { 36 | const url = state.url || '/'; 37 | 38 | if ( this.auth.isLoggedIn ) { 39 | this.logger.debug('user has been loggedIn, return true'); 40 | return true; 41 | } 42 | 43 | this.auth.redirectUrl = decodeURIComponent(url); 44 | this.route.navigate(['/login']); 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/app/main/main.component.scss: -------------------------------------------------------------------------------- 1 | ion-menu ion-content { 2 | --padding-bottom: 20px; 3 | 4 | --background: var(--ion-item-background, var(--ion-background-color, #fff)); 5 | } 6 | 7 | /* Remove background transitions for switching themes */ 8 | ion-menu ion-item { 9 | --transition: none; 10 | // --border-radius: 50px; 11 | } 12 | 13 | ion-item.selected { 14 | --color: var(--ion-color-primary); 15 | } 16 | 17 | ion-split-pane { 18 | --side-max-width: 20%; 19 | --side-min-width: 200px; 20 | } 21 | 22 | 23 | /* 24 | * Material Design Menu 25 | */ 26 | ion-menu.md ion-list { 27 | padding: 20px 0; 28 | } 29 | 30 | ion-menu.md ion-item.selected { 31 | --background: rgba(var(--ion-color-primary-rgb), 0.14); 32 | } 33 | 34 | ion-menu.md ion-item.selected ion-icon { 35 | color: var(--ion-color-primary); 36 | } 37 | 38 | ion-menu.md ion-list-header, 39 | ion-menu.md ion-item ion-icon { 40 | color: var(--ion-color-step-650, #5f6368); 41 | } 42 | 43 | ion-menu.md ion-list:not(:last-of-type) { 44 | border-bottom: 1px solid var(--ion-color-step-150, #d7d8da); 45 | } 46 | 47 | 48 | /* 49 | * iOS Menu 50 | */ 51 | ion-menu.ios ion-list-header { 52 | padding-left: 16px; 53 | padding-right: 16px; 54 | 55 | margin-bottom: 8px; 56 | } 57 | 58 | ion-menu.ios ion-list { 59 | padding: 20px 0 0; 60 | } 61 | 62 | ion-menu.ios ion-item { 63 | --padding-start: 16px; 64 | --min-height: 50px; 65 | } 66 | 67 | ion-menu.ios ion-item ion-icon { 68 | font-size: 24px; 69 | color: #73849a; 70 | } 71 | 72 | ion-menu.ios ion-item.selected ion-icon { 73 | color: var(--ion-color-primary); 74 | } 75 | 76 | .drawtoolbar { 77 | position: absolute; 78 | width: 100%; 79 | top:0; 80 | left: 0; 81 | height: 40px; 82 | } 83 | 84 | .pagetoolbar { 85 | position: absolute; 86 | width: 100%; 87 | bottom: 20px; 88 | left: 0; 89 | height: 40px; 90 | } -------------------------------------------------------------------------------- /app/src/app/member/member.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/member/member.component.scss -------------------------------------------------------------------------------- /app/src/app/member/member.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { MemberComponent } from './member.component'; 5 | 6 | describe('MemberComponent', () => { 7 | let component: MemberComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ MemberComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(MemberComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/member/member.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { PeerService } from '../service/peer.service'; 3 | import { I18nService } from '../service/i18n.service'; 4 | 5 | @Component({ 6 | selector: 'app-member', 7 | templateUrl: './member.component.html', 8 | styleUrls: ['./member.component.scss'], 9 | }) 10 | export class MemberComponent implements OnInit { 11 | localCamera: MediaStream; 12 | constructor( 13 | public peer: PeerService, 14 | public i18n: I18nService, 15 | ) { } 16 | 17 | async ngOnInit() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/app/pages/login/login-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { LoginPage } from './login'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: LoginPage 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forChild(routes)], 15 | exports: [RouterModule] 16 | }) 17 | export class LoginPageRoutingModule { } 18 | -------------------------------------------------------------------------------- /app/src/app/pages/login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/app/pages/login/login.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { IonicModule } from '@ionic/angular'; 5 | 6 | import { LoginPage } from './login'; 7 | import { LoginPageRoutingModule } from './login-routing.module'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | FormsModule, 13 | IonicModule, 14 | LoginPageRoutingModule 15 | ], 16 | declarations: [ 17 | LoginPage, 18 | ] 19 | }) 20 | export class LoginModule { } 21 | -------------------------------------------------------------------------------- /app/src/app/pages/login/login.scss: -------------------------------------------------------------------------------- 1 | .login-logo { 2 | padding: 20px 0; 3 | min-height: 200px; 4 | text-align: center; 5 | } 6 | 7 | .login-logo img { 8 | max-width: 370px; 9 | } 10 | 11 | .list { 12 | margin-bottom: 0; 13 | } 14 | 15 | .app-login{ 16 | display: flex; 17 | flex-direction: column; 18 | align-items: center; 19 | height: 100%; 20 | width: 100%; 21 | } 22 | 23 | .login-window{ 24 | max-width: 400px; 25 | } -------------------------------------------------------------------------------- /app/src/app/pagetool/pagetool.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 5 | 6 | 7 | {{dc.selectedDoc?.pageNum}} / {{dc.selectedDoc?.numPages}} 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{dc.selectedDoc?.zoom|percent}} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
-------------------------------------------------------------------------------- /app/src/app/pagetool/pagetool.component.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 40px; 3 | width: 100%; 4 | display: flex; 5 | justify-content: center; 6 | justify-items: center; 7 | } -------------------------------------------------------------------------------- /app/src/app/pagetool/pagetool.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { PagetoolComponent } from './pagetool.component'; 5 | 6 | describe('PagetoolComponent', () => { 7 | let component: PagetoolComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ PagetoolComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(PagetoolComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/pagetool/pagetool.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { DrawtoolService, DrawtoolType } from '../service/drawtool.service'; 3 | import { LoggerService } from '../service/logger.service'; 4 | import { DocumentService } from '../service/document.service'; 5 | import { ProfileService } from '../service/profile.service'; 6 | 7 | @Component({ 8 | selector: 'app-pagetool', 9 | templateUrl: './pagetool.component.html', 10 | styleUrls: ['./pagetool.component.scss'], 11 | }) 12 | export class PagetoolComponent implements OnInit { 13 | 14 | constructor( 15 | public drawtool: DrawtoolService, 16 | public dc: DocumentService, 17 | public profile: ProfileService, 18 | private logger: LoggerService, 19 | ) { } 20 | 21 | ngOnInit() {} 22 | 23 | zoomOut() { 24 | const zoom = this.dc.selectedDoc.zoom + 0.05; 25 | this.dc.selectedDoc.setZoom(zoom); 26 | } 27 | 28 | zoomIn() { 29 | const zoom = this.dc.selectedDoc.zoom - 0.05; 30 | if (zoom < 0.05) { 31 | return; 32 | } 33 | this.dc.selectedDoc.setZoom(zoom); 34 | } 35 | 36 | async nextPage() { 37 | await this.dc.selectedDoc.nextPage(); 38 | this.drawtool.setupTool(); 39 | } 40 | 41 | async prevPage() { 42 | await this.dc.selectedDoc.prevPage(); 43 | this.drawtool.setupTool(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/app/popover/docselect/docselect.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{i18n.lang.selectFile}} 4 | 5 | 6 | {{i18n.lang.addFile}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | {{doc.fileName}} 20 | 21 | 22 |
{{i18n.lang.empty}}
23 |
24 | 25 | 26 | 27 | {{message}} 28 | 29 | 30 | 31 | {{i18n.lang.export}} 32 | 33 | 34 | {{i18n.lang.select}} 35 | 36 | 37 | {{i18n.lang.close}} 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/app/popover/docselect/docselect.component.scss: -------------------------------------------------------------------------------- 1 | 2 | .doc-item:hover{ 3 | background-color: gray; 4 | } 5 | .doc-item-selected{ 6 | background-color: gray; 7 | } 8 | 9 | ion-item { 10 | --background-hover: gray; 11 | --background-activated: gray; 12 | } 13 | 14 | .item-select { 15 | --background: var(--ion-color-medium); 16 | } -------------------------------------------------------------------------------- /app/src/app/popover/docselect/docselect.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { DocselectComponent } from './docselect.component'; 5 | 6 | describe('DocselectComponent', () => { 7 | let component: DocselectComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ DocselectComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(DocselectComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/emoji/emoji.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{emoji}} 3 | -------------------------------------------------------------------------------- /app/src/app/popover/emoji/emoji.component.scss: -------------------------------------------------------------------------------- 1 | 2 | ion-button { 3 | --background-hover: #696969; 4 | } -------------------------------------------------------------------------------- /app/src/app/popover/emoji/emoji.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { EmojiComponent } from './emoji.component'; 5 | 6 | describe('EmojiComponent', () => { 7 | let component: EmojiComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ EmojiComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(EmojiComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/emoji/emoji.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { emoji } from './emoji'; 3 | import { EventbusService, EventType } from '../../service/eventbus.service'; 4 | 5 | @Component({ 6 | selector: 'app-emoji', 7 | templateUrl: './emoji.component.html', 8 | styleUrls: ['./emoji.component.scss'], 9 | }) 10 | export class EmojiComponent implements OnInit { 11 | 12 | emojiArray = []; 13 | constructor( 14 | private eventbus: EventbusService, 15 | ) { 16 | for ( const kind of emoji ) { 17 | for ( const ele of kind.emojis ) { 18 | this.emojiArray.push(ele.emoji); 19 | } 20 | } 21 | } 22 | 23 | ngOnInit() {} 24 | 25 | // tslint:disable-next-line: no-shadowed-variable 26 | selectEmoji(emoji) { 27 | this.eventbus.chat$.next({ 28 | type: EventType.chat_emoji, 29 | data: emoji 30 | }); 31 | this.eventbus.popover$.next({ 32 | type: EventType.popover_emojiClosed 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/app/popover/information/information.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{profile.roomInfo.name}} 4 | 5 | 6 | 7 | 8 | 9 | {{url}} 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 | 20 | {{i18n.lang.copy}} 21 | {{i18n.lang.close}} 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/app/popover/information/information.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/popover/information/information.component.scss -------------------------------------------------------------------------------- /app/src/app/popover/information/information.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { InformationComponent } from './information.component'; 5 | 6 | describe('InformationComponent', () => { 7 | let component: InformationComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ InformationComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(InformationComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/information/information.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AuthService } from '../../service/auth.service'; 3 | import { EventbusService, EventType, IEventType } from '../../service/eventbus.service'; 4 | import { I18nService } from '../../service/i18n.service'; 5 | import { LoggerService } from '../../service/logger.service'; 6 | import { ProfileService } from '../../service/profile.service'; 7 | 8 | @Component({ 9 | selector: 'app-information', 10 | templateUrl: './information.component.html', 11 | styleUrls: ['./information.component.scss'], 12 | }) 13 | export class InformationComponent implements OnInit { 14 | url; 15 | 16 | constructor( 17 | public profile: ProfileService, 18 | public i18n: I18nService, 19 | public auth: AuthService, 20 | private eventbus: EventbusService, 21 | private logger: LoggerService, 22 | ) { 23 | } 24 | 25 | ngOnInit() { 26 | this.url = `${location.origin}/app?room=${this.profile.roomId}`; 27 | document.getElementById('classInfo').innerHTML = this.profile.roomInfo.description; 28 | } 29 | 30 | close() { 31 | this.eventbus.popover$.next({ 32 | type: EventType.popover_informationClosed 33 | }); 34 | } 35 | 36 | copyInfo() { 37 | this.logger.debug('url: ', this.url); 38 | navigator.clipboard.writeText(this.url); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/app/popover/more/more.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{i18n.lang.chinese}} 4 | 5 | 6 | 7 | {{i18n.lang.darkTheme}} 8 | 9 | 10 | 11 | {{i18n.lang.mutedAll}} 12 | 13 | 14 | 15 | {{i18n.lang.setAsPresenter}} 16 | 17 | -------------------------------------------------------------------------------- /app/src/app/popover/more/more.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/popover/more/more.component.scss -------------------------------------------------------------------------------- /app/src/app/popover/more/more.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { MoreComponent } from './more.component'; 5 | 6 | describe('MoreComponent', () => { 7 | let component: MoreComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ MoreComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(MoreComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/netstat/netstat.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{i18n.lang.videoCodec}}:{{stats.videoCodec}} 5 | 6 | 7 | {{i18n.lang.videoResolution}}:{{stats.videoWidth}}*{{stats.videoHeight}} 8 | 9 | 10 | {{i18n.lang.videoFrame}}:{{stats.videoFrameRate}} 11 | 12 | 13 | {{i18n.lang.videoRecvRate}}:{{stats.videoKbpsRecv}}kbps 14 | 15 | 16 | {{i18n.lang.videoSendRate}}:{{stats.videoKbpsSent}}kbps 17 | 18 | 19 | 20 | {{i18n.lang.audioCodec}}:{{stats.audioCodec}} 21 | 22 | 23 | {{i18n.lang.audioRecvRate}}:{{stats.audioKbpsRecv}}kbps 24 | 25 | 26 | {{i18n.lang.audioSendRate}}:{{stats.audioKbpsSent}}kbps 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/app/popover/netstat/netstat.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/popover/netstat/netstat.component.scss -------------------------------------------------------------------------------- /app/src/app/popover/netstat/netstat.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { NetstatComponent } from './netstat.component'; 5 | 6 | describe('NetstatComponent', () => { 7 | let component: NetstatComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ NetstatComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(NetstatComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/netstat/netstat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { StatsService } from '../../service/stats.service'; 3 | import { I18nService } from '../../service/i18n.service'; 4 | 5 | @Component({ 6 | selector: 'app-netstat', 7 | templateUrl: './netstat.component.html', 8 | styleUrls: ['./netstat.component.scss'], 9 | }) 10 | export class NetstatComponent implements OnInit { 11 | 12 | constructor( 13 | public stats: StatsService, 14 | public i18n: I18nService, 15 | ) { } 16 | 17 | ngOnInit() {} 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/app/popover/pencil/pencil.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /app/src/app/popover/pencil/pencil.component.scss: -------------------------------------------------------------------------------- 1 | .color_red { 2 | background: red; 3 | height: 10px; 4 | width: 100%; 5 | } 6 | 7 | .color_black { 8 | background: black; 9 | height: 10px; 10 | width: 100%; 11 | } 12 | 13 | .color_green { 14 | background:green; 15 | height: 10px; 16 | width: 100%; 17 | } 18 | 19 | .color_yellow { 20 | background:yellow; 21 | height: 10px; 22 | width: 100%; 23 | } 24 | 25 | .color_blue { 26 | background: blue; 27 | height: 10px; 28 | width: 100%; 29 | } 30 | 31 | .lineweight_1 { 32 | height: 1px; 33 | background:black; 34 | width: 100%; 35 | } 36 | 37 | .lineweight_5 { 38 | height: 5px; 39 | background:black; 40 | width: 100%; 41 | } 42 | 43 | .lineweight_10 { 44 | height: 10px; 45 | background:black; 46 | width: 100%; 47 | } 48 | 49 | .lineweight_20 { 50 | height: 20px; 51 | background:black; 52 | width: 100%; 53 | } -------------------------------------------------------------------------------- /app/src/app/popover/pencil/pencil.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { PencilComponent } from './pencil.component'; 5 | 6 | describe('PencilComponent', () => { 7 | let component: PencilComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ PencilComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(PencilComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/pencil/pencil.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NavParams } from '@ionic/angular'; 3 | import { LoggerService } from '../../service/logger.service'; 4 | import { DrawtoolService } from '../../service/drawtool.service'; 5 | import { EventbusService, EventType } from '../../service/eventbus.service'; 6 | 7 | @Component({ 8 | selector: 'app-pencil', 9 | templateUrl: './pencil.component.html', 10 | styleUrls: ['./pencil.component.scss'], 11 | }) 12 | export class PencilComponent implements OnInit { 13 | prop = ''; 14 | constructor( 15 | private drawtool: DrawtoolService, 16 | private navparams: NavParams, 17 | private logger: LoggerService, 18 | private eventbus: EventbusService, 19 | ) { 20 | this.prop = this.navparams.data.props; 21 | this.logger.debug(this.prop); 22 | } 23 | 24 | ngOnInit() { 25 | this.logger.debug(this.navparams); 26 | } 27 | 28 | setColor(color) { 29 | this.drawtool.setColor(color); 30 | this.eventbus.popover$.next({ 31 | type: EventType.popover_selectPencilClosed 32 | }); 33 | } 34 | 35 | setLineWeight(weight) { 36 | this.drawtool.setLineWeight(weight); 37 | this.eventbus.popover$.next({ 38 | type: EventType.popover_selectPencilClosed 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/app/popover/setting/setting.component.scss: -------------------------------------------------------------------------------- 1 | video { 2 | max-width: 200px; 3 | max-height: 200px; 4 | } 5 | 6 | .item { 7 | display: flex; 8 | flex-direction: row; 9 | justify-content: space-between; 10 | margin-right: 10px; 11 | width: 100%; 12 | } 13 | 14 | .audio-volume-meter { 15 | width: 6px; 16 | height: 12px; 17 | margin: 2px; 18 | background-color: gray; 19 | } 20 | .meter-green{ 21 | background-color:green; 22 | } 23 | 24 | .volume-display { 25 | display: flex; 26 | flex-direction: row; 27 | align-items: flex-end; 28 | } -------------------------------------------------------------------------------- /app/src/app/popover/setting/setting.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { SettingComponent } from './setting.component'; 5 | 6 | describe('SettingComponent', () => { 7 | let component: SettingComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ SettingComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(SettingComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/sharepopover/sharepopover.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{i18n.lang.shareScreen}} 7 | 8 | 9 | 10 | 11 | 12 | {{i18n.lang.shareMedia}} 13 | 14 | 15 | 16 | 17 | 18 | {{i18n.lang.document}} 19 | 20 | 21 | 22 | 23 | 24 | {{i18n.lang.mainVideo}} 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/app/popover/sharepopover/sharepopover.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/popover/sharepopover/sharepopover.component.scss -------------------------------------------------------------------------------- /app/src/app/popover/sharepopover/sharepopover.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { SharepopoverComponent } from './sharepopover.component'; 5 | 6 | describe('SharepopoverComponent', () => { 7 | let component: SharepopoverComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ SharepopoverComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(SharepopoverComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/popover/sharepopover/sharepopover.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ProfileService } from '../../service/profile.service'; 3 | import { WlBoardComp } from '../../defines'; 4 | import { PeerService } from '../../service/peer.service'; 5 | import { EventbusService, EventType } from '../../service/eventbus.service'; 6 | import { I18nService } from '../../service/i18n.service'; 7 | import { SignalingService } from '../../service/signaling.service'; 8 | 9 | @Component({ 10 | selector: 'app-sharepopover', 11 | templateUrl: './sharepopover.component.html', 12 | styleUrls: ['./sharepopover.component.scss'], 13 | }) 14 | export class SharepopoverComponent implements OnInit { 15 | 16 | constructor( 17 | public profile: ProfileService, 18 | public peer: PeerService, 19 | public i18n: I18nService, 20 | private eventbus: EventbusService, 21 | private signaling: SignalingService, 22 | ) { } 23 | 24 | ngOnInit() {} 25 | 26 | openVideo() { 27 | this.profile.switchBoardComponent(WlBoardComp.video); 28 | this.closeWindow(); 29 | this.signaling.sendSwitchComponent(WlBoardComp.video); 30 | } 31 | 32 | async shareDesktop() { 33 | this.closeWindow(); 34 | if (this.peer.pScreen) { 35 | this.profile.switchBoardComponent(WlBoardComp.sharescreen); 36 | this.signaling.sendSwitchComponent(WlBoardComp.sharescreen); 37 | } else if (await this.peer.startScreenShare()) { 38 | this.profile.switchBoardComponent(WlBoardComp.sharescreen); 39 | this.signaling.sendSwitchComponent(WlBoardComp.sharescreen); 40 | } 41 | } 42 | 43 | shareMedia() { 44 | this.profile.switchBoardComponent(WlBoardComp.sharemedia); 45 | this.closeWindow(); 46 | this.signaling.sendSwitchComponent(WlBoardComp.sharemedia); 47 | } 48 | 49 | openDocument() { 50 | this.profile.switchBoardComponent(WlBoardComp.document); 51 | this.closeWindow(); 52 | this.signaling.sendSwitchComponent(WlBoardComp.document); 53 | } 54 | 55 | closeWindow() { 56 | this.eventbus.popover$.next({ 57 | type: EventType.popover_shareClosed, 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/app/service/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthService } from './auth.service'; 4 | 5 | describe('AuthService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: AuthService = TestBed.get(AuthService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/auth.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | 16 | import { Injectable } from '@angular/core'; 17 | import * as CryptoJs from 'crypto-js'; 18 | import { WlhttpService } from './wlhttp.service'; 19 | import { LoggerService } from './logger.service'; 20 | import { AdminServer } from '../config'; 21 | 22 | @Injectable({ 23 | providedIn: 'root' 24 | }) 25 | export class AuthService { 26 | public redirectUrl: string; 27 | public isLoggedIn = false; 28 | 29 | constructor( 30 | private logger: LoggerService, 31 | private http: WlhttpService, 32 | ) { 33 | } 34 | login(userInfo: {username: string, password: string, roomId: string, roler}) { 35 | this.isLoggedIn = true; 36 | const cryptoPasswd = CryptoJs.MD5(userInfo.password).toString().toUpperCase(); 37 | const loginUrl = `https://${AdminServer.address}/room/login/${userInfo.roomId}/${userInfo.roler}/${userInfo.username}/${cryptoPasswd}`; 38 | this.logger.debug('loginUrl : %s', loginUrl); 39 | 40 | return this.http.http.get(loginUrl).toPromise(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/app/service/chat.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ChatService } from './chat.service'; 4 | import { PeerService } from './peer.service'; 5 | import { WebsocketService } from './websocket.service'; 6 | import { EventbusService, IEventType, EventType } from './eventbus.service'; 7 | import { ProfileService } from './profile.service'; 8 | 9 | 10 | describe('ChatService', () => { 11 | beforeEach(() => TestBed.configureTestingModule({ 12 | providers: [PeerService, WebsocketService, EventbusService, ProfileService] 13 | })); 14 | 15 | it('should be created', () => { 16 | const service: ChatService = TestBed.get(ChatService); 17 | expect(service).toBeTruthy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /app/src/app/service/classroom.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ClassroomService } from './classroom.service'; 4 | 5 | describe('ClassroomService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ClassroomService = TestBed.get(ClassroomService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/document.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { DocumentService } from './document.service'; 4 | 5 | describe('DocumentService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: DocumentService = TestBed.get(DocumentService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/drawtool.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { DrawtoolService } from './drawtool.service'; 4 | 5 | describe('DrawtoolService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: DrawtoolService = TestBed.get(DrawtoolService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/eventbus.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { EventbusService } from './eventbus.service'; 4 | 5 | describe('EventbusService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: EventbusService = TestBed.get(EventbusService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/eventbus.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | 16 | import { Injectable } from '@angular/core'; 17 | import { Subject } from 'rxjs'; 18 | 19 | export interface IEventType { 20 | type: string|number; 21 | data?: any; 22 | error?: string; 23 | } 24 | 25 | export enum EventType { 26 | socket_connected = 1, 27 | socket_disconnected, 28 | 29 | media_newPeer, 30 | media_peerClose, 31 | media_consumerClosed, 32 | media_consumerPaused, 33 | media_consumerResumed, 34 | media_newConsumer, 35 | media_consumerScore, 36 | 37 | chat_message, 38 | chat_emoji, 39 | 40 | popover_docSelectClosed, 41 | popover_settingClosed, 42 | popover_emojiClosed, 43 | popover_shareClosed, 44 | popover_moreClosed, 45 | popover_selectPencilClosed, 46 | popover_informationClosed, 47 | 48 | document_docSelect, 49 | document_docImport, 50 | document_syncDocInfo, 51 | 52 | pdftranscode_start, 53 | pdftranscode_end, 54 | pdftranscode_progress, 55 | 56 | class_start, 57 | class_stop, 58 | class_connectVideo, 59 | class_connectApproval, 60 | class_disconnectVideo, 61 | class_announcementTextChange, 62 | class_muted, 63 | class_unmuted, 64 | class_roomUpdate, 65 | 66 | peer_changeRoler, 67 | 68 | draw_action, 69 | 70 | main_switchComponent, 71 | } 72 | 73 | @Injectable({ 74 | providedIn: 'root' 75 | }) 76 | export class EventbusService { 77 | public socket$ = new Subject(); 78 | public media$ = new Subject(); 79 | public chat$ = new Subject(); 80 | public popover$ = new Subject(); 81 | public pdftranscode$ = new Subject(); 82 | public document$ = new Subject(); 83 | public class$ = new Subject(); 84 | public draw$ = new Subject(); 85 | public main$ = new Subject(); 86 | public peer$ = new Subject(); 87 | 88 | constructor() { } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/app/service/i18n.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { I18nService } from './i18n.service'; 4 | 5 | describe('I18nService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: I18nService = TestBed.get(I18nService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/logger.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { LoggerService } from './logger.service'; 4 | 5 | describe('LoggerService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: LoggerService = TestBed.get(LoggerService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/logger.service.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import { Injectable } from '@angular/core'; 16 | import debug from 'debug'; 17 | 18 | const APP_NAME = 'WiLearning'; 19 | localStorage.debug = 'mediasoup-client:* WiLearning:*'; 20 | 21 | @Injectable({ 22 | providedIn: 'root' 23 | }) 24 | export class LoggerService { 25 | private pDebug: any; 26 | private pWarn: any; 27 | private pError: any; 28 | 29 | constructor( 30 | ) { 31 | this.pDebug = debug(`${APP_NAME}:DEBUG`); 32 | this.pWarn = debug(`${APP_NAME}:WARN`); 33 | this.pError = debug(`${APP_NAME}:ERROR`); 34 | 35 | // tslint:disable-next-line: no-console 36 | this.pDebug.log = console.info.bind(console); 37 | this.pWarn.log = console.warn.bind(console); 38 | this.pError.log = console.error.bind(console); 39 | } 40 | 41 | get debug() { 42 | return this.pDebug; 43 | } 44 | 45 | get warn() { 46 | return this.pWarn; 47 | } 48 | 49 | get error() { 50 | return this.pError; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/app/service/media.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { MediaService } from './media.service'; 4 | 5 | describe('MediaService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: MediaService = TestBed.get(MediaService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/peer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { PeerService } from './peer.service'; 4 | describe('PeerService', () => { 5 | beforeEach(() => TestBed.configureTestingModule({ 6 | providers: [ 7 | PeerService 8 | ], 9 | imports: [ 10 | ] 11 | })); 12 | 13 | it('should be created', () => { 14 | const service: PeerService = TestBed.get(PeerService); 15 | expect(service).toBeTruthy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /app/src/app/service/profile.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ProfileService } from './profile.service'; 4 | 5 | describe('ProfileService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ProfileService = TestBed.get(ProfileService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/sharemedia.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SharemediaService } from './sharemedia.service'; 4 | 5 | describe('SharemediaService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: SharemediaService = TestBed.get(SharemediaService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/sharemedia.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { LoggerService } from './logger.service'; 3 | import { PeerService } from './peer.service'; 4 | import {WlMedia, WlMediaSource} from '../defines'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class SharemediaService { 10 | shareVideoStream: MediaStream; 11 | videoElement: HTMLVideoElement; 12 | 13 | constructor( 14 | private logger: LoggerService, 15 | private peer: PeerService, 16 | ) { 17 | } 18 | 19 | 20 | exportVideo(videoElement) { 21 | const media = videoElement.captureStream(); 22 | this.logger.debug('export media: %o.', media); 23 | 24 | this.peer.produceVideo(media, WlMediaSource.media); 25 | this.peer.produceAudio(media, WlMediaSource.media); 26 | 27 | this.shareVideoStream = media; 28 | } 29 | 30 | close() { 31 | this.peer.stopProduceStream(this.shareVideoStream as WlMedia); 32 | document.getElementById('sharemedia').removeChild(this.videoElement); 33 | this.shareVideoStream = null; 34 | this.videoElement = null; 35 | } 36 | 37 | fileChange(event) { 38 | this.logger.debug(event); 39 | 40 | const file = event.target.files[0] as File; 41 | if ( !file ) { 42 | return; 43 | } 44 | 45 | const shareObjSrc = URL.createObjectURL(file); 46 | this.videoElement = document.createElement('video'); 47 | this.videoElement.src = shareObjSrc; 48 | this.videoElement.loop = true; 49 | this.videoElement.load(); 50 | 51 | this.videoElement.addEventListener('loadeddata', () => { 52 | this.logger.debug('video %s loaded finished.', file.name); 53 | this.videoElement.muted = true; 54 | this.videoElement.play(); 55 | this.videoElement.controls = true; 56 | this.exportVideo(this.videoElement); 57 | }, true); 58 | 59 | document.getElementById('sharemedia').appendChild(this.videoElement); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/app/service/signaling.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { SignalingService } from './signaling.service'; 4 | 5 | describe('WebsocketService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: SignalingService = TestBed.get(SignalingService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/stats.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { StatsService } from './stats.service'; 4 | 5 | describe('StatsService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: StatsService = TestBed.get(StatsService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/service/wlhttp.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { WlhttpService } from './wlhttp.service'; 4 | 5 | describe('WlhttpService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: WlhttpService = TestBed.get(WlhttpService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/src/app/thumbnail/thumbnail.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/app/thumbnail/thumbnail.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/app/thumbnail/thumbnail.component.scss -------------------------------------------------------------------------------- /app/src/app/thumbnail/thumbnail.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { ThumbnailComponent } from './thumbnail.component'; 5 | 6 | describe('ThumbnailComponent', () => { 7 | let component: ThumbnailComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ ThumbnailComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(ThumbnailComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/videoplayer/videoplayer.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | {{stream.peer.displayName}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
-------------------------------------------------------------------------------- /app/src/app/videoplayer/videoplayer.component.scss: -------------------------------------------------------------------------------- 1 | video { 2 | max-width: 100%; 3 | width: auto; 4 | height: auto; 5 | max-height: 33vh; 6 | } 7 | 8 | .container { 9 | position: relative; 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: center; 13 | } 14 | 15 | .controlbar { 16 | width: 100%; 17 | height: 32px; 18 | bottom: 0; 19 | position: absolute; 20 | z-index: 99; 21 | } -------------------------------------------------------------------------------- /app/src/app/videoplayer/videoplayer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { VideoplayerComponent } from './videoplayer.component'; 5 | 6 | describe('VideoplayerComponent', () => { 7 | let component: VideoplayerComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ VideoplayerComponent ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(VideoplayerComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/src/app/videoplayer/videoplayer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; 2 | import { WlMedia } from '../defines'; 3 | import { PeerService } from '../service/peer.service'; 4 | 5 | @Component({ 6 | selector: 'app-videoplayer', 7 | templateUrl: './videoplayer.component.html', 8 | styleUrls: ['./videoplayer.component.scss'], 9 | }) 10 | export class VideoplayerComponent implements OnInit { 11 | @Input() stream: WlMedia; 12 | @Input() toolbar = true; 13 | @Input() fullscreen = false; 14 | @ViewChild('videoplayer', {static: true}) videoplayer: ElementRef; 15 | 16 | constructor( 17 | public peer: PeerService, 18 | ) { } 19 | 20 | ngOnInit() { 21 | if (this.fullscreen) { 22 | (this.videoplayer.nativeElement as HTMLVideoElement).style.maxHeight = '100%'; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/assets/img/about/Archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/about/Archive.zip -------------------------------------------------------------------------------- /app/src/assets/img/about/austin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/about/austin.jpg -------------------------------------------------------------------------------- /app/src/assets/img/about/chicago.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/about/chicago.jpg -------------------------------------------------------------------------------- /app/src/assets/img/about/madison.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/about/madison.jpg -------------------------------------------------------------------------------- /app/src/assets/img/about/seattle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/about/seattle.jpg -------------------------------------------------------------------------------- /app/src/assets/img/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/appicon.png -------------------------------------------------------------------------------- /app/src/assets/img/appicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/assets/img/ica-slidebox-img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/ica-slidebox-img-1.png -------------------------------------------------------------------------------- /app/src/assets/img/ica-slidebox-img-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/ica-slidebox-img-2.png -------------------------------------------------------------------------------- /app/src/assets/img/ica-slidebox-img-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/ica-slidebox-img-3.png -------------------------------------------------------------------------------- /app/src/assets/img/ica-slidebox-img-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/ica-slidebox-img-4.png -------------------------------------------------------------------------------- /app/src/assets/img/ionic-logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/assets/img/speaker-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speaker-background.png -------------------------------------------------------------------------------- /app/src/assets/img/speakers/bear.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/bear.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/cheetah.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/cheetah.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/duck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/duck.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/eagle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/eagle.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/elephant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/elephant.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/giraffe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/giraffe.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/iguana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/iguana.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/kitten.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/lion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/lion.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/mouse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/mouse.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/puppy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/puppy.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/rabbit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/rabbit.jpg -------------------------------------------------------------------------------- /app/src/assets/img/speakers/turtle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/app/src/assets/img/speakers/turtle.jpg -------------------------------------------------------------------------------- /app/src/assets/img/videocam-off.svg: -------------------------------------------------------------------------------- 1 | 2 | ionicons-v5-g 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /app/src/global.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * App Global CSS 3 | * ---------------------------------------------------------------------------- 4 | * Put style rules here that you want to apply globally. These styles are for 5 | * the entire app and not just one component. Additionally, this file can be 6 | * used as an entry point to import other CSS/Sass files to be included in the 7 | * output CSS. 8 | * For more information on global stylesheets, visit the documentation: 9 | * https://ionicframework.com/docs/layout/global-stylesheets 10 | */ 11 | 12 | /* Core CSS required for Ionic components to work properly */ 13 | @import "~@ionic/angular/css/core.css"; 14 | 15 | /* Basic CSS for apps built with Ionic */ 16 | @import "~@ionic/angular/css/normalize.css"; 17 | @import "~@ionic/angular/css/structure.css"; 18 | @import "~@ionic/angular/css/typography.css"; 19 | 20 | /* Optional CSS utils that can be commented out */ 21 | @import "~@ionic/angular/css/padding.css"; 22 | @import "~@ionic/angular/css/float-elements.css"; 23 | @import "~@ionic/angular/css/text-alignment.css"; 24 | @import "~@ionic/angular/css/text-transformation.css"; 25 | @import "~@ionic/angular/css/flex-utils.css"; 26 | @import "~@ionic/angular/css/display.css"; 27 | 28 | 29 | .popoverDocselect { 30 | --min-width: 30%; 31 | --max-width: 100%; 32 | --max-height: 60%; 33 | } 34 | 35 | .my-custom-interface { 36 | --color: #5e3e2c; 37 | --color-hover: #362419; 38 | } 39 | 40 | .my-custom-interface .select-interface-option { 41 | color: red; 42 | --color: red; 43 | background: red; 44 | } 45 | 46 | 47 | 48 | /* 49 | * App CSS 50 | * ---------------------------------------------------------------------------- 51 | * Imports a file that can contain Sass/CSS that should be used throughout 52 | * the entire app. 53 | */ 54 | 55 | @import "./app/app.scss"; 56 | -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WiLearning 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | process.env.CHROME_BIN = require('puppeteer').executablePath(); 5 | 6 | module.exports = function (config) { 7 | config.set({ 8 | basePath: '', 9 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 10 | plugins: [ 11 | require('karma-jasmine'), 12 | require('karma-chrome-launcher'), 13 | require('karma-jasmine-html-reporter'), 14 | require('karma-coverage-istanbul-reporter'), 15 | require('@angular-devkit/build-angular/plugins/karma') 16 | ], 17 | client: { 18 | clearContext: false // leave Jasmine Spec Runner output visible in browser 19 | }, 20 | coverageIstanbulReporter: { 21 | dir: require('path').join(__dirname, 'coverage'), 22 | reports: ['html', 'lcovonly'], 23 | fixWebpackSourcePaths: true 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome', 'ChromeHeadless'], 31 | customLaunchers: { 32 | ChromeHeadlessCI: { 33 | base: 'ChromeHeadless', 34 | flags: ['--no-sandbox', '--disable-gpu'] 35 | } 36 | }, 37 | singleRun: false 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /app/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ionic Conference", 3 | "short_name": "Ionic Conference", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "icons": [ 7 | { 8 | "src": "assets/img/appicon.png", 9 | "sizes": "512x512", 10 | "type": "image/png" 11 | } 12 | ], 13 | "background_color": "#387ef5", 14 | "theme_color": "#387ef5" 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /app/src/zone-flags.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Prevents Angular change detection from 3 | * running with certain Web Component callbacks 4 | */ 5 | (window as any).__Zone_disable_customElements = true; 6 | -------------------------------------------------------------------------------- /app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "downlevelIteration": true, 4 | "importHelpers": true, 5 | "module": "esnext", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es2015", 13 | "skipLibCheck": true, 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/zone-flags.ts", 13 | "src/polyfills.ts" 14 | ], 15 | "include": [ 16 | "src/**/*.spec.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-define": true, 52 | "no-unused-variable": true, 53 | "no-var-requires": false, 54 | "object-literal-key-quotes": [ 55 | true, 56 | "as-needed" 57 | ], 58 | "object-literal-sort-keys": false, 59 | "ordered-imports": false, 60 | "quotemark": [ 61 | true, 62 | "single" 63 | ], 64 | "trailing-comma": false, 65 | "no-output-on-prefix": true, 66 | "no-inputs-metadata-property": true, 67 | "no-host-metadata-property": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-lifecycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "one-variable-per-declaration": false, 73 | "component-class-suffix": [true, "Page", "Component"], 74 | "directive-class-suffix": true, 75 | "directive-selector": [ 76 | true, 77 | "attribute", 78 | "app", 79 | "camelCase" 80 | ], 81 | "component-selector": [ 82 | true, 83 | "element", 84 | "app", 85 | "page", 86 | "kebab-case" 87 | ] 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | if command -v cnpm > /dev/null 2>&1; then 4 | alias npm_command='cnpm' 5 | elif command -v npm > /dev/null 2>&1; then 6 | alias npm_command='npm' 7 | fi 8 | 9 | npm_command -v > /dev/null 10 | if [ $? != 0 ];then 11 | echo "Please install nodejs first!" 12 | exit -1; 13 | fi 14 | 15 | node_version=$(node -v) 16 | echo "Node version: " $node_version 17 | greater=$(echo "$node_version v12"|tr " " "\n"|sort -r|head -n 1) 18 | 19 | if [ $greater = "v12" ];then 20 | echo "Please update nodejs, nodejs > v12 is required!" 21 | exit; 22 | fi 23 | 24 | 25 | mkdir -p dist 26 | #mkdir -p /var/run/wilearning/public/ 27 | 28 | # build server 29 | build_server() { 30 | cd server 31 | if [ ! -d "node_modules" ];then 32 | npm_command i 33 | fi 34 | 35 | rm -rf dist 36 | npm run build 37 | 38 | if [ $? != 0 ];then 39 | exit -1; 40 | fi 41 | 42 | cp -a dist/* ../dist/ 43 | ln -s $PWD/node_modules ../dist/ 44 | cd .. 45 | } 46 | 47 | # build app client 48 | build_app() { 49 | cd app 50 | if [ ! -d "node_modules" ];then 51 | npm_command i 52 | fi 53 | 54 | npm run build 55 | if [ $? != 0 ];then 56 | exit -1; 57 | fi 58 | 59 | cp -a www ../dist/app 60 | cd .. 61 | } 62 | 63 | # build web admin 64 | build_admin() { 65 | cd admin 66 | if [ ! -d "node_modules" ];then 67 | npm_command i 68 | fi 69 | 70 | npm run build 71 | if [ $? != 0 ];then 72 | exit -1; 73 | fi 74 | 75 | cp -a dist ../dist/admin 76 | cd .. 77 | } 78 | 79 | case "$1" in 80 | all) 81 | rm -rf dist/ 82 | mkdir -p dist 83 | build_server 84 | build_admin 85 | build_app 86 | ;; 87 | server) 88 | build_server 89 | ;; 90 | admin) 91 | rm -rf dist/admin 92 | build_admin 93 | ;; 94 | app) 95 | rm -rf dist/app 96 | build_app 97 | ;; 98 | *) 99 | echo 100 | echo "Usage: ./build.sh [all/admin/web/server]" 101 | echo 102 | ;; 103 | esac 104 | -------------------------------------------------------------------------------- /res/appreciate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/res/appreciate.png -------------------------------------------------------------------------------- /res/wi2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wistingcn/WiLearning/dd7c00909ec49b52a44104fb89e18f9a84bd4fe4/res/wi2.gif -------------------------------------------------------------------------------- /server/certs/rtc.liweix.com.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAshMOzuCXQE950mGC3cS/tGKuOECXBxVQ3yBzT/LY/3vfoENP 3 | UWp66o7uHAY63zu9Hwp/spBuL0CxISPOAbFcBuF5GNv0+kxc05wOdSGVuLq5lpD9 4 | Fj1ZEHr+5GqXc/y4c+8cGBaOUVr6mwBlnWCK0sYRoqOvsWlMyj4uKQqKDlR9NZCt 5 | OFrKd+cliqmh+wvh0hIGw7Ma/45MdMTR8YZ+npPjle0NgK8Ce9R10eam6NdcjRrL 6 | WT5imd7cy5QbH9uHECfKSuIS9vO94noCEwQ6QO0mz2+vOFdptq0z+JLDaqeLF76r 7 | i3LKE7zpvm79XAapb0PJp/IsKmCW+vFvL79sxwIDAQABAoIBAFefeRHjAYnrkY4r 8 | 3TEytwIT7JZRcf9SRdEFLriQTqaoVYmOlWECfCke25t+lUlzI8Q6bkt3NkCaHI6q 9 | B5xdoWLhX60Tzp+7I5IcoCCSvhGAEAI+pcpjtYeeff33VT8Fg93IMpLHxo/RrK7I 10 | qzedYbIFNHhLAxDRN6/smNopDXWu2x/nXokR68IJt4kAmZIxb4iv6ZQDvMaGeFDf 11 | TqcrMBauTJyTlL4tsFqi9ufxrWAb+j/2mbs6ZI15F+I6s+asVOerS9lcbdlQr94X 12 | t3/3hgsHqF8xVUCci/jyFbfza0i5IBCXDNFGtVSg4Ub792KoEYsIftMQAjuH6eC3 13 | kb+CfUECgYEA2nlB8v3mKO1nrGHQvvb5olZyQ5bJIiit0a7uD4roXuY4ZrPYpRIG 14 | J8k+P+Q77XXlXjd65VXnVVM9lV5tCFzt4fQMnUbtLDhgBVYiX6HVHVhZ59wwonSM 15 | jAAAya3IuYgM/URvXm6bg/KxZLffU7ksLYmbeZ86YBnVSeyogdeS0q0CgYEA0Klc 16 | xmsZVPuP2IbWc/tpDKjo14fAXj5OKrHWQU0zLsG0sK7DjVD7bRSd0cDjphCf2/IJ 17 | oCPSxE51zBFdAZp402AO09BCUtpHL0EGfZwvw4hhDDdwrXzXBe8Vd71ZKNgXBeez 18 | 6w3TOHLLM+1e1owO+LqVPtvTwxT4/+OIWhO+H8MCgYBTo8yNvIpa7Nid9Cy4j1Du 19 | x22jVpzWCQDSn8fgB2wCuNyq5Ptx5r6YiMDeMQCIXnRbZ54TIpoX3nBbOPVBGGKx 20 | +H7Dz52JKmQIZvtt5UTzoRl6XJ/58ejEyFOE4pVQAjgG1iZU3r+G+mjTiFllciTp 21 | oh71pvHwTLONQ58XZnJ9WQKBgQCPKBMZonLfxjO7Z08Mhald4SjWbjGBtxdj3VzU 22 | soVyB73TFiPd+Xh2LxXTyZWl4nP6r4UBFHLO8dGJr0PzDKrmwlmJ4rEOLOl3txIf 23 | 7Z8rdI64lfS9YhAX5x8AZq0cA51QFNZwZqzKZp+ZQbq2UaMX7NLmLGWNyv7GeLGc 24 | szzWhwKBgQCB9YQWjGBvfbj44I9o20gtfyAIV/21p541sKFwKwjpSwYrmXGkdKuv 25 | 5PsaOhOEtHLUy3TqqOTYc5mAA7VLJA8aiaw2R/MSxKDTEVSp6xrv57XdhHkE7cgt 26 | G+Hs4BoZ6hTKYDsTFvdfDU6iNCh/Gn0veFE8O9V4xwFbQzTbN7cjkg== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /server/config/config.ts: -------------------------------------------------------------------------------- 1 | export const lConfig = 2 | { 3 | listeningPort : 443, 4 | listeningRedirectPort : 80, 5 | 6 | worker: { 7 | logLevel : 'warn', 8 | rtcMinPort : 40000, 9 | rtcMaxPort : 49999 10 | }, 11 | 12 | webRtcTransport : { 13 | listenIps : [], 14 | maxIncomingBitrate : 350000, 15 | initialAvailableOutgoingBitrate : 200000 16 | }, 17 | 18 | databaseFile : '/var/run/wilearning/database', 19 | roomStatusInterval: 300, // seconds 20 | publicDirectory : '/var/run/wilearning/public/' 21 | }; 22 | -------------------------------------------------------------------------------- /server/log4js.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": { 3 | "access": { 4 | "type": "dateFile", 5 | "filename": "log/access.log", 6 | "pattern": "-yyyy-MM-dd", 7 | "category": "http" 8 | }, 9 | "app": { 10 | "type": "file", 11 | "filename": "log/app.log", 12 | "maxLogSize": 10485760, 13 | "numBackups": 3 14 | }, 15 | "errorFile": { 16 | "type": "file", 17 | "filename": "log/errors.log" 18 | }, 19 | "errors": { 20 | "type": "logLevelFilter", 21 | "level": "ERROR", 22 | "appender": "errorFile" 23 | }, 24 | "console" : {"type": "console"} 25 | }, 26 | "categories": { 27 | "default": { "appenders": [ "app", "errors", "console" ], "level": "DEBUG" }, 28 | "http": { "appenders": [ "access"], "level": "DEBUG" } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/model/model.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 liwei 3 | * 4 | * This program is free software: you can use, redistribute, and/or modify 5 | * it under the terms of the GNU Affero General Public License, version 3 6 | * or later ("AGPL"), as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. 11 | * 12 | * You should have received a copy of the GNU Affero General Public License 13 | * along with this program. If not, see . 14 | */ 15 | import {Entity, PrimaryColumn, PrimaryGeneratedColumn, Column, BaseEntity, OneToMany, ManyToOne} from 'typeorm'; 16 | 17 | @Entity() 18 | export class ClaRoom extends BaseEntity{ 19 | @PrimaryColumn() 20 | id: string; 21 | 22 | @Column() 23 | name: string; 24 | 25 | @Column() 26 | speakerPassword: string; 27 | 28 | @Column() 29 | attendeePassword: string; 30 | 31 | @Column("text") 32 | description: string; 33 | 34 | @Column() 35 | createTime: string; 36 | 37 | @Column() 38 | lastActiveTime: string; 39 | } 40 | 41 | @Entity() 42 | export class ClaDocs extends BaseEntity{ 43 | @PrimaryGeneratedColumn() 44 | id: string; 45 | 46 | @Column() 47 | roomId: string; 48 | 49 | @Column() 50 | fileName: string; 51 | 52 | @Column() 53 | uploadTime: string; 54 | 55 | @OneToMany(type => ClaDocPages, page => page.doc) 56 | pages: ClaDocPages[]; 57 | } 58 | 59 | @Entity() 60 | export class ClaDocPages extends BaseEntity{ 61 | @PrimaryGeneratedColumn() 62 | id: string; 63 | 64 | @Column() 65 | page: number; 66 | 67 | @Column() 68 | path: string; 69 | 70 | @ManyToOne(type => ClaDocs, doc=>doc.pages) 71 | doc: ClaDocs; 72 | } 73 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wilearning", 3 | "version": "2.0.0", 4 | "scripts": { 5 | "start": "ts-node server.ts --cert ./certs/rtc.liweix.com.pem --key ./certs/rtc.liweix.com.key", 6 | "build": "tsc; cp -a start.sh package.json log4js.json certs dist/", 7 | "pack": "webpack" 8 | }, 9 | "private": true, 10 | "description": "WiLearning is a dedicated cloud for telecommuting and video conferencing, born for privacy and security.", 11 | "author": "liwei", 12 | "license": "AGPL", 13 | "main": "server.ts", 14 | "dependencies": { 15 | "base-64": "^0.1.0", 16 | "body-parser": "^1.19.0", 17 | "color": "^3.1.2", 18 | "compression": "^1.7.4", 19 | "cookie-parser": "^1.4.4", 20 | "cors": "^2.8.5", 21 | "crypto-js": "^4.0.0", 22 | "debug": "^4.1.1", 23 | "express": "^4.17.1", 24 | "express-session": "^1.17.0", 25 | "got": "^10.5.5", 26 | "helmet": "^3.21.2", 27 | "log4js": "^6.3.0", 28 | "mediasoup": "^3.6.13", 29 | "morgan": "^1.9.1", 30 | "multer": "^1.4.2", 31 | "pm2": "^4.5.0", 32 | "socket.io": "^2.3.0", 33 | "sqlite3": "^4.1.1", 34 | "ts-node": "^8.8.1", 35 | "typeorm": "^0.2.22", 36 | "yargs": "^15.1.0" 37 | }, 38 | "devDependencies": { 39 | "@types/express": "^4.17.2", 40 | "@types/yargs": "^15.0.3", 41 | "@types/node": "^13.1.2", 42 | "@types/socket.io": "^2.1.4", 43 | "@types/got": "^9.6.9", 44 | "ts-loader": "^6.2.1", 45 | "typescript": "^3.6.4", 46 | "webpack": "^4.41.6", 47 | "webpack-cli": "^3.3.11", 48 | "webpack-node-externals": "^1.7.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /server/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DEBUG='classvideo-server:* mediasoup:*' 3 | 4 | if [ -f "server.js" ] 5 | then 6 | node server.js --cert certs/rtc.liweix.com.pem --key certs/rtc.liweix.com.key; 7 | elif [ -f "server.ts" ] 8 | then 9 | ts-node server.ts --cert certs/rtc.liweix.com.pem --key certs/rtc.liweix.com.key 10 | else 11 | echo 12 | echo "Do not find executable file" 13 | fi 14 | -------------------------------------------------------------------------------- /server/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const nodeExternals = require('webpack-node-externals'); 3 | 4 | module.exports = { 5 | mode: 'production', 6 | entry: './server.ts', 7 | module: { 8 | rules: [ 9 | { 10 | use: 'ts-loader', 11 | exclude: /node_modules/, 12 | }, 13 | ], 14 | }, 15 | resolve: { 16 | extensions: ['.ts', '.js' ], 17 | }, 18 | output: { 19 | filename: 'classvideo-server.js', 20 | path: path.resolve(__dirname, 'dist'), 21 | }, 22 | target: 'node', 23 | externals: [nodeExternals()], 24 | }; 25 | --------------------------------------------------------------------------------