├── .gitattributes ├── .gitmodules ├── README.md ├── aulas ├── aula01 │ ├── app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── assets │ │ │ ├── avatars │ │ │ │ ├── erick.jpeg │ │ │ │ ├── ivete-souza.png │ │ │ │ ├── mariazinha.png │ │ │ │ └── ze-silva.png │ │ │ ├── favicon.jpeg │ │ │ ├── icons │ │ │ │ ├── asterisk.svg │ │ │ │ ├── bell.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── envelope.svg │ │ │ │ ├── file.svg │ │ │ │ ├── github.svg │ │ │ │ ├── hand-solid.svg │ │ │ │ ├── hand.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── search.svg │ │ │ │ ├── team.svg │ │ │ │ └── worldwide.svg │ │ │ └── printscreen │ │ │ │ ├── clubhouse-home.PNG │ │ │ │ ├── clubhouse-login.PNG │ │ │ │ └── clubhouse-room.PNG │ │ ├── css │ │ │ ├── colors.css │ │ │ ├── reset.css │ │ │ └── styles.css │ │ ├── deps │ │ │ └── polyfill.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ └── pages │ │ │ ├── _shared │ │ │ ├── constants.js │ │ │ └── socketBuilder.js │ │ │ ├── lobby │ │ │ └── index.html │ │ │ ├── login │ │ │ └── index.html │ │ │ └── room │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── index.js │ │ │ └── util │ │ │ └── roomSocket.js │ └── server │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── controllers │ │ └── roomsController.js │ │ ├── entities │ │ ├── attendee.js │ │ └── room.js │ │ ├── index.js │ │ └── util │ │ ├── constants.js │ │ └── socket.js ├── aula02 │ ├── app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── assets │ │ │ ├── avatars │ │ │ │ ├── erick.jpeg │ │ │ │ ├── ivete-souza.png │ │ │ │ ├── mariazinha.png │ │ │ │ └── ze-silva.png │ │ │ ├── favicon.jpeg │ │ │ ├── icons │ │ │ │ ├── asterisk.svg │ │ │ │ ├── bell.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── envelope.svg │ │ │ │ ├── file.svg │ │ │ │ ├── github.svg │ │ │ │ ├── hand-solid.svg │ │ │ │ ├── hand.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── search.svg │ │ │ │ ├── team.svg │ │ │ │ └── worldwide.svg │ │ │ └── printscreen │ │ │ │ ├── clubhouse-home.PNG │ │ │ │ ├── clubhouse-login.PNG │ │ │ │ └── clubhouse-room.PNG │ │ ├── css │ │ │ ├── colors.css │ │ │ ├── reset.css │ │ │ └── styles.css │ │ ├── deps │ │ │ └── polyfill.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ └── pages │ │ │ ├── _shared │ │ │ ├── constants.js │ │ │ └── socketBuilder.js │ │ │ ├── lobby │ │ │ ├── index.html │ │ │ └── src │ │ │ │ ├── controller.js │ │ │ │ ├── entities │ │ │ │ └── room.js │ │ │ │ ├── index.js │ │ │ │ ├── templates │ │ │ │ └── lobbyItem.js │ │ │ │ ├── util │ │ │ │ └── lobbySocketBuilder.js │ │ │ │ └── view.js │ │ │ ├── login │ │ │ └── index.html │ │ │ └── room │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── controller.js │ │ │ ├── entities │ │ │ └── attendee.js │ │ │ ├── index.js │ │ │ ├── templates │ │ │ └── attendeeTemplate.js │ │ │ ├── util │ │ │ └── roomSocket.js │ │ │ └── view.js │ └── server │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── controllers │ │ ├── lobbyController.js │ │ └── roomsController.js │ │ ├── entities │ │ ├── attendee.js │ │ └── room.js │ │ ├── index.js │ │ └── util │ │ ├── constants.js │ │ └── socket.js ├── aula03 │ ├── app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── assets │ │ │ ├── avatars │ │ │ │ ├── erick.jpeg │ │ │ │ ├── ivete-souza.png │ │ │ │ ├── mariazinha.png │ │ │ │ └── ze-silva.png │ │ │ ├── favicon.jpeg │ │ │ ├── icons │ │ │ │ ├── asterisk.svg │ │ │ │ ├── bell.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── envelope.svg │ │ │ │ ├── file.svg │ │ │ │ ├── github.svg │ │ │ │ ├── hand-solid.svg │ │ │ │ ├── hand.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── search.svg │ │ │ │ ├── team.svg │ │ │ │ └── worldwide.svg │ │ │ └── printscreen │ │ │ │ ├── clubhouse-home.PNG │ │ │ │ ├── clubhouse-login.PNG │ │ │ │ └── clubhouse-room.PNG │ │ ├── css │ │ │ ├── colors.css │ │ │ ├── reset.css │ │ │ └── styles.css │ │ ├── deps │ │ │ └── polyfill.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ └── pages │ │ │ ├── _shared │ │ │ ├── constants.js │ │ │ ├── media.js │ │ │ ├── peerBuilder.js │ │ │ └── socketBuilder.js │ │ │ ├── lobby │ │ │ ├── index.html │ │ │ └── src │ │ │ │ ├── controller.js │ │ │ │ ├── entities │ │ │ │ └── room.js │ │ │ │ ├── index.js │ │ │ │ ├── templates │ │ │ │ └── lobbyItem.js │ │ │ │ ├── util │ │ │ │ └── lobbySocketBuilder.js │ │ │ │ └── view.js │ │ │ ├── login │ │ │ └── index.html │ │ │ └── room │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── controller.js │ │ │ ├── entities │ │ │ ├── attendee.js │ │ │ └── userStream.js │ │ │ ├── index.js │ │ │ ├── service.js │ │ │ ├── templates │ │ │ └── attendeeTemplate.js │ │ │ ├── util │ │ │ └── roomSocket.js │ │ │ └── view.js │ └── server │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── controllers │ │ ├── lobbyController.js │ │ └── roomsController.js │ │ ├── entities │ │ ├── attendee.js │ │ └── room.js │ │ ├── index.js │ │ └── util │ │ ├── constants.js │ │ ├── customMap.js │ │ └── socket.js ├── aula04 │ ├── app │ │ ├── .gitignore │ │ ├── README.md │ │ ├── assets │ │ │ ├── avatars │ │ │ │ ├── erick.jpeg │ │ │ │ ├── ivete-souza.png │ │ │ │ ├── mariazinha.png │ │ │ │ └── ze-silva.png │ │ │ ├── favicon.jpeg │ │ │ ├── icons │ │ │ │ ├── asterisk.svg │ │ │ │ ├── bell.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── envelope.svg │ │ │ │ ├── file.svg │ │ │ │ ├── github.svg │ │ │ │ ├── hand-solid.svg │ │ │ │ ├── hand.svg │ │ │ │ ├── lock.svg │ │ │ │ ├── search.svg │ │ │ │ ├── team.svg │ │ │ │ └── worldwide.svg │ │ │ └── printscreen │ │ │ │ ├── clubhouse-home.PNG │ │ │ │ ├── clubhouse-login.PNG │ │ │ │ └── clubhouse-room.PNG │ │ ├── css │ │ │ ├── colors.css │ │ │ ├── reset.css │ │ │ └── styles.css │ │ ├── deps │ │ │ └── polyfill.js │ │ ├── index.html │ │ ├── package-lock.json │ │ ├── package.json │ │ └── pages │ │ │ ├── _shared │ │ │ ├── constants.js │ │ │ ├── media.js │ │ │ ├── peerBuilder.js │ │ │ └── socketBuilder.js │ │ │ ├── lobby │ │ │ ├── index.html │ │ │ └── src │ │ │ │ ├── controller.js │ │ │ │ ├── entities │ │ │ │ └── room.js │ │ │ │ ├── index.js │ │ │ │ ├── templates │ │ │ │ └── lobbyItem.js │ │ │ │ ├── util │ │ │ │ └── lobbySocketBuilder.js │ │ │ │ └── view.js │ │ │ ├── login │ │ │ └── index.html │ │ │ └── room │ │ │ ├── index.html │ │ │ └── src │ │ │ ├── controller.js │ │ │ ├── entities │ │ │ ├── attendee.js │ │ │ └── userStream.js │ │ │ ├── index.js │ │ │ ├── service.js │ │ │ ├── templates │ │ │ └── attendeeTemplate.js │ │ │ ├── util │ │ │ └── roomSocket.js │ │ │ └── view.js │ └── server │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── controllers │ │ ├── lobbyController.js │ │ └── roomsController.js │ │ ├── entities │ │ ├── attendee.js │ │ └── room.js │ │ ├── index.js │ │ └── util │ │ ├── constants.js │ │ ├── customMap.js │ │ └── socket.js └── aula05 │ ├── app │ ├── .gitignore │ ├── README.md │ ├── assets │ │ ├── avatars │ │ │ ├── erick.jpeg │ │ │ ├── ivete-souza.png │ │ │ ├── mariazinha.png │ │ │ └── ze-silva.png │ │ ├── favicon.jpeg │ │ ├── icons │ │ │ ├── asterisk.svg │ │ │ ├── bell.svg │ │ │ ├── calendar.svg │ │ │ ├── envelope.svg │ │ │ ├── file.svg │ │ │ ├── github.svg │ │ │ ├── hand-solid.svg │ │ │ ├── hand.svg │ │ │ ├── lock.svg │ │ │ ├── search.svg │ │ │ ├── team.svg │ │ │ └── worldwide.svg │ │ └── printscreen │ │ │ ├── clubhouse-home.PNG │ │ │ ├── clubhouse-login.PNG │ │ │ └── clubhouse-room.PNG │ ├── css │ │ ├── colors.css │ │ ├── reset.css │ │ └── styles.css │ ├── deps │ │ └── polyfill.js │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── pages │ │ ├── _shared │ │ ├── constants.js │ │ ├── media.js │ │ ├── peerBuilder.js │ │ ├── socketBuilder.js │ │ └── userDb.js │ │ ├── lobby │ │ ├── index.html │ │ └── src │ │ │ ├── controller.js │ │ │ ├── entities │ │ │ └── room.js │ │ │ ├── index.js │ │ │ ├── templates │ │ │ └── lobbyItem.js │ │ │ ├── util │ │ │ └── lobbySocketBuilder.js │ │ │ └── view.js │ │ ├── login │ │ ├── index.html │ │ └── src │ │ │ └── app.js │ │ └── room │ │ ├── index.html │ │ └── src │ │ ├── controller.js │ │ ├── entities │ │ ├── attendee.js │ │ └── userStream.js │ │ ├── index.js │ │ ├── service.js │ │ ├── templates │ │ └── attendeeTemplate.js │ │ ├── util │ │ └── roomSocket.js │ │ └── view.js │ ├── peerjs-server │ ├── package-lock.json │ └── package.json │ └── server │ ├── package-lock.json │ ├── package.json │ └── src │ ├── controllers │ ├── lobbyController.js │ └── roomsController.js │ ├── entities │ ├── attendee.js │ └── room.js │ ├── index.js │ └── util │ ├── constants.js │ ├── customMap.js │ └── socket.js └── welcome.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-detectable=true 2 | *.html linguist-detectable=false 3 | *.css linguist-detectable=false 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "initial-template"] 2 | path = initial-template 3 | url = https://github.com/ErickWendel/semanajsexpert-clubhouse-template.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [ ] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [ ] Deve ter login com GitHub 26 | - [ ] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [ ] Mostra todas as salas ativas 31 | - [ ] Atualiza salas em realtime 32 | - [ ] Pode criar uma sala sem topico 33 | - [ ] Pode criar uma sala com topico 34 | - [ ] Pode acessar salas ativas 35 | - Room 36 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [ ] Cria uma sala com um usuário dono 38 | - [ ] Todos usuários futuros entram com perfil de attendees 39 | - [ ] Notifica Lobby sobre atualizações na sala 40 | - [ ] Lista usuarios com perfis de speakers e attendees 41 | - [ ] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [ ] Recebe notificação de attendees para se tornarem speakers 45 | - [ ] Atualizam a tela o upgrade de attendee para speaker 46 | - [ ] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [ ] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [ ] Promove o speaker mais velho da sala 51 | - [ ] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [ ] Pode ouvir speakers ativos 54 | - [ ] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [ ] Reinicia todas as suas chamas ativas com os usuarios da sala 57 | - [ ] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula01/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /aulas/aula01/app/README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [ ] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [ ] Deve ter login com GitHub 26 | - [ ] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [ ] Mostra todas as salas ativas 31 | - [ ] Atualiza salas em realtime 32 | - [ ] Pode criar uma sala sem topico 33 | - [ ] Pode criar uma sala com topico 34 | - [ ] Pode acessar salas ativas 35 | - Room 36 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [x] Cria uma sala com um usuário dono 38 | - [x] Todos usuários futuros entram com perfil de attendees 39 | - [ ] Notifica Lobby sobre atualizações na sala 40 | - [ ] Lista usuarios com perfis de speakers e attendees 41 | - [ ] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [ ] Recebe notificação de attendees para se tornarem speakers 45 | - [ ] Atualizam a tela o upgrade de attendee para speaker 46 | - [ ] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [ ] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [ ] Promove o speaker mais velho da sala 51 | - [ ] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [ ] Pode ouvir speakers ativos 54 | - [ ] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [ ] Reinicia todas as suas chamas ativas com os usuarios da sala 57 | - [ ] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/avatars/erick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/avatars/erick.jpeg -------------------------------------------------------------------------------- /aulas/aula01/app/assets/avatars/ivete-souza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/avatars/ivete-souza.png -------------------------------------------------------------------------------- /aulas/aula01/app/assets/avatars/mariazinha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/avatars/mariazinha.png -------------------------------------------------------------------------------- /aulas/aula01/app/assets/avatars/ze-silva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/avatars/ze-silva.png -------------------------------------------------------------------------------- /aulas/aula01/app/assets/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/favicon.jpeg -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/bell.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/hand-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula01/app/assets/printscreen/clubhouse-home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/printscreen/clubhouse-home.PNG -------------------------------------------------------------------------------- /aulas/aula01/app/assets/printscreen/clubhouse-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/printscreen/clubhouse-login.PNG -------------------------------------------------------------------------------- /aulas/aula01/app/assets/printscreen/clubhouse-room.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula01/app/assets/printscreen/clubhouse-room.PNG -------------------------------------------------------------------------------- /aulas/aula01/app/css/colors.css: -------------------------------------------------------------------------------- 1 | /* Clubhouse color palette */ 2 | 3 | :root { 4 | --primary-color: #F2EFE4; 5 | --white-color: #ffffff; 6 | --graphite-color: #4e4d49; 7 | --graphite-dark-color: #252523; 8 | --graphite-light-color: #b4b3b3; 9 | --gray-color: #ececec; 10 | --ice-color: #dddddd; 11 | --button-green-color: #4FAF62; 12 | --red-color: #f06034; 13 | --button-blue-color: #6495ed; 14 | --modal-shadow-color: rgba(0, 0, 0, 0.4); 15 | --github-color: #24292e; 16 | } -------------------------------------------------------------------------------- /aulas/aula01/app/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /aulas/aula01/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aulas/aula01/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erickwendel/clubhouse-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx http-server .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "devDependencies": { 11 | "http-server": "^0.12.3" 12 | }, 13 | "keywords": [], 14 | "author": "erickwendel", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /aulas/aula01/app/pages/_shared/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | socketUrl: 'http://localhost:3000', 3 | socketNamespaces: { 4 | room: 'room', 5 | lobby: 'lobby' 6 | }, 7 | events: { 8 | USER_CONNECTED: 'userConnection', 9 | USER_DISCONNECTED: 'userDisconnection', 10 | 11 | JOIN_ROOM: 'joinRoom', 12 | LOBBY_UPDATED: 'lobbyUpdated' 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /aulas/aula01/app/pages/_shared/socketBuilder.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | 4 | export default class SocketBuilder { 5 | constructor({ socketUrl, namespace }) { 6 | this.socketUrl = `${socketUrl}/${namespace}` 7 | 8 | this.onUserConnected = () => { } 9 | this.onUserDisconnected = () => { } 10 | } 11 | 12 | setOnUserConnected(fn) { 13 | this.onUserConnected = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserDisconnected(fn) { 19 | this.onUserDisconnected = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = globalThis.io.connect(this.socketUrl, { 26 | withCredentials: false 27 | }) 28 | 29 | socket.on('connection', () => console.log('conectei!')) 30 | 31 | socket.on(constants.events.USER_CONNECTED, this.onUserConnected) 32 | socket.on(constants.events.USER_DISCONNECTED, this.onUserDisconnected) 33 | 34 | return socket 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula01/app/pages/login/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clubhouse - Semana JSExpert 9 | 10 | 11 | 12 | 13 | 14 |
15 |

16 | 🎉 17 | Welcome! 18 |

19 |

20 | We're working hard to get Clubhouse ready for everyone! While we wrap up the finishing touches, we're adding people gradually to make sure nothing breaks. :) 21 |

22 |

23 | Anyone can join with an invite from an existing user -- or reserve your username and we'll text you if you have a friend on the app who can let you in. We are so grateful you're here and can't wait to have you join us! 24 | 🙏 25 |

26 | 30 | 31 |
32 | 33 | console.log('user connected!', user)) 12 | .setOnUserDisconnected((user) => console.log('user disconnected!', user)) 13 | .setOnRoomUpdated((room) => console.log('room list!', room)) 14 | .build() 15 | 16 | const room = { 17 | id: '0001', 18 | topic: 'JS Expert éh noix' 19 | } 20 | 21 | const user = { 22 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 23 | username: 'Erick ' + Date.now() 24 | } 25 | 26 | socket.emit(constants.events.JOIN_ROOM, { user, room }) -------------------------------------------------------------------------------- /aulas/aula01/app/pages/room/src/util/roomSocket.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class RoomSocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onRoomUpdated = () => { } 9 | } 10 | 11 | setOnRoomUpdated(fn) { 12 | this.onRoomUpdated = fn 13 | 14 | return this 15 | } 16 | 17 | build() { 18 | const socket = super.build() 19 | 20 | socket.on(constants.events.LOBBY_UPDATED, this.onRoomUpdated) 21 | 22 | return socket; 23 | } 24 | } -------------------------------------------------------------------------------- /aulas/aula01/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/index.js", 9 | "dev": "npx nodemon src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "socket.io": "^4.0.2" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /aulas/aula01/server/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.username = username 5 | this.img = img 6 | this.isSpeaker = isSpeaker 7 | this.roomId = roomId 8 | this.peerId = peerId 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula01/server/src/entities/room.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./attendee.js" 2 | 3 | export default class Room { 4 | constructor({ id, topic, attendeesCount, speakersCount, featuredAttendees, owner, users }) { 5 | 6 | this.id = id 7 | this.topic = topic 8 | this.attendeesCount = attendeesCount 9 | this.speakersCount = speakersCount 10 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 11 | this.owner = new Attendee(owner) 12 | this.users = users 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula01/server/src/index.js: -------------------------------------------------------------------------------- 1 | import RoomsController from "./controllers/roomsController.js"; 2 | import SocketServer from "./util/socket.js"; 3 | import Event from 'events' 4 | import { constants } from './util/constants.js' 5 | 6 | const port = process.env.PORT || 3000 7 | const socketServer = new SocketServer({ port }) 8 | const server = await socketServer.start() 9 | 10 | const roomsController = new RoomsController() 11 | 12 | const namespaces = { 13 | room: { controller: roomsController, eventEmitter: new Event() } 14 | } 15 | 16 | // namespaces.room.eventEmitter.on( 17 | // 'userConnected', 18 | // namespaces.room.controller.onNewConnection.bind(namespaces.room.controller) 19 | // ) 20 | 21 | // namespaces.room.eventEmitter.emit('userConnected', { id: '001' }) 22 | // namespaces.room.eventEmitter.emit('userConnected', { id: '002' }) 23 | // namespaces.room.eventEmitter.emit('userConnected', { id: '003' }) 24 | 25 | const routeConfig = Object.entries(namespaces) 26 | .map(([namespace, { controller, eventEmitter }]) => { 27 | const controllerEvents = controller.getEvents() 28 | eventEmitter.on( 29 | constants.event.USER_CONNECTED, 30 | controller.onNewConnection.bind(controller) 31 | ) 32 | 33 | return { 34 | [namespace]: { events: controllerEvents, eventEmitter } 35 | } 36 | 37 | }) 38 | 39 | 40 | socketServer.attachEvents({ routeConfig }) 41 | 42 | console.log('socket server is running at', server.address().port) -------------------------------------------------------------------------------- /aulas/aula01/server/src/util/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | event: { 3 | USER_CONNECTED: 'userConnection', 4 | USER_DISCONNECTED: 'userDisconnection', 5 | 6 | JOIN_ROOM: 'joinRoom', 7 | 8 | LOBBY_UPDATED: 'lobbyUpdated' 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula01/server/src/util/socket.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { Server } from 'socket.io' 3 | import { constants } from './constants.js' 4 | 5 | export default class SocketServer { 6 | #io 7 | constructor({ port }) { 8 | this.port = port 9 | this.namespaces = {} 10 | } 11 | // [ 12 | // { 13 | // room: { 14 | // events, 15 | // eventEmitter 16 | // } 17 | // } 18 | // ] 19 | 20 | attachEvents({ routeConfig }) { 21 | for (const routes of routeConfig) { 22 | for (const [namespace, { events, eventEmitter }] of Object.entries(routes)) { 23 | const route = this.namespaces[namespace] = this.#io.of(`/${namespace}`) 24 | route.on('connection', socket => { 25 | for (const [functionName, functionValue] of events) { 26 | socket.on(functionName, (...args) => functionValue(socket, ...args)) 27 | } 28 | 29 | eventEmitter.emit(constants.event.USER_CONNECTED, socket) 30 | }) 31 | } 32 | } 33 | } 34 | 35 | async start() { 36 | const server = http.createServer((request, response) => { 37 | response.writeHead(200, { 38 | 'Access-Control-Allow-Origin': '*', 39 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' 40 | }) 41 | 42 | response.end('hey there!!') 43 | }) 44 | 45 | this.#io = new Server(server, { 46 | cors: { 47 | origin: '*', 48 | credentials: false 49 | } 50 | }) 51 | 52 | 53 | 54 | return new Promise((resolve, reject) => { 55 | server.on('error', reject) 56 | 57 | server.listen(this.port, () => resolve(server)) 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /aulas/aula02/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /aulas/aula02/app/README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [ ] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [ ] Deve ter login com GitHub 26 | - [ ] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [x] Mostra todas as salas ativas 31 | - [ ] Atualiza salas em realtime 32 | - [x] Pode criar uma sala sem topico 33 | - [x] Pode criar uma sala com topico 34 | - [x] Pode acessar salas ativas 35 | - Room 36 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [x] Cria uma sala com um usuário dono 38 | - [x] Todos usuários futuros entram com perfil de attendees 39 | - [ ] Notifica Lobby sobre atualizações na sala 40 | - [x] Lista usuarios com perfis de speakers e attendees 41 | - [X] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [ ] Recebe notificação de attendees para se tornarem speakers 45 | - [X] Atualizam a tela o upgrade de attendee para speaker 46 | - [ ] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [ ] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [X] Promove o speaker mais velho da sala 51 | - [X] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [ ] Pode ouvir speakers ativos 54 | - [ ] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [ ] Reinicia todas as suas chamas ativas com os usuarios da sala 57 | - [ ] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/avatars/erick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/avatars/erick.jpeg -------------------------------------------------------------------------------- /aulas/aula02/app/assets/avatars/ivete-souza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/avatars/ivete-souza.png -------------------------------------------------------------------------------- /aulas/aula02/app/assets/avatars/mariazinha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/avatars/mariazinha.png -------------------------------------------------------------------------------- /aulas/aula02/app/assets/avatars/ze-silva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/avatars/ze-silva.png -------------------------------------------------------------------------------- /aulas/aula02/app/assets/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/favicon.jpeg -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/bell.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/hand-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula02/app/assets/printscreen/clubhouse-home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/printscreen/clubhouse-home.PNG -------------------------------------------------------------------------------- /aulas/aula02/app/assets/printscreen/clubhouse-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/printscreen/clubhouse-login.PNG -------------------------------------------------------------------------------- /aulas/aula02/app/assets/printscreen/clubhouse-room.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula02/app/assets/printscreen/clubhouse-room.PNG -------------------------------------------------------------------------------- /aulas/aula02/app/css/colors.css: -------------------------------------------------------------------------------- 1 | /* Clubhouse color palette */ 2 | 3 | :root { 4 | --primary-color: #F2EFE4; 5 | --white-color: #ffffff; 6 | --graphite-color: #4e4d49; 7 | --graphite-dark-color: #252523; 8 | --graphite-light-color: #b4b3b3; 9 | --gray-color: #ececec; 10 | --ice-color: #dddddd; 11 | --button-green-color: #4FAF62; 12 | --red-color: #f06034; 13 | --button-blue-color: #6495ed; 14 | --modal-shadow-color: rgba(0, 0, 0, 0.4); 15 | --github-color: #24292e; 16 | } -------------------------------------------------------------------------------- /aulas/aula02/app/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /aulas/aula02/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aulas/aula02/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erickwendel/clubhouse-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx http-server .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "devDependencies": { 11 | "http-server": "^0.12.3" 12 | }, 13 | "keywords": [], 14 | "author": "erickwendel", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /aulas/aula02/app/pages/_shared/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | socketUrl: 'http://localhost:3000', 3 | socketNamespaces: { 4 | room: 'room', 5 | lobby: 'lobby' 6 | }, 7 | events: { 8 | USER_CONNECTED: 'userConnection', 9 | USER_DISCONNECTED: 'userDisconnection', 10 | 11 | JOIN_ROOM: 'joinRoom', 12 | LOBBY_UPDATED: 'lobbyUpdated', 13 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission' 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/_shared/socketBuilder.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | 4 | export default class SocketBuilder { 5 | constructor({ socketUrl, namespace }) { 6 | this.socketUrl = `${socketUrl}/${namespace}` 7 | 8 | this.onUserConnected = () => { } 9 | this.onUserDisconnected = () => { } 10 | } 11 | 12 | setOnUserConnected(fn) { 13 | this.onUserConnected = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserDisconnected(fn) { 19 | this.onUserDisconnected = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = globalThis.io.connect(this.socketUrl, { 26 | withCredentials: false 27 | }) 28 | 29 | socket.on('connect', () => console.log('conectei!')) 30 | 31 | socket.on(constants.events.USER_CONNECTED, this.onUserConnected) 32 | socket.on(constants.events.USER_DISCONNECTED, this.onUserDisconnected) 33 | 34 | return socket 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/controller.js: -------------------------------------------------------------------------------- 1 | export default class LobbyController { 2 | constructor({ socketBuilder, user, view }) { 3 | this.socketBuilder = socketBuilder 4 | this.user = user 5 | this.view = view 6 | 7 | this.socket = {} 8 | } 9 | 10 | static initialize(deps) { 11 | return new LobbyController(deps)._init() 12 | } 13 | 14 | async _init() { 15 | this._setupViewEvents() 16 | this.socket = this._setupSocket() 17 | } 18 | 19 | _setupViewEvents() { 20 | this.view.updateUserImage(this.user) 21 | this.view.configureCreateRoomButton() 22 | 23 | } 24 | _setupSocket() { 25 | return this.socketBuilder 26 | .setOnLobbyUpdated(this.onLobbyUpdated()) 27 | .build() 28 | } 29 | 30 | onLobbyUpdated() { 31 | return (rooms) => { 32 | console.log('rooms', rooms) 33 | this.view.updateRoomList(rooms) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/entities/room.js: -------------------------------------------------------------------------------- 1 | class Attendee { 2 | constructor({ id, img, username }) { 3 | this.id = id 4 | this.img = img 5 | this.username = username 6 | } 7 | } 8 | 9 | export default class Room { 10 | constructor({ 11 | id, 12 | topic, 13 | subTopic, 14 | roomLink, 15 | attendeesCount, 16 | speakersCount, 17 | featuredAttendees, 18 | owner, 19 | }) { 20 | 21 | this.id = id 22 | this.topic = topic 23 | this.subTopic = subTopic || "Semana JS Expert 4.0" 24 | this.attendeesCount = attendeesCount 25 | this.speakersCount = speakersCount 26 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 27 | this.owner = new Attendee(owner) 28 | this.roomLink = roomLink 29 | } 30 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js"; 2 | import LobbyController from "./controller.js"; 3 | import LobbySocketBuilder from "./util/lobbySocketBuilder.js"; 4 | import View from "./view.js"; 5 | 6 | const user = { 7 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 8 | username: 'Erick ' + Date.now() 9 | } 10 | 11 | 12 | const socketBuilder = new LobbySocketBuilder({ 13 | socketUrl: constants.socketUrl, 14 | namespace: constants.socketNamespaces.lobby 15 | }) 16 | 17 | const dependencies = { 18 | socketBuilder, 19 | user, 20 | view: View 21 | } 22 | await LobbyController.initialize(dependencies) -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/templates/lobbyItem.js: -------------------------------------------------------------------------------- 1 | import Room from "../entities/room.js"; 2 | 3 | function createFeaturedSpeakersTemplate(featuredAttendees) { 4 | if(!featuredAttendees.length) return ''; 5 | 6 | const attendees = featuredAttendees.map((attende) => { 7 | return ` 8 |
  • ${attende.username} 💬
  • 9 | ` 10 | }) 11 | 12 | return attendees.join('') 13 | } 14 | 15 | 16 | export default function getTemplate(room = new Room()) { 17 | const { owner } = room 18 | 19 | return ` 20 | 21 |
    22 | 23 | ${room.subTopic} 24 | 25 | 26 |

    27 |

    28 | ${room.topic} 29 |

    30 |
    31 |
    32 | ${owner.username} 33 |
    34 |
    35 |
      36 | ${createFeaturedSpeakersTemplate(room.featuredAttendees)} 37 | 38 | ${room.attendeesCount} / ${room.speakersCount} 39 | 40 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | ` 47 | } 48 | -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/util/lobbySocketBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class LobbySocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onLobbyUpdated = () => { } 9 | } 10 | 11 | setOnLobbyUpdated(fn) { 12 | this.onLobbyUpdated = fn 13 | 14 | return this 15 | } 16 | 17 | build() { 18 | const socket = super.build() 19 | 20 | socket.on(constants.events.LOBBY_UPDATED, this.onLobbyUpdated) 21 | 22 | return socket; 23 | } 24 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/lobby/src/view.js: -------------------------------------------------------------------------------- 1 | import Room from "./entities/room.js" 2 | import getTemplate from "./templates/lobbyItem.js" 3 | 4 | const roomGrid = document.getElementById('roomGrid') 5 | 6 | const btnCreateRoomWithTopic = document.getElementById('btnCreateRoomWithTopic') 7 | const btnCreateRoomWithoutTopic = document.getElementById('btnCreateRoomWithoutTopic') 8 | const txtTopic = document.getElementById('txtTopic') 9 | 10 | export default class View { 11 | static clearRoomList() { 12 | roomGrid.innerHTML = '' 13 | } 14 | static generateRoomLink({ id, topic }) { 15 | return `./../room/index.html?id=${id}&topic=${topic}` 16 | } 17 | 18 | static redirectToRoom(topic = '') { 19 | // https://stackoverflow.com/a/44078785/4087199 20 | const id = Date.now().toString(36) + Math.random().toString(36).substring(2) 21 | window.location = View.generateRoomLink({ 22 | id, 23 | topic 24 | }) 25 | } 26 | 27 | static configureCreateRoomButton() { 28 | btnCreateRoomWithoutTopic.addEventListener('click', () => { 29 | View.redirectToRoom() 30 | }) 31 | btnCreateRoomWithTopic.addEventListener('click', () => { 32 | const topic = txtTopic.value 33 | View.redirectToRoom(topic) 34 | 35 | }) 36 | } 37 | static updateRoomList(rooms) { 38 | View.clearRoomList() 39 | 40 | rooms.forEach(room => { 41 | const params = new Room({ 42 | ...room, 43 | roomLink: View.generateRoomLink(room) 44 | }) 45 | const htmlTemplate = getTemplate(params) 46 | 47 | roomGrid.innerHTML += htmlTemplate 48 | }) 49 | } 50 | 51 | static updateUserImage({ img, username }) { 52 | imgUser.src = img 53 | imgUser.al = username 54 | } 55 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/login/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clubhouse - Semana JSExpert 9 | 10 | 11 | 12 | 13 | 14 |
    15 |

    16 | 🎉 17 | Welcome! 18 |

    19 |

    20 | We're working hard to get Clubhouse ready for everyone! While we wrap up the finishing touches, we're adding people gradually to make sure nothing breaks. :) 21 |

    22 |

    23 | Anyone can join with an invite from an existing user -- or reserve your username and we'll text you if you have a friend on the app who can let you in. We are so grateful you're here and can't wait to have you join us! 24 | 🙏 25 |

    26 | 30 | 31 |
    32 | 33 | { 41 | const attendee = new Attendee(data) 42 | console.log('onUserProfileUgrade', attendee) 43 | if(attendee.isSpeaker) { 44 | this.view.addAttendeeOnGrid(attendee, true) 45 | } 46 | } 47 | } 48 | 49 | onRoomUpdated() { 50 | return (room) => { 51 | this.view.updateAttendeesOnGrid(room) 52 | console.log('room list!', room) 53 | } 54 | } 55 | 56 | onDisconnected() { 57 | return (data) => { 58 | const attendee = new Attendee(data) 59 | 60 | console.log(`${attendee.username} disconnected!`) 61 | this.view.removeItemFromGrid(attendee.id) 62 | } 63 | } 64 | 65 | onUserConnected() { 66 | return (data) => { 67 | const attendee = new Attendee(data) 68 | console.log('user connected!', attendee) 69 | this.view.addAttendeeOnGrid(attendee) 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/room/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.img = img || "" 5 | this.isSpeaker = isSpeaker 6 | this.roomId = roomId 7 | this.peerId = peerId 8 | 9 | const name = username || "Usuário Anônimo" 10 | this.username = name 11 | 12 | const [firstName, lastName] = name.split(/\s/) 13 | this.firstName = firstName + id 14 | this.lastName = lastName 15 | } 16 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/room/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js" 2 | import RoomController from "./controller.js" 3 | import RoomSocketBuilder from "./util/roomSocket.js" 4 | import View from "./view.js" 5 | 6 | 7 | const urlParams = new URLSearchParams(window.location.search) 8 | const keys = ['id', 'topic'] 9 | const urlData = keys.map((key) => [key, urlParams.get(key)]) 10 | 11 | 12 | 13 | const user = { 14 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 15 | username: 'Erick ' + Date.now() 16 | } 17 | 18 | const roomInfo = { 19 | room: { ...Object.fromEntries(urlData) }, 20 | user 21 | } 22 | 23 | 24 | const socketBuilder = new RoomSocketBuilder({ 25 | socketUrl: constants.socketUrl, 26 | namespace: constants.socketNamespaces.room 27 | }) 28 | 29 | const dependencies = { 30 | view: View, 31 | socketBuilder, 32 | roomInfo 33 | } 34 | 35 | 36 | await RoomController.initialize(dependencies) 37 | 38 | 39 | -------------------------------------------------------------------------------- /aulas/aula02/app/pages/room/src/templates/attendeeTemplate.js: -------------------------------------------------------------------------------- 1 | import Attendee from "../entities/attendee.js" 2 | const speakericon = 'File icon' 3 | 4 | export default function getTemplate(attendee = new Attendee()) { 5 | const speakerTemplate = attendee.isSpeaker ? speakericon : "" 6 | 7 | return ` 8 |
    9 |
    10 | ${attendee.username} 11 |
    12 |

    13 | ${speakerTemplate} 14 | ${attendee.firstName} 15 |

    16 |
    17 | `; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /aulas/aula02/app/pages/room/src/util/roomSocket.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class RoomSocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onRoomUpdated = () => { } 9 | this.onUserProfileUpgrade = () => { } 10 | } 11 | 12 | setOnRoomUpdated(fn) { 13 | this.onRoomUpdated = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserProfileUpgrade(fn) { 19 | this.onUserProfileUpgrade = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = super.build() 26 | 27 | socket.on(constants.events.LOBBY_UPDATED, this.onRoomUpdated) 28 | socket.on(constants.events.UPGRADE_USER_PERMISSION, this.onUserProfileUpgrade) 29 | 30 | return socket; 31 | } 32 | } -------------------------------------------------------------------------------- /aulas/aula02/app/pages/room/src/view.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./entities/attendee.js" 2 | import getTemplate from "./templates/attendeeTemplate.js" 3 | 4 | const imgUser = document.getElementById('imgUser') 5 | const roomTopic = document.getElementById('pTopic') 6 | const gridAttendees = document.getElementById('gridAttendees') 7 | const gridSpeakers = document.getElementById('gridSpeakers') 8 | 9 | export default class View { 10 | static updateUserImage({ img, username }) { 11 | imgUser.src = img 12 | imgUser.al = username 13 | } 14 | 15 | static updateRoomTopic({ topic }) { 16 | roomTopic.innerHTML = topic 17 | } 18 | 19 | static updateAttendeesOnGrid(users) { 20 | users.forEach(item => View.addAttendeeOnGrid(item)) 21 | } 22 | 23 | static _getExistingItemOnGrid({ id, baseElement = document }) { 24 | const existingItem = baseElement.querySelector(`[id="${id}"]`) 25 | return existingItem 26 | } 27 | 28 | 29 | static removeItemFromGrid(id) { 30 | const existingElement = View._getExistingItemOnGrid({ id }) 31 | existingElement?.remove() 32 | } 33 | 34 | static addAttendeeOnGrid(item, removeFirst = false) { 35 | const attendee = new Attendee(item) 36 | const id = attendee.id 37 | const htmlTemplate = getTemplate(attendee) 38 | const baseElement = attendee.isSpeaker ? gridSpeakers : gridAttendees 39 | 40 | if (removeFirst) { 41 | View.removeItemFromGrid(id) 42 | baseElement.innerHTML += htmlTemplate 43 | return; 44 | } 45 | 46 | const existingItem = View._getExistingItemOnGrid({ id, baseElement }) 47 | if(existingItem) { 48 | existingItem.innerHTML = htmlTemplate; 49 | return; 50 | } 51 | 52 | baseElement.innerHTML += htmlTemplate 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /aulas/aula02/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/index.js", 9 | "dev": "npx nodemon src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "socket.io": "^4.0.2" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /aulas/aula02/server/src/controllers/lobbyController.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../util/constants.js" 2 | 3 | export default class LobbyController { 4 | constructor({ activeRooms, roomsListener }) { 5 | this.activeRooms = activeRooms 6 | this.roomsListener = roomsListener 7 | } 8 | 9 | onNewConnection(socket) { 10 | const { id } = socket 11 | console.log('[Lobby] connection stablished with', id) 12 | this.#updateLobbyRooms(socket, [...this.activeRooms.values()]) 13 | } 14 | 15 | #updateLobbyRooms(socket, activeRooms) { 16 | socket.emit(constants.event.LOBBY_UPDATED, activeRooms) 17 | } 18 | 19 | getEvents() { 20 | const functions = Reflect.ownKeys(LobbyController.prototype) 21 | .filter(fn => fn !== 'constructor') 22 | .map(name => [name, this[name].bind(this)]) 23 | 24 | return new Map(functions) 25 | } 26 | } -------------------------------------------------------------------------------- /aulas/aula02/server/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.username = username 5 | this.img = img 6 | this.isSpeaker = isSpeaker 7 | this.roomId = roomId 8 | this.peerId = peerId 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula02/server/src/entities/room.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./attendee.js" 2 | 3 | export default class Room { 4 | constructor({ id, topic, attendeesCount, speakersCount, featuredAttendees, owner, users }) { 5 | 6 | this.id = id 7 | this.topic = topic 8 | this.attendeesCount = attendeesCount 9 | this.speakersCount = speakersCount 10 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 11 | this.owner = new Attendee(owner) 12 | this.users = users 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula02/server/src/index.js: -------------------------------------------------------------------------------- 1 | import RoomsController from "./controllers/roomsController.js"; 2 | import SocketServer from "./util/socket.js"; 3 | import Event from 'events' 4 | import { constants } from './util/constants.js' 5 | import LobbyController from "./controllers/lobbyController.js"; 6 | 7 | const port = process.env.PORT || 3000 8 | const socketServer = new SocketServer({ port }) 9 | const server = await socketServer.start() 10 | 11 | 12 | const roomsPubSub = new Event() 13 | 14 | const roomsController = new RoomsController() 15 | const lobbyController = new LobbyController({ 16 | activeRooms: roomsController.rooms, 17 | roomsListener: roomsPubSub 18 | }) 19 | 20 | const namespaces = { 21 | room: { controller: roomsController, eventEmitter: new Event() }, 22 | lobby: { controller: lobbyController, eventEmitter: roomsPubSub }, 23 | } 24 | 25 | // namespaces.room.eventEmitter.on( 26 | // 'userConnected', 27 | // namespaces.room.controller.onNewConnection.bind(namespaces.room.controller) 28 | // ) 29 | 30 | // namespaces.room.eventEmitter.emit('userConnected', { id: '001' }) 31 | // namespaces.room.eventEmitter.emit('userConnected', { id: '002' }) 32 | // namespaces.room.eventEmitter.emit('userConnected', { id: '003' }) 33 | 34 | const routeConfig = Object.entries(namespaces) 35 | .map(([namespace, { controller, eventEmitter }]) => { 36 | const controllerEvents = controller.getEvents() 37 | eventEmitter.on( 38 | constants.event.USER_CONNECTED, 39 | controller.onNewConnection.bind(controller) 40 | ) 41 | 42 | return { 43 | [namespace]: { events: controllerEvents, eventEmitter } 44 | } 45 | 46 | }) 47 | 48 | 49 | socketServer.attachEvents({ routeConfig }) 50 | 51 | console.log('socket server is running at', server.address().port) -------------------------------------------------------------------------------- /aulas/aula02/server/src/util/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | event: { 3 | USER_CONNECTED: 'userConnection', 4 | USER_DISCONNECTED: 'userDisconnection', 5 | 6 | JOIN_ROOM: 'joinRoom', 7 | 8 | LOBBY_UPDATED: 'lobbyUpdated', 9 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission' 10 | } 11 | } -------------------------------------------------------------------------------- /aulas/aula02/server/src/util/socket.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { Server } from 'socket.io' 3 | import { constants } from './constants.js' 4 | 5 | export default class SocketServer { 6 | #io 7 | constructor({ port }) { 8 | this.port = port 9 | this.namespaces = {} 10 | } 11 | // [ 12 | // { 13 | // room: { 14 | // events, 15 | // eventEmitter 16 | // } 17 | // } 18 | // ] 19 | 20 | attachEvents({ routeConfig }) { 21 | for (const routes of routeConfig) { 22 | for (const [namespace, { events, eventEmitter }] of Object.entries(routes)) { 23 | const route = this.namespaces[namespace] = this.#io.of(`/${namespace}`) 24 | route.on('connection', socket => { 25 | for (const [functionName, functionValue] of events) { 26 | socket.on(functionName, (...args) => functionValue(socket, ...args)) 27 | } 28 | 29 | eventEmitter.emit(constants.event.USER_CONNECTED, socket) 30 | }) 31 | } 32 | } 33 | } 34 | 35 | async start() { 36 | const server = http.createServer((request, response) => { 37 | response.writeHead(200, { 38 | 'Access-Control-Allow-Origin': '*', 39 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' 40 | }) 41 | 42 | response.end('hey there!!') 43 | }) 44 | 45 | this.#io = new Server(server, { 46 | cors: { 47 | origin: '*', 48 | credentials: false 49 | } 50 | }) 51 | 52 | 53 | 54 | return new Promise((resolve, reject) => { 55 | server.on('error', reject) 56 | 57 | server.listen(this.port, () => resolve(server)) 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /aulas/aula03/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /aulas/aula03/app/README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [ ] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [ ] Deve ter login com GitHub 26 | - [ ] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [x] Mostra todas as salas ativas 31 | - [x] Atualiza salas em realtime 32 | - [x] Pode criar uma sala sem topico 33 | - [x] Pode criar uma sala com topico 34 | - [x] Pode acessar salas ativas 35 | - Room 36 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [x] Cria uma sala com um usuário dono 38 | - [x] Todos usuários futuros entram com perfil de attendees 39 | - [x] Notifica Lobby sobre atualizações na sala 40 | - [x] Lista usuarios com perfis de speakers e attendees 41 | - [X] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [ ] Recebe notificação de attendees para se tornarem speakers 45 | - [X] Atualizam a tela o upgrade de attendee para speaker 46 | - [ ] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [ ] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [X] Promove o speaker mais velho da sala 51 | - [X] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [x] Pode ouvir speakers ativos 54 | - [ ] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [ ] Reinicia todas as suas chamas ativas com os usuarios da sala 57 | - [ ] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/avatars/erick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/avatars/erick.jpeg -------------------------------------------------------------------------------- /aulas/aula03/app/assets/avatars/ivete-souza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/avatars/ivete-souza.png -------------------------------------------------------------------------------- /aulas/aula03/app/assets/avatars/mariazinha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/avatars/mariazinha.png -------------------------------------------------------------------------------- /aulas/aula03/app/assets/avatars/ze-silva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/avatars/ze-silva.png -------------------------------------------------------------------------------- /aulas/aula03/app/assets/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/favicon.jpeg -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/bell.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/hand-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula03/app/assets/printscreen/clubhouse-home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/printscreen/clubhouse-home.PNG -------------------------------------------------------------------------------- /aulas/aula03/app/assets/printscreen/clubhouse-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/printscreen/clubhouse-login.PNG -------------------------------------------------------------------------------- /aulas/aula03/app/assets/printscreen/clubhouse-room.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula03/app/assets/printscreen/clubhouse-room.PNG -------------------------------------------------------------------------------- /aulas/aula03/app/css/colors.css: -------------------------------------------------------------------------------- 1 | /* Clubhouse color palette */ 2 | 3 | :root { 4 | --primary-color: #F2EFE4; 5 | --white-color: #ffffff; 6 | --graphite-color: #4e4d49; 7 | --graphite-dark-color: #252523; 8 | --graphite-light-color: #b4b3b3; 9 | --gray-color: #ececec; 10 | --ice-color: #dddddd; 11 | --button-green-color: #4FAF62; 12 | --red-color: #f06034; 13 | --button-blue-color: #6495ed; 14 | --modal-shadow-color: rgba(0, 0, 0, 0.4); 15 | --github-color: #24292e; 16 | } -------------------------------------------------------------------------------- /aulas/aula03/app/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /aulas/aula03/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aulas/aula03/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erickwendel/clubhouse-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx http-server .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "devDependencies": { 11 | "http-server": "^0.12.3" 12 | }, 13 | "keywords": [], 14 | "author": "erickwendel", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /aulas/aula03/app/pages/_shared/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | socketUrl: 'http://localhost:3000', 3 | socketNamespaces: { 4 | room: 'room', 5 | lobby: 'lobby' 6 | }, 7 | peerConfig: Object.values({ 8 | id: undefined, 9 | // config: { 10 | // port: 9000, 11 | // host: 'localhost', 12 | // path: '/' 13 | // } 14 | }), 15 | events: { 16 | USER_CONNECTED: 'userConnection', 17 | USER_DISCONNECTED: 'userDisconnection', 18 | 19 | JOIN_ROOM: 'joinRoom', 20 | LOBBY_UPDATED: 'lobbyUpdated', 21 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission' 22 | } 23 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/_shared/media.js: -------------------------------------------------------------------------------- 1 | export default class Media { 2 | static async getUserAudio(audio = true) { 3 | return navigator.mediaDevices.getUserMedia({ 4 | audio 5 | }) 6 | } 7 | 8 | static createMediaStreamFake() { 9 | return new MediaStream([ 10 | Media._createEmptyAudioTrack() 11 | ]) 12 | } 13 | 14 | static _createEmptyAudioTrack() { 15 | const audioContext = new AudioContext() 16 | const oscillator = audioContext.createOscillator() 17 | const destination = oscillator.connect(audioContext.createMediaStreamDestination()) 18 | oscillator.start() 19 | const [track] = destination.stream.getAudioTracks() 20 | 21 | return Object.assign(track, { enabled: false }) 22 | } 23 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/_shared/socketBuilder.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | 4 | export default class SocketBuilder { 5 | constructor({ socketUrl, namespace }) { 6 | this.socketUrl = `${socketUrl}/${namespace}` 7 | 8 | this.onUserConnected = () => { } 9 | this.onUserDisconnected = () => { } 10 | } 11 | 12 | setOnUserConnected(fn) { 13 | this.onUserConnected = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserDisconnected(fn) { 19 | this.onUserDisconnected = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = globalThis.io.connect(this.socketUrl, { 26 | withCredentials: false 27 | }) 28 | 29 | socket.on('connect', () => console.log('conectei!')) 30 | 31 | socket.on(constants.events.USER_CONNECTED, this.onUserConnected) 32 | socket.on(constants.events.USER_DISCONNECTED, this.onUserDisconnected) 33 | 34 | return socket 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/controller.js: -------------------------------------------------------------------------------- 1 | export default class LobbyController { 2 | constructor({ socketBuilder, user, view }) { 3 | this.socketBuilder = socketBuilder 4 | this.user = user 5 | this.view = view 6 | 7 | this.socket = {} 8 | } 9 | 10 | static initialize(deps) { 11 | return new LobbyController(deps)._init() 12 | } 13 | 14 | async _init() { 15 | this._setupViewEvents() 16 | this.socket = this._setupSocket() 17 | } 18 | 19 | _setupViewEvents() { 20 | this.view.updateUserImage(this.user) 21 | this.view.configureCreateRoomButton() 22 | 23 | } 24 | _setupSocket() { 25 | return this.socketBuilder 26 | .setOnLobbyUpdated(this.onLobbyUpdated()) 27 | .build() 28 | } 29 | 30 | onLobbyUpdated() { 31 | return (rooms) => { 32 | console.log('rooms', rooms) 33 | this.view.updateRoomList(rooms) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/entities/room.js: -------------------------------------------------------------------------------- 1 | class Attendee { 2 | constructor({ id, img, username }) { 3 | this.id = id 4 | this.img = img 5 | this.username = username 6 | } 7 | } 8 | 9 | export default class Room { 10 | constructor({ 11 | id, 12 | topic, 13 | subTopic, 14 | roomLink, 15 | attendeesCount, 16 | speakersCount, 17 | featuredAttendees, 18 | owner, 19 | }) { 20 | 21 | this.id = id 22 | this.topic = topic 23 | this.subTopic = subTopic || "Semana JS Expert 4.0" 24 | this.attendeesCount = attendeesCount 25 | this.speakersCount = speakersCount 26 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 27 | this.owner = new Attendee(owner) 28 | this.roomLink = roomLink 29 | } 30 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js"; 2 | import LobbyController from "./controller.js"; 3 | import LobbySocketBuilder from "./util/lobbySocketBuilder.js"; 4 | import View from "./view.js"; 5 | 6 | const user = { 7 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 8 | username: 'Erick ' + Date.now() 9 | } 10 | 11 | 12 | const socketBuilder = new LobbySocketBuilder({ 13 | socketUrl: constants.socketUrl, 14 | namespace: constants.socketNamespaces.lobby 15 | }) 16 | 17 | const dependencies = { 18 | socketBuilder, 19 | user, 20 | view: View 21 | } 22 | await LobbyController.initialize(dependencies) -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/templates/lobbyItem.js: -------------------------------------------------------------------------------- 1 | import Room from "../entities/room.js"; 2 | 3 | function createFeaturedSpeakersTemplate(featuredAttendees) { 4 | if(!featuredAttendees.length) return ''; 5 | 6 | const attendees = featuredAttendees.map((attende) => { 7 | return ` 8 |
  • ${attende.username} 💬
  • 9 | ` 10 | }) 11 | 12 | return attendees.join('') 13 | } 14 | 15 | 16 | export default function getTemplate(room = new Room()) { 17 | const { owner } = room 18 | 19 | return ` 20 | 21 |
    22 | 23 | ${room.subTopic} 24 | 25 | 26 |

    27 |

    28 | ${room.topic} 29 |

    30 |
    31 |
    32 | ${owner.username} 33 |
    34 |
    35 |
      36 | ${createFeaturedSpeakersTemplate(room.featuredAttendees)} 37 | 38 | ${room.attendeesCount} / ${room.speakersCount} 39 | 40 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | ` 47 | } 48 | -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/util/lobbySocketBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class LobbySocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onLobbyUpdated = () => { } 9 | } 10 | 11 | setOnLobbyUpdated(fn) { 12 | this.onLobbyUpdated = fn 13 | 14 | return this 15 | } 16 | 17 | build() { 18 | const socket = super.build() 19 | 20 | socket.on(constants.events.LOBBY_UPDATED, this.onLobbyUpdated) 21 | 22 | return socket; 23 | } 24 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/lobby/src/view.js: -------------------------------------------------------------------------------- 1 | import Room from "./entities/room.js" 2 | import getTemplate from "./templates/lobbyItem.js" 3 | 4 | const roomGrid = document.getElementById('roomGrid') 5 | 6 | const btnCreateRoomWithTopic = document.getElementById('btnCreateRoomWithTopic') 7 | const btnCreateRoomWithoutTopic = document.getElementById('btnCreateRoomWithoutTopic') 8 | const txtTopic = document.getElementById('txtTopic') 9 | 10 | export default class View { 11 | static clearRoomList() { 12 | roomGrid.innerHTML = '' 13 | } 14 | static generateRoomLink({ id, topic }) { 15 | return `./../room/index.html?id=${id}&topic=${topic}` 16 | } 17 | 18 | static redirectToRoom(topic = '') { 19 | // https://stackoverflow.com/a/44078785/4087199 20 | const id = Date.now().toString(36) + Math.random().toString(36).substring(2) 21 | window.location = View.generateRoomLink({ 22 | id, 23 | topic 24 | }) 25 | } 26 | 27 | static configureCreateRoomButton() { 28 | btnCreateRoomWithoutTopic.addEventListener('click', () => { 29 | View.redirectToRoom() 30 | }) 31 | btnCreateRoomWithTopic.addEventListener('click', () => { 32 | const topic = txtTopic.value 33 | View.redirectToRoom(topic) 34 | 35 | }) 36 | } 37 | static updateRoomList(rooms) { 38 | View.clearRoomList() 39 | 40 | rooms.forEach(room => { 41 | const params = new Room({ 42 | ...room, 43 | roomLink: View.generateRoomLink(room) 44 | }) 45 | const htmlTemplate = getTemplate(params) 46 | 47 | roomGrid.innerHTML += htmlTemplate 48 | }) 49 | } 50 | 51 | static updateUserImage({ img, username }) { 52 | imgUser.src = img 53 | imgUser.al = username 54 | } 55 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/login/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clubhouse - Semana JSExpert 9 | 10 | 11 | 12 | 13 | 14 |
    15 |

    16 | 🎉 17 | Welcome! 18 |

    19 |

    20 | We're working hard to get Clubhouse ready for everyone! While we wrap up the finishing touches, we're adding people gradually to make sure nothing breaks. :) 21 |

    22 |

    23 | Anyone can join with an invite from an existing user -- or reserve your username and we'll text you if you have a friend on the app who can let you in. We are so grateful you're here and can't wait to have you join us! 24 | 🙏 25 |

    26 | 30 | 31 |
    32 | 33 | [key, urlParams.get(key)]) 13 | 14 | 15 | 16 | const user = { 17 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 18 | username: 'Erick ' + Date.now() 19 | } 20 | 21 | const roomInfo = { 22 | room: { ...Object.fromEntries(urlData) }, 23 | user 24 | } 25 | const peerBuilder = new PeerBuilder({ 26 | peerConfig: constants.peerConfig 27 | }) 28 | 29 | const socketBuilder = new RoomSocketBuilder({ 30 | socketUrl: constants.socketUrl, 31 | namespace: constants.socketNamespaces.room 32 | }) 33 | const roomService = new RoomService({ 34 | media: Media 35 | }) 36 | 37 | const dependencies = { 38 | view: View, 39 | socketBuilder, 40 | roomInfo, 41 | roomService, 42 | peerBuilder 43 | } 44 | 45 | 46 | await RoomController.initialize(dependencies) 47 | 48 | 49 | -------------------------------------------------------------------------------- /aulas/aula03/app/pages/room/src/service.js: -------------------------------------------------------------------------------- 1 | import UserStream from './entities/userStream.js' 2 | export default class RoomService { 3 | constructor({ media }) { 4 | this.media = media 5 | 6 | this.currentPeer = {} 7 | this.currentUser = {} 8 | this.currentStream = {} 9 | 10 | this.peers = new Map() 11 | } 12 | 13 | async init() { 14 | this.currentStream = new UserStream({ 15 | stream: await this.media.getUserAudio(), 16 | isFake: false 17 | }) 18 | } 19 | setCurrentPeer(peer) { 20 | this.currentPeer = peer 21 | } 22 | 23 | getCurrentUser() { 24 | return this.currentUser 25 | } 26 | 27 | upgradeUserPermission(user) { 28 | if (!user.isSpeaker) return; 29 | 30 | const isCurrentUser = user.id === this.currentUser.id 31 | if (!isCurrentUser) return; 32 | 33 | this.currentUser = user 34 | } 35 | 36 | updateCurrentUserProfile(users) { 37 | this.currentUser = users.find(({ peerId }) => peerId === this.currentPeer.id) 38 | } 39 | 40 | async getCurrentStream() { 41 | const { isSpeaker } = this.currentUser 42 | if(isSpeaker) { 43 | return this.currentStream.stream 44 | } 45 | 46 | return this.media.createMediaStreamFake() 47 | } 48 | 49 | addReceivedPeer(call) { 50 | const calledId = call.peer 51 | this.peers.set(calledId, { call }) 52 | 53 | const isCurrentId = calledId === this.currentUser.id 54 | return { isCurrentId } 55 | } 56 | 57 | disconnectPeer({ peerId }) { 58 | if(!this.peers.has(peerId)) return; 59 | 60 | this.peers.get(peerId).call.close() 61 | this.peers.delete(peerId); 62 | } 63 | async callNewUser(user) { 64 | // se o usuario que entrou for speaker, ele vai me ligar! 65 | const { isSpeaker } = this.currentUser 66 | 67 | if(!isSpeaker) return; 68 | 69 | const stream = await this.getCurrentStream() 70 | this.currentPeer.call(user.peerId, stream) 71 | } 72 | } -------------------------------------------------------------------------------- /aulas/aula03/app/pages/room/src/templates/attendeeTemplate.js: -------------------------------------------------------------------------------- 1 | import Attendee from "../entities/attendee.js" 2 | const speakericon = 'File icon' 3 | 4 | export default function getTemplate(attendee = new Attendee()) { 5 | const speakerTemplate = attendee.isSpeaker ? speakericon : "" 6 | 7 | return ` 8 |
    9 |
    10 | ${attendee.username} 11 |
    12 |

    13 | ${speakerTemplate} 14 | ${attendee.firstName} 15 |

    16 |
    17 | `; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /aulas/aula03/app/pages/room/src/util/roomSocket.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class RoomSocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onRoomUpdated = () => { } 9 | this.onUserProfileUpgrade = () => { } 10 | } 11 | 12 | setOnRoomUpdated(fn) { 13 | this.onRoomUpdated = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserProfileUpgrade(fn) { 19 | this.onUserProfileUpgrade = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = super.build() 26 | 27 | socket.on(constants.events.LOBBY_UPDATED, this.onRoomUpdated) 28 | socket.on(constants.events.UPGRADE_USER_PERMISSION, this.onUserProfileUpgrade) 29 | 30 | return socket; 31 | } 32 | } -------------------------------------------------------------------------------- /aulas/aula03/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/index.js", 9 | "dev": "npx nodemon src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "socket.io": "^4.0.2" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /aulas/aula03/server/src/controllers/lobbyController.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../util/constants.js" 2 | 3 | export default class LobbyController { 4 | constructor({ activeRooms, roomsListener }) { 5 | this.activeRooms = activeRooms 6 | this.roomsListener = roomsListener 7 | } 8 | 9 | onNewConnection(socket) { 10 | const { id } = socket 11 | console.log('[Lobby] connection stablished with', id) 12 | this.#updateLobbyRooms(socket, [...this.activeRooms.values()]) 13 | 14 | this.#activateEventProxy(socket) 15 | } 16 | 17 | #activateEventProxy(socket) { 18 | this.roomsListener.on(constants.event.LOBBY_UPDATED, rooms => { 19 | this.#updateLobbyRooms(socket, rooms) 20 | }) 21 | } 22 | #updateLobbyRooms(socket, activeRooms) { 23 | socket.emit(constants.event.LOBBY_UPDATED, activeRooms) 24 | } 25 | 26 | getEvents() { 27 | const functions = Reflect.ownKeys(LobbyController.prototype) 28 | .filter(fn => fn !== 'constructor') 29 | .map(name => [name, this[name].bind(this)]) 30 | 31 | return new Map(functions) 32 | } 33 | } -------------------------------------------------------------------------------- /aulas/aula03/server/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.username = username 5 | this.img = img 6 | this.isSpeaker = isSpeaker 7 | this.roomId = roomId 8 | this.peerId = peerId 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula03/server/src/entities/room.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./attendee.js" 2 | 3 | export default class Room { 4 | constructor({ id, topic, attendeesCount, speakersCount, featuredAttendees, owner, users }) { 5 | 6 | this.id = id 7 | this.topic = topic 8 | this.attendeesCount = attendeesCount 9 | this.speakersCount = speakersCount 10 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 11 | this.owner = new Attendee(owner) 12 | this.users = users 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula03/server/src/index.js: -------------------------------------------------------------------------------- 1 | import RoomsController from "./controllers/roomsController.js"; 2 | import SocketServer from "./util/socket.js"; 3 | import Event from 'events' 4 | import { constants } from './util/constants.js' 5 | import LobbyController from "./controllers/lobbyController.js"; 6 | 7 | const port = process.env.PORT || 3000 8 | const socketServer = new SocketServer({ port }) 9 | const server = await socketServer.start() 10 | 11 | 12 | const roomsPubSub = new Event() 13 | 14 | const roomsController = new RoomsController({ 15 | roomsPubSub 16 | }) 17 | const lobbyController = new LobbyController({ 18 | activeRooms: roomsController.rooms, 19 | roomsListener: roomsPubSub 20 | }) 21 | 22 | const namespaces = { 23 | room: { controller: roomsController, eventEmitter: new Event() }, 24 | lobby: { controller: lobbyController, eventEmitter: roomsPubSub }, 25 | } 26 | 27 | // namespaces.room.eventEmitter.on( 28 | // 'userConnected', 29 | // namespaces.room.controller.onNewConnection.bind(namespaces.room.controller) 30 | // ) 31 | 32 | // namespaces.room.eventEmitter.emit('userConnected', { id: '001' }) 33 | // namespaces.room.eventEmitter.emit('userConnected', { id: '002' }) 34 | // namespaces.room.eventEmitter.emit('userConnected', { id: '003' }) 35 | 36 | const routeConfig = Object.entries(namespaces) 37 | .map(([namespace, { controller, eventEmitter }]) => { 38 | const controllerEvents = controller.getEvents() 39 | eventEmitter.on( 40 | constants.event.USER_CONNECTED, 41 | controller.onNewConnection.bind(controller) 42 | ) 43 | 44 | return { 45 | [namespace]: { events: controllerEvents, eventEmitter } 46 | } 47 | 48 | }) 49 | 50 | 51 | socketServer.attachEvents({ routeConfig }) 52 | 53 | console.log('socket server is running at', server.address().port) -------------------------------------------------------------------------------- /aulas/aula03/server/src/util/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | event: { 3 | USER_CONNECTED: 'userConnection', 4 | USER_DISCONNECTED: 'userDisconnection', 5 | 6 | JOIN_ROOM: 'joinRoom', 7 | 8 | LOBBY_UPDATED: 'lobbyUpdated', 9 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission' 10 | } 11 | } -------------------------------------------------------------------------------- /aulas/aula03/server/src/util/customMap.js: -------------------------------------------------------------------------------- 1 | export default class CustomMap extends Map { 2 | #observer 3 | #customMapper 4 | constructor({ observer, customMapper }) { 5 | super() 6 | 7 | this.#observer = observer 8 | this.#customMapper = customMapper 9 | } 10 | * values() { 11 | for(const value of super.values()) { 12 | yield this.#customMapper(value) 13 | } 14 | } 15 | set(...args) { 16 | const result = super.set(...args) 17 | this.#observer.notify(this) 18 | 19 | return result 20 | } 21 | 22 | delete(...args) { 23 | const result = super.delete(...args) 24 | this.#observer.notify(this) 25 | 26 | return result 27 | } 28 | } -------------------------------------------------------------------------------- /aulas/aula03/server/src/util/socket.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { Server } from 'socket.io' 3 | import { constants } from './constants.js' 4 | 5 | export default class SocketServer { 6 | #io 7 | constructor({ port }) { 8 | this.port = port 9 | this.namespaces = {} 10 | } 11 | // [ 12 | // { 13 | // room: { 14 | // events, 15 | // eventEmitter 16 | // } 17 | // } 18 | // ] 19 | 20 | attachEvents({ routeConfig }) { 21 | for (const routes of routeConfig) { 22 | for (const [namespace, { events, eventEmitter }] of Object.entries(routes)) { 23 | const route = this.namespaces[namespace] = this.#io.of(`/${namespace}`) 24 | route.on('connection', socket => { 25 | for (const [functionName, functionValue] of events) { 26 | socket.on(functionName, (...args) => functionValue(socket, ...args)) 27 | } 28 | 29 | eventEmitter.emit(constants.event.USER_CONNECTED, socket) 30 | }) 31 | } 32 | } 33 | } 34 | 35 | async start() { 36 | const server = http.createServer((request, response) => { 37 | response.writeHead(200, { 38 | 'Access-Control-Allow-Origin': '*', 39 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' 40 | }) 41 | 42 | response.end('hey there!!') 43 | }) 44 | 45 | this.#io = new Server(server, { 46 | cors: { 47 | origin: '*', 48 | credentials: false 49 | } 50 | }) 51 | 52 | 53 | 54 | return new Promise((resolve, reject) => { 55 | server.on('error', reject) 56 | 57 | server.listen(this.port, () => resolve(server)) 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /aulas/aula04/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /aulas/aula04/app/README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [ ] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [ ] Deve ter login com GitHub 26 | - [ ] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [x] Mostra todas as salas ativas 31 | - [x] Atualiza salas em realtime 32 | - [x] Pode criar uma sala sem topico 33 | - [x] Pode criar uma sala com topico 34 | - [x] Pode acessar salas ativas 35 | - Room 36 | - [ ] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [x] Cria uma sala com um usuário dono 38 | - [x] Todos usuários futuros entram com perfil de attendees 39 | - [x] Notifica Lobby sobre atualizações na sala 40 | - [x] Lista usuarios com perfis de speakers e attendees 41 | - [X] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [x] Recebe notificação de attendees para se tornarem speakers 45 | - [X] Atualizam a tela o upgrade de attendee para speaker 46 | - [x] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [x] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [X] Promove o speaker mais velho da sala 51 | - [X] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [x] Pode ouvir speakers ativos 54 | - [x] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [x] Reinicia todas as suas chamadas ativas com os usuarios da sala 57 | - [x] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/avatars/erick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/avatars/erick.jpeg -------------------------------------------------------------------------------- /aulas/aula04/app/assets/avatars/ivete-souza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/avatars/ivete-souza.png -------------------------------------------------------------------------------- /aulas/aula04/app/assets/avatars/mariazinha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/avatars/mariazinha.png -------------------------------------------------------------------------------- /aulas/aula04/app/assets/avatars/ze-silva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/avatars/ze-silva.png -------------------------------------------------------------------------------- /aulas/aula04/app/assets/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/favicon.jpeg -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/bell.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/hand-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula04/app/assets/printscreen/clubhouse-home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/printscreen/clubhouse-home.PNG -------------------------------------------------------------------------------- /aulas/aula04/app/assets/printscreen/clubhouse-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/printscreen/clubhouse-login.PNG -------------------------------------------------------------------------------- /aulas/aula04/app/assets/printscreen/clubhouse-room.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula04/app/assets/printscreen/clubhouse-room.PNG -------------------------------------------------------------------------------- /aulas/aula04/app/css/colors.css: -------------------------------------------------------------------------------- 1 | /* Clubhouse color palette */ 2 | 3 | :root { 4 | --primary-color: #F2EFE4; 5 | --white-color: #ffffff; 6 | --graphite-color: #4e4d49; 7 | --graphite-dark-color: #252523; 8 | --graphite-light-color: #b4b3b3; 9 | --gray-color: #ececec; 10 | --ice-color: #dddddd; 11 | --button-green-color: #4FAF62; 12 | --red-color: #f06034; 13 | --button-blue-color: #6495ed; 14 | --modal-shadow-color: rgba(0, 0, 0, 0.4); 15 | --github-color: #24292e; 16 | } -------------------------------------------------------------------------------- /aulas/aula04/app/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /aulas/aula04/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aulas/aula04/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erickwendel/clubhouse-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx http-server .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "devDependencies": { 11 | "http-server": "^0.12.3" 12 | }, 13 | "keywords": [], 14 | "author": "erickwendel", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /aulas/aula04/app/pages/_shared/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | socketUrl: 'http://localhost:3000', 3 | socketNamespaces: { 4 | room: 'room', 5 | lobby: 'lobby' 6 | }, 7 | peerConfig: Object.values({ 8 | id: undefined, 9 | // config: { 10 | // port: 9000, 11 | // host: 'localhost', 12 | // path: '/' 13 | // } 14 | }), 15 | pages: { 16 | lobby: '/pages/lobby', 17 | login: '/pages/login', 18 | }, 19 | events: { 20 | USER_CONNECTED: 'userConnection', 21 | USER_DISCONNECTED: 'userDisconnection', 22 | 23 | JOIN_ROOM: 'joinRoom', 24 | LOBBY_UPDATED: 'lobbyUpdated', 25 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission', 26 | 27 | SPEAK_REQUEST: 'speakRequest', 28 | SPEAK_ANSWER: 'speakAnswer' 29 | } 30 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/_shared/media.js: -------------------------------------------------------------------------------- 1 | export default class Media { 2 | static async getUserAudio(audio = true) { 3 | return navigator.mediaDevices.getUserMedia({ 4 | audio 5 | }) 6 | } 7 | 8 | static createMediaStreamFake() { 9 | return new MediaStream([ 10 | Media._createEmptyAudioTrack() 11 | ]) 12 | } 13 | 14 | static _createEmptyAudioTrack() { 15 | const audioContext = new AudioContext() 16 | const oscillator = audioContext.createOscillator() 17 | const destination = oscillator.connect(audioContext.createMediaStreamDestination()) 18 | oscillator.start() 19 | const [track] = destination.stream.getAudioTracks() 20 | 21 | return Object.assign(track, { enabled: false }) 22 | } 23 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/_shared/socketBuilder.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | 4 | export default class SocketBuilder { 5 | constructor({ socketUrl, namespace }) { 6 | this.socketUrl = `${socketUrl}/${namespace}` 7 | 8 | this.onUserConnected = () => { } 9 | this.onUserDisconnected = () => { } 10 | } 11 | 12 | setOnUserConnected(fn) { 13 | this.onUserConnected = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserDisconnected(fn) { 19 | this.onUserDisconnected = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = globalThis.io.connect(this.socketUrl, { 26 | withCredentials: false 27 | }) 28 | 29 | socket.on('connect', () => console.log('conectei!')) 30 | 31 | socket.on(constants.events.USER_CONNECTED, this.onUserConnected) 32 | socket.on(constants.events.USER_DISCONNECTED, this.onUserDisconnected) 33 | 34 | return socket 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/controller.js: -------------------------------------------------------------------------------- 1 | export default class LobbyController { 2 | constructor({ socketBuilder, user, view }) { 3 | this.socketBuilder = socketBuilder 4 | this.user = user 5 | this.view = view 6 | 7 | this.socket = {} 8 | } 9 | 10 | static initialize(deps) { 11 | return new LobbyController(deps)._init() 12 | } 13 | 14 | async _init() { 15 | this._setupViewEvents() 16 | this.socket = this._setupSocket() 17 | } 18 | 19 | _setupViewEvents() { 20 | this.view.updateUserImage(this.user) 21 | this.view.configureCreateRoomButton() 22 | 23 | } 24 | _setupSocket() { 25 | return this.socketBuilder 26 | .setOnLobbyUpdated(this.onLobbyUpdated()) 27 | .build() 28 | } 29 | 30 | onLobbyUpdated() { 31 | return (rooms) => { 32 | console.log('rooms', rooms) 33 | this.view.updateRoomList(rooms) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/entities/room.js: -------------------------------------------------------------------------------- 1 | class Attendee { 2 | constructor({ id, img, username }) { 3 | this.id = id 4 | this.img = img 5 | this.username = username 6 | } 7 | } 8 | 9 | export default class Room { 10 | constructor({ 11 | id, 12 | topic, 13 | subTopic, 14 | roomLink, 15 | attendeesCount, 16 | speakersCount, 17 | featuredAttendees, 18 | owner, 19 | }) { 20 | 21 | this.id = id 22 | this.topic = topic 23 | this.subTopic = subTopic || "Semana JS Expert 4.0" 24 | this.attendeesCount = attendeesCount 25 | this.speakersCount = speakersCount 26 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 27 | this.owner = new Attendee(owner) 28 | this.roomLink = roomLink 29 | } 30 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js"; 2 | import LobbyController from "./controller.js"; 3 | import LobbySocketBuilder from "./util/lobbySocketBuilder.js"; 4 | import View from "./view.js"; 5 | 6 | const user = { 7 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 8 | username: 'Erick ' + Date.now() 9 | } 10 | 11 | 12 | const socketBuilder = new LobbySocketBuilder({ 13 | socketUrl: constants.socketUrl, 14 | namespace: constants.socketNamespaces.lobby 15 | }) 16 | 17 | const dependencies = { 18 | socketBuilder, 19 | user, 20 | view: View 21 | } 22 | LobbyController.initialize(dependencies) 23 | .catch(error => { 24 | alert(error.message) 25 | }) -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/templates/lobbyItem.js: -------------------------------------------------------------------------------- 1 | import Room from "../entities/room.js"; 2 | 3 | function createFeaturedSpeakersTemplate(featuredAttendees) { 4 | if(!featuredAttendees.length) return ''; 5 | 6 | const attendees = featuredAttendees.map((attende) => { 7 | return ` 8 |
  • ${attende.username} 💬
  • 9 | ` 10 | }) 11 | 12 | return attendees.join('') 13 | } 14 | 15 | 16 | export default function getTemplate(room = new Room()) { 17 | const { owner } = room 18 | 19 | return ` 20 | 21 |
    22 | 23 | ${room.subTopic} 24 | 25 | 26 |

    27 |

    28 | ${room.topic} 29 |

    30 |
    31 |
    32 | ${owner.username} 33 |
    34 |
    35 |
      36 | ${createFeaturedSpeakersTemplate(room.featuredAttendees)} 37 | 38 | ${room.attendeesCount} / ${room.speakersCount} 39 | 40 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | ` 47 | } 48 | -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/util/lobbySocketBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class LobbySocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onLobbyUpdated = () => { } 9 | } 10 | 11 | setOnLobbyUpdated(fn) { 12 | this.onLobbyUpdated = fn 13 | 14 | return this 15 | } 16 | 17 | build() { 18 | const socket = super.build() 19 | 20 | socket.on(constants.events.LOBBY_UPDATED, this.onLobbyUpdated) 21 | 22 | return socket; 23 | } 24 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/lobby/src/view.js: -------------------------------------------------------------------------------- 1 | import Room from "./entities/room.js" 2 | import getTemplate from "./templates/lobbyItem.js" 3 | 4 | const roomGrid = document.getElementById('roomGrid') 5 | 6 | const btnCreateRoomWithTopic = document.getElementById('btnCreateRoomWithTopic') 7 | const btnCreateRoomWithoutTopic = document.getElementById('btnCreateRoomWithoutTopic') 8 | const txtTopic = document.getElementById('txtTopic') 9 | const imgUser = document.getElementById('imgUser') 10 | 11 | export default class View { 12 | static clearRoomList() { 13 | roomGrid.innerHTML = '' 14 | } 15 | static generateRoomLink({ id, topic }) { 16 | return `./../room/index.html?id=${id}&topic=${topic}` 17 | } 18 | 19 | static redirectToRoom(topic = '') { 20 | // https://stackoverflow.com/a/44078785/4087199 21 | const id = Date.now().toString(36) + Math.random().toString(36).substring(2) 22 | window.location = View.generateRoomLink({ 23 | id, 24 | topic 25 | }) 26 | } 27 | 28 | static configureCreateRoomButton() { 29 | btnCreateRoomWithoutTopic.addEventListener('click', () => { 30 | View.redirectToRoom() 31 | }) 32 | btnCreateRoomWithTopic.addEventListener('click', () => { 33 | const topic = txtTopic.value 34 | View.redirectToRoom(topic) 35 | 36 | }) 37 | } 38 | static updateRoomList(rooms) { 39 | View.clearRoomList() 40 | 41 | rooms.forEach(room => { 42 | const params = new Room({ 43 | ...room, 44 | roomLink: View.generateRoomLink(room) 45 | }) 46 | const htmlTemplate = getTemplate(params) 47 | 48 | roomGrid.innerHTML += htmlTemplate 49 | }) 50 | } 51 | 52 | static updateUserImage({ img, username }) { 53 | imgUser.src = img 54 | imgUser.al = username 55 | } 56 | } -------------------------------------------------------------------------------- /aulas/aula04/app/pages/login/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clubhouse - Semana JSExpert 9 | 10 | 11 | 12 | 13 | 14 |
    15 |

    16 | 🎉 17 | Welcome! 18 |

    19 |

    20 | We're working hard to get Clubhouse ready for everyone! While we wrap up the finishing touches, we're adding people gradually to make sure nothing breaks. :) 21 |

    22 |

    23 | Anyone can join with an invite from an existing user -- or reserve your username and we'll text you if you have a friend on the app who can let you in. We are so grateful you're here and can't wait to have you join us! 24 | 🙏 25 |

    26 | 30 | 31 |
    32 | 33 | [key, urlParams.get(key)]) 13 | 14 | 15 | 16 | const user = { 17 | img: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/bear_russian_animal_avatar-256.png', 18 | username: 'Erick ' + Date.now() 19 | } 20 | 21 | const roomInfo = { 22 | room: { ...Object.fromEntries(urlData) }, 23 | user 24 | } 25 | const peerBuilder = new PeerBuilder({ 26 | peerConfig: constants.peerConfig 27 | }) 28 | 29 | const socketBuilder = new RoomSocketBuilder({ 30 | socketUrl: constants.socketUrl, 31 | namespace: constants.socketNamespaces.room 32 | }) 33 | const roomService = new RoomService({ 34 | media: Media 35 | }) 36 | 37 | const dependencies = { 38 | view: View, 39 | socketBuilder, 40 | roomInfo, 41 | roomService, 42 | peerBuilder 43 | } 44 | 45 | 46 | RoomController.initialize(dependencies) 47 | .catch(error => { 48 | alert(error.message) 49 | }) 50 | 51 | 52 | -------------------------------------------------------------------------------- /aulas/aula04/app/pages/room/src/templates/attendeeTemplate.js: -------------------------------------------------------------------------------- 1 | import Attendee from "../entities/attendee.js" 2 | const speakericon = 'File icon' 3 | 4 | export default function getTemplate(attendee = new Attendee()) { 5 | const speakerTemplate = attendee.isSpeaker ? speakericon : "" 6 | 7 | return ` 8 |
    9 |
    10 | ${attendee.username} 11 |
    12 |

    13 | ${speakerTemplate} 14 | ${attendee.firstName} 15 |

    16 |
    17 | `; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /aulas/aula04/app/pages/room/src/util/roomSocket.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class RoomSocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onRoomUpdated = () => { } 9 | this.onUserProfileUpgrade = () => { } 10 | this.onSpeakRequested = () => {} 11 | } 12 | 13 | setOnRoomUpdated(fn) { 14 | this.onRoomUpdated = fn 15 | 16 | return this 17 | } 18 | 19 | setOnUserProfileUpgrade(fn) { 20 | this.onUserProfileUpgrade = fn 21 | 22 | return this 23 | } 24 | setOnSpeakRequested(fn) { 25 | this.onSpeakRequested = fn 26 | return this 27 | } 28 | 29 | 30 | build() { 31 | const socket = super.build() 32 | 33 | socket.on(constants.events.LOBBY_UPDATED, this.onRoomUpdated) 34 | socket.on(constants.events.UPGRADE_USER_PERMISSION, this.onUserProfileUpgrade) 35 | socket.on(constants.events.SPEAK_REQUEST, this.onSpeakRequested) 36 | 37 | return socket; 38 | } 39 | } -------------------------------------------------------------------------------- /aulas/aula04/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/index.js", 9 | "dev": "npx nodemon src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "socket.io": "^4.0.2" 17 | }, 18 | "devDependencies": { 19 | "nodemon": "^2.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /aulas/aula04/server/src/controllers/lobbyController.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../util/constants.js" 2 | 3 | export default class LobbyController { 4 | constructor({ activeRooms, roomsListener }) { 5 | this.activeRooms = activeRooms 6 | this.roomsListener = roomsListener 7 | } 8 | 9 | onNewConnection(socket) { 10 | const { id } = socket 11 | console.log('[Lobby] connection stablished with', id) 12 | this.#updateLobbyRooms(socket, [...this.activeRooms.values()]) 13 | 14 | this.#activateEventProxy(socket) 15 | } 16 | 17 | #activateEventProxy(socket) { 18 | this.roomsListener.on(constants.event.LOBBY_UPDATED, rooms => { 19 | this.#updateLobbyRooms(socket, rooms) 20 | }) 21 | } 22 | #updateLobbyRooms(socket, activeRooms) { 23 | socket.emit(constants.event.LOBBY_UPDATED, activeRooms) 24 | } 25 | 26 | getEvents() { 27 | const functions = Reflect.ownKeys(LobbyController.prototype) 28 | .filter(fn => fn !== 'constructor') 29 | .map(name => [name, this[name].bind(this)]) 30 | 31 | return new Map(functions) 32 | } 33 | } -------------------------------------------------------------------------------- /aulas/aula04/server/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.username = username 5 | this.img = img 6 | this.isSpeaker = isSpeaker 7 | this.roomId = roomId 8 | this.peerId = peerId 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula04/server/src/entities/room.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./attendee.js" 2 | 3 | export default class Room { 4 | constructor({ id, topic, attendeesCount, speakersCount, featuredAttendees, owner, users }) { 5 | 6 | this.id = id 7 | this.topic = topic 8 | this.attendeesCount = attendeesCount 9 | this.speakersCount = speakersCount 10 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 11 | this.owner = new Attendee(owner) 12 | this.users = users 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula04/server/src/index.js: -------------------------------------------------------------------------------- 1 | import RoomsController from "./controllers/roomsController.js"; 2 | import SocketServer from "./util/socket.js"; 3 | import Event from 'events' 4 | import { constants } from './util/constants.js' 5 | import LobbyController from "./controllers/lobbyController.js"; 6 | 7 | const port = process.env.PORT || 3000 8 | const socketServer = new SocketServer({ port }) 9 | const server = await socketServer.start() 10 | 11 | 12 | const roomsPubSub = new Event() 13 | 14 | const roomsController = new RoomsController({ 15 | roomsPubSub 16 | }) 17 | const lobbyController = new LobbyController({ 18 | activeRooms: roomsController.rooms, 19 | roomsListener: roomsPubSub 20 | }) 21 | 22 | const namespaces = { 23 | room: { controller: roomsController, eventEmitter: new Event() }, 24 | lobby: { controller: lobbyController, eventEmitter: roomsPubSub }, 25 | } 26 | 27 | // namespaces.room.eventEmitter.on( 28 | // 'userConnected', 29 | // namespaces.room.controller.onNewConnection.bind(namespaces.room.controller) 30 | // ) 31 | 32 | // namespaces.room.eventEmitter.emit('userConnected', { id: '001' }) 33 | // namespaces.room.eventEmitter.emit('userConnected', { id: '002' }) 34 | // namespaces.room.eventEmitter.emit('userConnected', { id: '003' }) 35 | 36 | const routeConfig = Object.entries(namespaces) 37 | .map(([namespace, { controller, eventEmitter }]) => { 38 | const controllerEvents = controller.getEvents() 39 | eventEmitter.on( 40 | constants.event.USER_CONNECTED, 41 | controller.onNewConnection.bind(controller) 42 | ) 43 | 44 | return { 45 | [namespace]: { events: controllerEvents, eventEmitter } 46 | } 47 | 48 | }) 49 | 50 | 51 | socketServer.attachEvents({ routeConfig }) 52 | 53 | console.log('socket server is running at', server.address().port) -------------------------------------------------------------------------------- /aulas/aula04/server/src/util/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | event: { 3 | USER_CONNECTED: 'userConnection', 4 | USER_DISCONNECTED: 'userDisconnection', 5 | 6 | JOIN_ROOM: 'joinRoom', 7 | 8 | LOBBY_UPDATED: 'lobbyUpdated', 9 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission', 10 | 11 | SPEAK_REQUEST: 'speakRequest', 12 | SPEAK_ANSWER: 'speakAnswer' 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula04/server/src/util/customMap.js: -------------------------------------------------------------------------------- 1 | export default class CustomMap extends Map { 2 | #observer 3 | #customMapper 4 | constructor({ observer, customMapper }) { 5 | super() 6 | 7 | this.#observer = observer 8 | this.#customMapper = customMapper 9 | } 10 | * values() { 11 | for(const value of super.values()) { 12 | yield this.#customMapper(value) 13 | } 14 | } 15 | set(...args) { 16 | const result = super.set(...args) 17 | this.#observer.notify(this) 18 | 19 | return result 20 | } 21 | 22 | delete(...args) { 23 | const result = super.delete(...args) 24 | this.#observer.notify(this) 25 | 26 | return result 27 | } 28 | } -------------------------------------------------------------------------------- /aulas/aula04/server/src/util/socket.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { Server } from 'socket.io' 3 | import { constants } from './constants.js' 4 | 5 | export default class SocketServer { 6 | #io 7 | constructor({ port }) { 8 | this.port = port 9 | this.namespaces = {} 10 | } 11 | // [ 12 | // { 13 | // room: { 14 | // events, 15 | // eventEmitter 16 | // } 17 | // } 18 | // ] 19 | 20 | attachEvents({ routeConfig }) { 21 | for (const routes of routeConfig) { 22 | for (const [namespace, { events, eventEmitter }] of Object.entries(routes)) { 23 | const route = this.namespaces[namespace] = this.#io.of(`/${namespace}`) 24 | route.on('connection', socket => { 25 | for (const [functionName, functionValue] of events) { 26 | socket.on(functionName, (...args) => functionValue(socket, ...args)) 27 | } 28 | 29 | eventEmitter.emit(constants.event.USER_CONNECTED, socket) 30 | }) 31 | } 32 | } 33 | } 34 | 35 | async start() { 36 | const server = http.createServer((request, response) => { 37 | response.writeHead(200, { 38 | 'Access-Control-Allow-Origin': '*', 39 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' 40 | }) 41 | 42 | response.end('hey there!!') 43 | }) 44 | 45 | this.#io = new Server(server, { 46 | cors: { 47 | origin: '*', 48 | credentials: false 49 | } 50 | }) 51 | 52 | 53 | 54 | return new Promise((resolve, reject) => { 55 | server.on('error', reject) 56 | 57 | server.listen(this.port, () => resolve(server)) 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /aulas/aula05/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /aulas/aula05/app/README.md: -------------------------------------------------------------------------------- 1 | # ClubHouse Clone Template - Semana JS Expert 4.0 2 | 3 | Seja bem vindo(a) à quarta Semana Javascript Expert.Este é o código inicial para iniciar nossa jornada. 4 | 5 | Marque esse projeto com uma estrela 🌟 6 | 7 | ## Preview 8 | 9 | ### Página de Login 10 | 11 | Login 12 | 13 | ### Página de Salas 14 | 15 | Home 16 | 17 | ### Página de Sala 18 | 19 | Room 20 | 21 | ## Checklist Features 22 | 23 | - [x] O app deve funcionar na Web, Android e IOS 24 | - Login 25 | - [x] Deve ter login com GitHub 26 | - [x] Se houver dados do usuario em localStorage deve ir para lobby direto 27 | 28 | - Lobby 29 | - [x] Se não houver dados do usuario em localStorage deve voltar para login 30 | - [x] Mostra todas as salas ativas 31 | - [x] Atualiza salas em realtime 32 | - [x] Pode criar uma sala sem topico 33 | - [x] Pode criar uma sala com topico 34 | - [x] Pode acessar salas ativas 35 | - Room 36 | - [x] Se não houver dados do usuario em localStorage deve voltar para login 37 | - [x] Cria uma sala com um usuário dono 38 | - [x] Todos usuários futuros entram com perfil de attendees 39 | - [x] Notifica Lobby sobre atualizações na sala 40 | - [x] Lista usuarios com perfis de speakers e attendees 41 | - [X] Se o dono da sala desconectar, será removida 42 | - Users 43 | - Speaker 44 | - [x] Recebe notificação de attendees para se tornarem speakers 45 | - [X] Atualizam a tela o upgrade de attendee para speaker 46 | - [x] Poderá deixar seu microfone mudo 47 | - Se dono da sala 48 | - [x] Pode aprovar attendees a virarem speakers 49 | - Ao se desconectar 50 | - [X] Promove o speaker mais velho da sala 51 | - [X] Se não houver speaker promove o attendee mais velho da sala 52 | - Attendee 53 | - [x] Pode ouvir speakers ativos 54 | - [x] Pode pedir upgrade de perfil ao dono da sala 55 | - Ao ser aprovado 56 | - [x] Reinicia todas as suas chamadas ativas com os usuarios da sala 57 | - [x] Recebe as permissões do perfil speaker 58 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/avatars/erick.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/avatars/erick.jpeg -------------------------------------------------------------------------------- /aulas/aula05/app/assets/avatars/ivete-souza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/avatars/ivete-souza.png -------------------------------------------------------------------------------- /aulas/aula05/app/assets/avatars/mariazinha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/avatars/mariazinha.png -------------------------------------------------------------------------------- /aulas/aula05/app/assets/avatars/ze-silva.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/avatars/ze-silva.png -------------------------------------------------------------------------------- /aulas/aula05/app/assets/favicon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/favicon.jpeg -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/asterisk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/bell.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/envelope.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/hand-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/hand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aulas/aula05/app/assets/printscreen/clubhouse-home.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/printscreen/clubhouse-home.PNG -------------------------------------------------------------------------------- /aulas/aula05/app/assets/printscreen/clubhouse-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/printscreen/clubhouse-login.PNG -------------------------------------------------------------------------------- /aulas/aula05/app/assets/printscreen/clubhouse-room.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErickWendel/semana-javascript-expert04/2afc0c4355208f22f46411cac38024b6461af375/aulas/aula05/app/assets/printscreen/clubhouse-room.PNG -------------------------------------------------------------------------------- /aulas/aula05/app/css/colors.css: -------------------------------------------------------------------------------- 1 | /* Clubhouse color palette */ 2 | 3 | :root { 4 | --primary-color: #F2EFE4; 5 | --white-color: #ffffff; 6 | --graphite-color: #4e4d49; 7 | --graphite-dark-color: #252523; 8 | --graphite-light-color: #b4b3b3; 9 | --gray-color: #ececec; 10 | --ice-color: #dddddd; 11 | --button-green-color: #4FAF62; 12 | --red-color: #f06034; 13 | --button-blue-color: #6495ed; 14 | --modal-shadow-color: rgba(0, 0, 0, 0.4); 15 | --github-color: #24292e; 16 | } -------------------------------------------------------------------------------- /aulas/aula05/app/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /aulas/aula05/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /aulas/aula05/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erickwendel/clubhouse-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx http-server --cors -p $PORT .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "devDependencies": { 11 | "http-server": "^0.12.3" 12 | }, 13 | "keywords": [], 14 | "author": "erickwendel", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /aulas/aula05/app/pages/_shared/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | socketUrl: 'http://localhost:3000', 3 | // socketUrl: 'https://ew-socket-server.herokuapp.com', 4 | socketNamespaces: { 5 | room: 'room', 6 | lobby: 'lobby' 7 | }, 8 | peerConfig: Object.values({ 9 | id: undefined, 10 | config: { 11 | // host: 'ew-peerjs-server.herokuapp.com', 12 | // secure: true, 13 | // path: '/' 14 | port: 9000, 15 | host: 'localhost', 16 | path: '/' 17 | } 18 | }), 19 | pages: { 20 | lobby: '/pages/lobby', 21 | login: '/pages/login', 22 | }, 23 | events: { 24 | USER_CONNECTED: 'userConnection', 25 | USER_DISCONNECTED: 'userDisconnection', 26 | 27 | JOIN_ROOM: 'joinRoom', 28 | LOBBY_UPDATED: 'lobbyUpdated', 29 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission', 30 | 31 | SPEAK_REQUEST: 'speakRequest', 32 | SPEAK_ANSWER: 'speakAnswer' 33 | }, 34 | firebaseConfig : { 35 | apiKey: "AIzaSyCdLFMlgRxj82fC9AvBB58VcrTMeJUWkLA", 36 | authDomain: "semana-js-expert.firebaseapp.com", 37 | projectId: "semana-js-expert", 38 | storageBucket: "semana-js-expert.appspot.com", 39 | messagingSenderId: "794971418018", 40 | appId: "1:794971418018:web:b81e5c23e3dc8ca7d3d6fb", 41 | measurementId: "G-CLPKV7RXQF" 42 | }, 43 | storageKey: 'jsexpert:storage:user' 44 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/_shared/media.js: -------------------------------------------------------------------------------- 1 | export default class Media { 2 | static async getUserAudio(audio = true) { 3 | return navigator.mediaDevices.getUserMedia({ 4 | audio 5 | }) 6 | } 7 | 8 | static createMediaStreamFake() { 9 | return new MediaStream([ 10 | Media._createEmptyAudioTrack() 11 | ]) 12 | } 13 | 14 | static _createEmptyAudioTrack() { 15 | const audioContext = new AudioContext() 16 | const oscillator = audioContext.createOscillator() 17 | const destination = oscillator.connect(audioContext.createMediaStreamDestination()) 18 | oscillator.start() 19 | const [track] = destination.stream.getAudioTracks() 20 | 21 | return Object.assign(track, { enabled: false }) 22 | } 23 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/_shared/socketBuilder.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | 4 | export default class SocketBuilder { 5 | constructor({ socketUrl, namespace }) { 6 | this.socketUrl = `${socketUrl}/${namespace}` 7 | 8 | this.onUserConnected = () => { } 9 | this.onUserDisconnected = () => { } 10 | } 11 | 12 | setOnUserConnected(fn) { 13 | this.onUserConnected = fn 14 | 15 | return this 16 | } 17 | 18 | setOnUserDisconnected(fn) { 19 | this.onUserDisconnected = fn 20 | 21 | return this 22 | } 23 | 24 | build() { 25 | const socket = globalThis.io.connect(this.socketUrl, { 26 | withCredentials: false 27 | }) 28 | 29 | socket.on('connect', () => console.log('conectei!')) 30 | 31 | socket.on(constants.events.USER_CONNECTED, this.onUserConnected) 32 | socket.on(constants.events.USER_DISCONNECTED, this.onUserDisconnected) 33 | 34 | return socket 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/_shared/userDb.js: -------------------------------------------------------------------------------- 1 | import { constants } from "./constants.js" 2 | 3 | const FIELD = constants.storageKey 4 | 5 | export default class UserDb { 6 | static insert(data) { 7 | localStorage.setItem(FIELD, JSON.stringify(data)) 8 | } 9 | 10 | static get() { 11 | const result = localStorage.getItem(FIELD) 12 | return JSON.parse(result || "{}") 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/controller.js: -------------------------------------------------------------------------------- 1 | export default class LobbyController { 2 | constructor({ socketBuilder, user, view }) { 3 | this.socketBuilder = socketBuilder 4 | this.user = user 5 | this.view = view 6 | 7 | this.socket = {} 8 | } 9 | 10 | static initialize(deps) { 11 | return new LobbyController(deps)._init() 12 | } 13 | 14 | async _init() { 15 | this._setupViewEvents() 16 | this.socket = this._setupSocket() 17 | } 18 | 19 | _setupViewEvents() { 20 | this.view.updateUserImage(this.user) 21 | this.view.configureCreateRoomButton() 22 | 23 | } 24 | _setupSocket() { 25 | return this.socketBuilder 26 | .setOnLobbyUpdated(this.onLobbyUpdated()) 27 | .build() 28 | } 29 | 30 | onLobbyUpdated() { 31 | return (rooms) => { 32 | console.log('rooms', rooms) 33 | this.view.updateRoomList(rooms) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/entities/room.js: -------------------------------------------------------------------------------- 1 | class Attendee { 2 | constructor({ id, img, username }) { 3 | this.id = id 4 | this.img = img 5 | this.username = username 6 | } 7 | } 8 | 9 | export default class Room { 10 | constructor({ 11 | id, 12 | topic, 13 | subTopic, 14 | roomLink, 15 | attendeesCount, 16 | speakersCount, 17 | featuredAttendees, 18 | owner, 19 | }) { 20 | 21 | this.id = id 22 | this.topic = topic 23 | this.subTopic = subTopic || "Semana JS Expert 4.0" 24 | this.attendeesCount = attendeesCount 25 | this.speakersCount = speakersCount 26 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 27 | this.owner = new Attendee(owner) 28 | this.roomLink = roomLink 29 | } 30 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js"; 2 | import UserDb from "../../_shared/userDb.js"; 3 | import LobbyController from "./controller.js"; 4 | import LobbySocketBuilder from "./util/lobbySocketBuilder.js"; 5 | import View from "./view.js"; 6 | 7 | const user = UserDb.get() 8 | if(!Object.keys(user).length) { 9 | View.redirectToLogin() 10 | } 11 | 12 | const socketBuilder = new LobbySocketBuilder({ 13 | socketUrl: constants.socketUrl, 14 | namespace: constants.socketNamespaces.lobby 15 | }) 16 | 17 | const dependencies = { 18 | socketBuilder, 19 | user, 20 | view: View 21 | } 22 | LobbyController.initialize(dependencies) 23 | .catch(error => { 24 | alert(error.message) 25 | }) -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/templates/lobbyItem.js: -------------------------------------------------------------------------------- 1 | import Room from "../entities/room.js"; 2 | 3 | function createFeaturedSpeakersTemplate(featuredAttendees) { 4 | if(!featuredAttendees.length) return ''; 5 | 6 | const attendees = featuredAttendees.map((attende) => { 7 | return ` 8 |
  • ${attende.username} 💬
  • 9 | ` 10 | }) 11 | 12 | return attendees.join('') 13 | } 14 | 15 | 16 | export default function getTemplate(room = new Room()) { 17 | const { owner } = room 18 | 19 | return ` 20 | 21 |
    22 | 23 | ${room.subTopic} 24 | 25 | 26 |

    27 |

    28 | ${room.topic} 29 |

    30 |
    31 |
    32 | ${owner.username} 33 |
    34 |
    35 |
      36 | ${createFeaturedSpeakersTemplate(room.featuredAttendees)} 37 | 38 | ${room.attendeesCount} / ${room.speakersCount} 39 | 40 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | ` 47 | } 48 | -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/util/lobbySocketBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class LobbySocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onLobbyUpdated = () => { } 9 | } 10 | 11 | setOnLobbyUpdated(fn) { 12 | this.onLobbyUpdated = fn 13 | 14 | return this 15 | } 16 | 17 | build() { 18 | const socket = super.build() 19 | 20 | socket.on(constants.events.LOBBY_UPDATED, this.onLobbyUpdated) 21 | 22 | return socket; 23 | } 24 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/lobby/src/view.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js" 2 | import Room from "./entities/room.js" 3 | import getTemplate from "./templates/lobbyItem.js" 4 | 5 | const roomGrid = document.getElementById('roomGrid') 6 | 7 | const btnCreateRoomWithTopic = document.getElementById('btnCreateRoomWithTopic') 8 | const btnCreateRoomWithoutTopic = document.getElementById('btnCreateRoomWithoutTopic') 9 | const txtTopic = document.getElementById('txtTopic') 10 | const imgUser = document.getElementById('imgUser') 11 | 12 | export default class View { 13 | static clearRoomList() { 14 | roomGrid.innerHTML = '' 15 | } 16 | static generateRoomLink({ id, topic }) { 17 | return `./../room/index.html?id=${id}&topic=${topic}` 18 | } 19 | 20 | static redirectToLogin() { 21 | window.location = constants.pages.login 22 | } 23 | 24 | static redirectToRoom(topic = '') { 25 | // https://stackoverflow.com/a/44078785/4087199 26 | const id = Date.now().toString(36) + Math.random().toString(36).substring(2) 27 | window.location = View.generateRoomLink({ 28 | id, 29 | topic 30 | }) 31 | } 32 | 33 | static configureCreateRoomButton() { 34 | btnCreateRoomWithoutTopic.addEventListener('click', () => { 35 | View.redirectToRoom() 36 | }) 37 | btnCreateRoomWithTopic.addEventListener('click', () => { 38 | const topic = txtTopic.value 39 | View.redirectToRoom(topic) 40 | 41 | }) 42 | } 43 | static updateRoomList(rooms) { 44 | View.clearRoomList() 45 | 46 | rooms.forEach(room => { 47 | const params = new Room({ 48 | ...room, 49 | roomLink: View.generateRoomLink(room) 50 | }) 51 | const htmlTemplate = getTemplate(params) 52 | 53 | roomGrid.innerHTML += htmlTemplate 54 | }) 55 | } 56 | 57 | static updateUserImage({ img, username }) { 58 | imgUser.src = img 59 | imgUser.al = username 60 | } 61 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/login/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clubhouse - Semana JSExpert 9 | 10 | 11 | 12 | 13 | 14 |
    15 |

    16 | 🎉 17 | Welcome! 18 |

    19 |

    20 | We're working hard to get Clubhouse ready for everyone! While we wrap up the finishing touches, we're adding people gradually to make sure nothing breaks. :) 21 |

    22 |

    23 | Anyone can join with an invite from an existing user -- or reserve your username and we'll text you if you have a friend on the app who can let you in. We are so grateful you're here and can't wait to have you join us! 24 | 🙏 25 |

    26 | 30 | 31 | 32 |
    33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | { 11 | try { 12 | const result = await firebase 13 | .auth() 14 | .signInWithPopup(provider) 15 | const { user } = result; 16 | const userData = { 17 | img: user.photoURL, 18 | username: user.displayName 19 | } 20 | 21 | UserDb.insert(userData) 22 | redirectToLobby() 23 | } catch (error) { 24 | alert(JSON.stringify(error)) 25 | console.error('error', error) 26 | } 27 | 28 | } 29 | } 30 | 31 | const currentUser = UserDb.get() 32 | if(Object.keys(currentUser).length) { 33 | redirectToLobby() 34 | } 35 | 36 | const { firebaseConfig } = constants 37 | // Initialize Firebase 38 | firebase.initializeApp(firebaseConfig); 39 | firebase.analytics(); 40 | 41 | var provider = new firebase.auth.GithubAuthProvider(); 42 | provider.addScope('read:user'); 43 | 44 | const btnLogin = document.getElementById('btnLogin') 45 | btnLogin.addEventListener('click', onLogin({ provider, firebase })) -------------------------------------------------------------------------------- /aulas/aula05/app/pages/room/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.img = img || "" 5 | this.isSpeaker = isSpeaker 6 | this.roomId = roomId 7 | this.peerId = peerId 8 | 9 | const name = username || "Usuário Anônimo" 10 | this.username = name 11 | 12 | const [firstName, lastName] = name.split(/\s/) 13 | this.firstName = firstName 14 | this.lastName = lastName 15 | } 16 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/room/src/entities/userStream.js: -------------------------------------------------------------------------------- 1 | export default class UserStream { 2 | constructor({ stream, isFake }) { 3 | this.stream = stream 4 | this.isFake = isFake 5 | } 6 | } -------------------------------------------------------------------------------- /aulas/aula05/app/pages/room/src/index.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../../_shared/constants.js" 2 | import Media from "../../_shared/media.js" 3 | import PeerBuilder from "../../_shared/peerBuilder.js" 4 | import UserDb from "../../_shared/userDb.js" 5 | import RoomController from "./controller.js" 6 | import RoomService from "./service.js" 7 | import RoomSocketBuilder from "./util/roomSocket.js" 8 | import View from "./view.js" 9 | 10 | 11 | 12 | const user = UserDb.get() 13 | if(!Object.keys(user).length) { 14 | View.redirectToLogin() 15 | } 16 | 17 | 18 | const urlParams = new URLSearchParams(window.location.search) 19 | const keys = ['id', 'topic'] 20 | const urlData = keys.map((key) => [key, urlParams.get(key)]) 21 | 22 | 23 | const roomInfo = { 24 | room: { ...Object.fromEntries(urlData) }, 25 | user 26 | } 27 | const peerBuilder = new PeerBuilder({ 28 | peerConfig: constants.peerConfig 29 | }) 30 | 31 | const socketBuilder = new RoomSocketBuilder({ 32 | socketUrl: constants.socketUrl, 33 | namespace: constants.socketNamespaces.room 34 | }) 35 | const roomService = new RoomService({ 36 | media: Media 37 | }) 38 | 39 | const dependencies = { 40 | view: View, 41 | socketBuilder, 42 | roomInfo, 43 | roomService, 44 | peerBuilder 45 | } 46 | 47 | 48 | RoomController.initialize(dependencies) 49 | .catch(error => { 50 | alert(error.message) 51 | }) 52 | 53 | 54 | -------------------------------------------------------------------------------- /aulas/aula05/app/pages/room/src/templates/attendeeTemplate.js: -------------------------------------------------------------------------------- 1 | import Attendee from "../entities/attendee.js" 2 | const speakericon = 'File icon' 3 | 4 | export default function getTemplate(attendee = new Attendee()) { 5 | const speakerTemplate = attendee.isSpeaker ? speakericon : "" 6 | 7 | return ` 8 |
    9 |
    10 | ${attendee.username} 11 |
    12 |

    13 | ${speakerTemplate} 14 | ${attendee.firstName} 15 |

    16 |
    17 | `; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /aulas/aula05/app/pages/room/src/util/roomSocket.js: -------------------------------------------------------------------------------- 1 | 2 | import { constants } from "../../../_shared/constants.js"; 3 | import SocketBuilder from "../../../_shared/socketBuilder.js"; 4 | 5 | export default class RoomSocketBuilder extends SocketBuilder { 6 | constructor({ socketUrl, namespace }) { 7 | super({ socketUrl, namespace }) 8 | this.onRoomUpdated = () => { } 9 | this.onUserProfileUpgrade = () => { } 10 | this.onSpeakRequested = () => {} 11 | } 12 | 13 | setOnRoomUpdated(fn) { 14 | this.onRoomUpdated = fn 15 | 16 | return this 17 | } 18 | 19 | setOnUserProfileUpgrade(fn) { 20 | this.onUserProfileUpgrade = fn 21 | 22 | return this 23 | } 24 | setOnSpeakRequested(fn) { 25 | this.onSpeakRequested = fn 26 | return this 27 | } 28 | 29 | 30 | build() { 31 | const socket = super.build() 32 | 33 | socket.on(constants.events.LOBBY_UPDATED, this.onRoomUpdated) 34 | socket.on(constants.events.UPGRADE_USER_PERMISSION, this.onUserProfileUpgrade) 35 | socket.on(constants.events.SPEAK_REQUEST, this.onSpeakRequested) 36 | 37 | return socket; 38 | } 39 | } -------------------------------------------------------------------------------- /aulas/aula05/peerjs-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peerjs-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "PORT=9000 npm start", 8 | "start": "npx peerjs --port $PORT --key peerjs --path /", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "peer": "^0.6.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /aulas/aula05/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node src/index.js", 9 | "dev": "npx nodemon src/index.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "socket.io": "^4.0.2" 17 | }, 18 | "engines": { 19 | "node": "16" 20 | }, 21 | "devDependencies": { 22 | "nodemon": "^2.0.7" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /aulas/aula05/server/src/controllers/lobbyController.js: -------------------------------------------------------------------------------- 1 | import { constants } from "../util/constants.js" 2 | 3 | export default class LobbyController { 4 | constructor({ activeRooms, roomsListener }) { 5 | this.activeRooms = activeRooms 6 | this.roomsListener = roomsListener 7 | } 8 | 9 | onNewConnection(socket) { 10 | const { id } = socket 11 | console.log('[Lobby] connection stablished with', id) 12 | this.#updateLobbyRooms(socket, [...this.activeRooms.values()]) 13 | 14 | this.#activateEventProxy(socket) 15 | } 16 | 17 | #activateEventProxy(socket) { 18 | this.roomsListener.on(constants.event.LOBBY_UPDATED, rooms => { 19 | this.#updateLobbyRooms(socket, rooms) 20 | }) 21 | } 22 | #updateLobbyRooms(socket, activeRooms) { 23 | socket.emit(constants.event.LOBBY_UPDATED, activeRooms) 24 | } 25 | 26 | getEvents() { 27 | const functions = Reflect.ownKeys(LobbyController.prototype) 28 | .filter(fn => fn !== 'constructor') 29 | .map(name => [name, this[name].bind(this)]) 30 | 31 | return new Map(functions) 32 | } 33 | } -------------------------------------------------------------------------------- /aulas/aula05/server/src/entities/attendee.js: -------------------------------------------------------------------------------- 1 | export default class Attendee { 2 | constructor({ id, username, img, isSpeaker, roomId, peerId }) { 3 | this.id = id 4 | this.username = username 5 | this.img = img 6 | this.isSpeaker = isSpeaker 7 | this.roomId = roomId 8 | this.peerId = peerId 9 | } 10 | } -------------------------------------------------------------------------------- /aulas/aula05/server/src/entities/room.js: -------------------------------------------------------------------------------- 1 | import Attendee from "./attendee.js" 2 | 3 | export default class Room { 4 | constructor({ id, topic, attendeesCount, speakersCount, featuredAttendees, owner, users }) { 5 | 6 | this.id = id 7 | this.topic = topic 8 | this.attendeesCount = attendeesCount 9 | this.speakersCount = speakersCount 10 | this.featuredAttendees = featuredAttendees?.map(attendee => new Attendee(attendee)) 11 | this.owner = new Attendee(owner) 12 | this.users = users 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula05/server/src/index.js: -------------------------------------------------------------------------------- 1 | import RoomsController from "./controllers/roomsController.js"; 2 | import SocketServer from "./util/socket.js"; 3 | import Event from 'events' 4 | import { constants } from './util/constants.js' 5 | import LobbyController from "./controllers/lobbyController.js"; 6 | 7 | const port = process.env.PORT || 3000 8 | const socketServer = new SocketServer({ port }) 9 | const server = await socketServer.start() 10 | 11 | 12 | const roomsPubSub = new Event() 13 | 14 | const roomsController = new RoomsController({ 15 | roomsPubSub 16 | }) 17 | const lobbyController = new LobbyController({ 18 | activeRooms: roomsController.rooms, 19 | roomsListener: roomsPubSub 20 | }) 21 | 22 | const namespaces = { 23 | room: { controller: roomsController, eventEmitter: new Event() }, 24 | lobby: { controller: lobbyController, eventEmitter: roomsPubSub }, 25 | } 26 | 27 | // namespaces.room.eventEmitter.on( 28 | // 'userConnected', 29 | // namespaces.room.controller.onNewConnection.bind(namespaces.room.controller) 30 | // ) 31 | 32 | // namespaces.room.eventEmitter.emit('userConnected', { id: '001' }) 33 | // namespaces.room.eventEmitter.emit('userConnected', { id: '002' }) 34 | // namespaces.room.eventEmitter.emit('userConnected', { id: '003' }) 35 | 36 | const routeConfig = Object.entries(namespaces) 37 | .map(([namespace, { controller, eventEmitter }]) => { 38 | const controllerEvents = controller.getEvents() 39 | eventEmitter.on( 40 | constants.event.USER_CONNECTED, 41 | controller.onNewConnection.bind(controller) 42 | ) 43 | 44 | return { 45 | [namespace]: { events: controllerEvents, eventEmitter } 46 | } 47 | 48 | }) 49 | 50 | 51 | socketServer.attachEvents({ routeConfig }) 52 | 53 | console.log('socket server is running at', server.address().port) -------------------------------------------------------------------------------- /aulas/aula05/server/src/util/constants.js: -------------------------------------------------------------------------------- 1 | export const constants = { 2 | event: { 3 | USER_CONNECTED: 'userConnection', 4 | USER_DISCONNECTED: 'userDisconnection', 5 | 6 | JOIN_ROOM: 'joinRoom', 7 | 8 | LOBBY_UPDATED: 'lobbyUpdated', 9 | UPGRADE_USER_PERMISSION: 'upgradeUserPermission', 10 | 11 | SPEAK_REQUEST: 'speakRequest', 12 | SPEAK_ANSWER: 'speakAnswer' 13 | } 14 | } -------------------------------------------------------------------------------- /aulas/aula05/server/src/util/customMap.js: -------------------------------------------------------------------------------- 1 | export default class CustomMap extends Map { 2 | #observer 3 | #customMapper 4 | constructor({ observer, customMapper }) { 5 | super() 6 | 7 | this.#observer = observer 8 | this.#customMapper = customMapper 9 | } 10 | * values() { 11 | for(const value of super.values()) { 12 | yield this.#customMapper(value) 13 | } 14 | } 15 | set(...args) { 16 | const result = super.set(...args) 17 | this.#observer.notify(this) 18 | 19 | return result 20 | } 21 | 22 | delete(...args) { 23 | const result = super.delete(...args) 24 | this.#observer.notify(this) 25 | 26 | return result 27 | } 28 | } -------------------------------------------------------------------------------- /aulas/aula05/server/src/util/socket.js: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | import { Server } from 'socket.io' 3 | import { constants } from './constants.js' 4 | 5 | export default class SocketServer { 6 | #io 7 | constructor({ port }) { 8 | this.port = port 9 | this.namespaces = {} 10 | } 11 | // [ 12 | // { 13 | // room: { 14 | // events, 15 | // eventEmitter 16 | // } 17 | // } 18 | // ] 19 | 20 | attachEvents({ routeConfig }) { 21 | for (const routes of routeConfig) { 22 | for (const [namespace, { events, eventEmitter }] of Object.entries(routes)) { 23 | const route = this.namespaces[namespace] = this.#io.of(`/${namespace}`) 24 | route.on('connection', socket => { 25 | for (const [functionName, functionValue] of events) { 26 | socket.on(functionName, (...args) => functionValue(socket, ...args)) 27 | } 28 | 29 | eventEmitter.emit(constants.event.USER_CONNECTED, socket) 30 | }) 31 | } 32 | } 33 | } 34 | 35 | async start() { 36 | const server = http.createServer((request, response) => { 37 | response.writeHead(200, { 38 | 'Access-Control-Allow-Origin': '*', 39 | 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' 40 | }) 41 | 42 | response.end('hey there!!') 43 | }) 44 | 45 | this.#io = new Server(server, { 46 | cors: { 47 | origin: '*', 48 | credentials: false 49 | } 50 | }) 51 | 52 | 53 | 54 | return new Promise((resolve, reject) => { 55 | server.on('error', reject) 56 | 57 | server.listen(this.port, () => resolve(server)) 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /welcome.js: -------------------------------------------------------------------------------- 1 | console.log('Seja bem vindo(a) e não esqueça de deixar o star nesse projeto!') --------------------------------------------------------------------------------