├── .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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
27 |
28 | Login with GitHub
29 |
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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
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 |
27 |
28 | Login with GitHub
29 |
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 = ' '
3 |
4 | export default function getTemplate(attendee = new Attendee()) {
5 | const speakerTemplate = attendee.isSpeaker ? speakericon : ""
6 |
7 | return `
8 |
9 |
10 |
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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
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 |
27 |
28 | Login with GitHub
29 |
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 = ' '
3 |
4 | export default function getTemplate(attendee = new Attendee()) {
5 | const speakerTemplate = attendee.isSpeaker ? speakericon : ""
6 |
7 | return `
8 |
9 |
10 |
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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
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 |
27 |
28 | Login with GitHub
29 |
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 = ' '
3 |
4 | export default function getTemplate(attendee = new Attendee()) {
5 | const speakerTemplate = attendee.isSpeaker ? speakericon : ""
6 |
7 | return `
8 |
9 |
10 |
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 |
12 |
13 | ### Página de Salas
14 |
15 |
16 |
17 | ### Página de Sala
18 |
19 |
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 |
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 |
27 |
28 | Login with GitHub
29 |
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 = ' '
3 |
4 | export default function getTemplate(attendee = new Attendee()) {
5 | const speakerTemplate = attendee.isSpeaker ? speakericon : ""
6 |
7 | return `
8 |
9 |
10 |
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!')
--------------------------------------------------------------------------------