Skip to content

Instantly share code, notes, and snippets.

@evlymn
Last active March 10, 2023 11:45
Show Gist options
  • Save evlymn/f0842591299d525733b9e4e584b63e99 to your computer and use it in GitHub Desktop.
Save evlymn/f0842591299d525733b9e4e584b63e99 to your computer and use it in GitHub Desktop.
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
}
.container-login {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.register-section {
display: none;
}
.avatar {
width: 90px;
height: 90px;
border: 2px solid rgb(218, 215, 215);
border-radius: 45px;
}
.avatar-nav,
.avatar-list {
width: 50px;
height: 50px;
border: 2px solid rgb(218, 215, 215);
border-radius: 50px;
}
input[type='file'] {
display: none;
}
.progress {
display: none;
}
#logged-section,
#unlogged-section {
display: none;
}
.navbar-brand div {
display: flex;
justify-content: center;
align-items: center;
}
.navbar-brand div div {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: 10px;
}
.navbar-brand div div small {
font-size: small;
color: rgb(165, 165, 165);
}
#chat-list {
display: flex;
flex-direction: column-reverse;
}
.favorite {
display: flex;
justify-items: center;
}
.favorite small {
font-size: smaller;
color: rgb(130, 130, 130);
margin-left: 5px;
}
.favorite-img {
cursor: pointer;
}
.post-list {
background-color: rgb(249, 249, 249);
margin: 10px;
border-radius: 5px;
}
.post-text {
margin: 10px;
}
#chat-id {
display: flex;
flex-wrap: nowrap;
align-items: center;
}
#chat-id img {
margin: 10px;
}
.room-action-text {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 10px;
}
#display-name {
font-weight: bold;
display: flex;
flex-direction: row;
width: 100%;
justify-content: space-between;
}
#display-name small {
color: rgb(135, 135, 135);
font-size: x-small;
margin-right: 10px;
font-weight: lighter;
}
.user-link,
.goback-link,
.a-room-link {
text-decoration: none;
color: black;
}
.goback-link {
width: auto;
font-weight: bold;
display: none;
}
.goback-link img {
display: flex;
flex-wrap: nowrap;
width: 20px;
}
.room-action-text {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 10px;
}
</style>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous">
</script>
</head>
<body>
<section id="logged-section">
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
<div class="container-fluid">
<div class="navbar-brand">
<div>
<img id="logged-photoURL" class="avatar-nav"></img>
<div>
<label id="logged-displayName"></label>
<small id="logged-email"></small>
</div>
</div>
</div>
<button id="logout-button" class="btn btn-dark my-2 my-sm-0 " type="button"> Logout ⤶</button>
</div>
</div>
</nav>
<main class="container">
<div class="bg-light p-5 rounded">
<div id="post-container">
<h4>No que você está pensando?</h4>
<div class="mb-3">
<form id="form-chat">
<textarea class="form-control" id="post-text" rows="3"></textarea>
<div class="m-2">
<div>
<input class="form-check-input " type="checkbox" checked id="post-enter">
<label class="form-check-label mb-2" for="post-enter">
postar com enter
</label>
</div>
</div>
<div class="d-grid gap-2">
<button type="button" id="post-button" class="btn btn-dark">Postar</button>
</div>
</form>
</div>
</div>
<div class="">
<a class="a-room-link " data-bs-toggle="collapse" href="#collapseRoom" role="button"
aria-expanded="false" aria-controls="collapseRoom">
Criar/Participar de uma sala ⬊
</a>
<div class="collapse mt-2" id="collapseRoom">
<div class="card card-body">
<div class="row g-3">
<div class="col-auto">
<input type="text" class="form-control" id="room-create-text"
placeholder="Nome da Sala">
</div>
<div class="col-auto">
<button id="room-create-button" class="btn btn-link-dark mb-3">ir para a
sala →</button>
</div>
</div>
</div>
</div>
</div>
<div class="room-action-text">
<a class="goback-link mb-3" href="?">← Voltar</a>
<h6 id="room-name"> </h6>
</div>
</div>
</div>
<div class="container">
<div id="chat-list">
</div>
</div>
</main>
</section>
<section id="unlogged-section">
<div class="container-login">
<div class="card text-center">
<div class="card-header">
Login
</div>
<div class="card-body">
<form id="form-login">
<div class="Login">
<section class="register-section">
<div class="mb-3">
<img id="avatar-register" class="avatar"
src="https://firebasestorage.googleapis.com/v0/b/app-emendes-com.appspot.com/o/apresentacao%2F1814074_draw_edit_pencile_write_icon%20(1).png?alt=media&token=d004283b-50c1-47bd-98ca-6b6c3fe8c273"></img>
</div>
<small class="mb-3">Selecione uma imagem de perfil</small>
<div class="mb-3">
<input type="text" class="form-control" id="name" placeholder="nome" />
</div>
</section>
<div class="mb-3">
<input type="email" class="form-control" id="email" required placeholder="email" />
</div>
<div class="mb-3">
<input type="password" class="form-control" id="password" required
placeholder="senha" />
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary" id="login-button" type="submit">logar</button>
</div>
<div class="progress mt-3">
<div class="progress-bar bg-info" role="progressbar" style="width: 50%;"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</form>
</div>
<div class="card-footer text-muted">
<button type="button" id="register-button" class="btn btn-link">Registrar-se</button>
</div>
</div>
</div>
</section>
<input type="file" />
</body>
</html>
<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.6.6/firebase-app.js';
import { getAuth, signOut, updateProfile, signInWithEmailAndPassword, createUserWithEmailAndPassword, onAuthStateChanged } from 'https://www.gstatic.com/firebasejs/9.6.6/firebase-auth.js';
import { getStorage, ref as refs, uploadBytesResumable, getDownloadURL } from 'https://www.gstatic.com/firebasejs/9.6.6/firebase-storage.js';
import { getDatabase, ref, remove, set, get, onValue, onChildRemoved, child, push, onChildAdded, update, equalTo, query, limitToLast, orderByChild } from 'https://www.gstatic.com/firebasejs/9.6.6/firebase-database.js';
const firebaseConfig = {
apiKey: "AIzaSyD7fKCjH9J812nbHTTD6LiF0JnHPeW9fcE",
authDomain: "tests-evlymn-dev.firebaseapp.com",
databaseURL: "https://tests-evlymn-dev.firebaseio.com",
projectId: "tests-evlymn-dev",
storageBucket: "tests-evlymn-dev.appspot.com",
messagingSenderId: "1077133737396",
appId: "1:1077133737396:web:759e44c30aeb2368"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth();
const storage = getStorage();
const db = getDatabase();
const urlParams = new URLSearchParams(window.location.search);
const roomParam = urlParams.get('sala');
const userParam = urlParams.get('user');
const badWords = [];
let isLogin = true
let fileToUpload = null;
let uploading = false;
let collectionRef = 'apresentacao/posts/';
let postWithEnter = true;
const form = document.querySelector('#form-login');
const loginButton = document.querySelector('#login-button');
const registerButton = document.querySelector('#register-button');
const registerSection = document.querySelector('.register-section');
const avatarRegister = document.querySelector('#avatar-register');
const inputFile = document.querySelector('input[type=file]');
const progress = document.querySelector('.progress');
const progressBar = document.querySelector('.progress-bar');
const loggedSection = document.querySelector('#logged-section');
const unloggedSection = document.querySelector('#unlogged-section');
const loggedDisplayName = document.querySelector('#logged-displayName');
const loggedPhotoURL = document.querySelector('#logged-photoURL');
const loggedEmail = document.querySelector('#logged-email');
const logoutButton = document.querySelector('#logout-button');
const postButton = document.querySelector("#post-button");
const postText = document.querySelector('#post-text');
const roomName = document.querySelector('#room-name');
const roomCreateButton = document.querySelector('#room-create-button');
const roomCreateText = document.querySelector('#room-create-text');
progress.style.display = 'none';
async function recuperarBadWords() {
const snapshot = await get(child(ref(db), 'bad_words/words/'));
snapshot.val().forEach(data => {
badWords.push(data);
})
}
recuperarBadWords();
function mostrarEsconderCampoTexto(user) {
if (userParam) {
document.querySelector('.goback-link').style.display = 'block';
}
if (userParam && userParam != user.uid) {
document.querySelector('#post-container').style.display = 'none';
}
}
function definirParametrosParaRoom() {
if (roomParam) {
collectionRef = 'apresentacao/rooms/' + roomParam + '/';
roomName.innerHTML = `Você está na sala: <b>${roomParam}</b>`;
document.querySelector('.goback-link').style.display = 'block';
}
}
function definirParametrosParaUser() {
if (userParam) {
collectionRef = 'apresentacao/by_users/' + userParam + '/';
}
}
function selecionarSomenteUmParametro() {
if (urlParams.has('sala') ^ urlParams.has('user')) {
definirParametrosParaRoom();
definirParametrosParaUser();
}
}
selecionarSomenteUmParametro();
function aoMudarStatusDeAutenticacao() {
onAuthStateChanged(auth, (user) => {
mostrarEsconderLoginProfile(user);
if (user) {
quandoNovoPostForAdicionado(user);
quandoValorDaBaseForAlteradoPreencherLista();
mostrarEsconderCampoTexto(user);
}
});
}
aoMudarStatusDeAutenticacao();
function quandoNovoPostForAdicionado(user) {
const postRef = query(ref(db, collectionRef), orderByChild('uid'), equalTo(user.uid));
onChildAdded(postRef, (data) => {
if (!data.val().adjusted) {
let newPost = data.val().post.toString().replace(/\n/g, '<br>');
let hasBadWord = false;
hasBadWord = badWords.some(w => newPost.toLowerCase().includes(w.word.toLowerCase()))
badWords.forEach(w => {
const regExp = new RegExp(eval("/" + w.word + "/ig"));
newPost = newPost.replace(regExp, w.replace ? w.replace : '****');;
})
atualizarRegistroNaBaseDados(collectionRef + data.key, { hasBadWord: hasBadWord, adjusted: true, post: newPost });
const newData = {...data.val()};
newData.post = newPost;
desnormalizarDadosPorUsuario(newData, data.key);
}
});
}
function quandoUmRegistroForDeletado() {
const deleteRef = query(ref(db, collectionRef), orderByChild('uid'));
onChildRemoved(deleteRef, (snapshot) => {
removerRegistroDaBaseDados(`apresentacao/favorites/${snapshot.key}`);
removerRegistroDaBaseDados(`apresentacao/by_users/${auth.currentUser.uid}/${snapshot.key}`);
});
}
quandoUmRegistroForDeletado();
function desnormalizarDadosPorUsuario(data, key) {
if (!roomParam) {
criarOuDefinirRegistroNaBaseDados(`apresentacao/by_users/${data.uid}/${key}`, data);
}
}
function limparListaDaTela(chatList) {
chatList.replaceChildren('')
}
function quandoValorDaBaseForAlteradoPreencherLista() {
const postRef = query(ref(db, collectionRef), orderByChild('time'), limitToLast(100));
const chatList = document.querySelector('#chat-list');
onValue(postRef, (snapshot) => {
limparListaDaTela(chatList);
snapshot.forEach(data => {
montarMensagemDeVisualizaoDeSalaOuUsuario(data);
const postsElement = document.createElement('div');
postsElement.className = 'post-list';
const userElement = document.createElement('div');
userElement.id = 'chat-id';
const avatar = document.createElement('img');
avatar.src = data.val().photoURL;
avatar.className = 'avatar-nav';
userElement.appendChild(avatar);
const displayName = document.createElement('div');
displayName.id = 'display-name';
displayName.innerHTML = `<a class="user-link" title="ver mensagens de ${data.val().displayName}" href="?user=${data.val().uid}">${data.val().displayName} ▸ </a>`;
userElement.appendChild(displayName);
const dateTimeSmall = document.createElement('small');
mostrarDataHoraDaPostagem(data, dateTimeSmall);
mostrarOuNaoBotaoDeletar(data, dateTimeSmall);
displayName.appendChild(dateTimeSmall);
const post = document.createElement('div');
post.className = 'post-text';
post.innerHTML = data.val().post;
postsElement.appendChild(userElement);
postsElement.appendChild(post);
recuperarEApresentarFavoritos(data, post);
chatList.appendChild(postsElement);
})
})
}
function montarMensagemDeVisualizaoDeSalaOuUsuario(data) {
if (userParam && userParam != auth.currentUser.uid) {
roomName.innerHTML = '<b>Você está vendo as postagens de :</b> ' + data.val().displayName;
} else if (userParam && userParam == auth.currentUser.uid) {
roomName.innerHTML = `Olá <b>${data.val().displayName}</b>, você está vendo as suas postagens!`;
}
}
function mostrarDataHoraDaPostagem(data, dateTimeSmall) {
const postDate = new Date(data.val().time);
const postDateTimeString = `${postDate.getDate()}/${postDate.getMonth() + 1}/${postDate.getFullYear()} ${postDate.getUTCHours()}:${postDate.getUTCMinutes()}`;
dateTimeSmall.style.display = 'flex';
dateTimeSmall.style.flexWrap = 'nowrap';
dateTimeSmall.style.alignItems = 'center';
dateTimeSmall.innerHTML = postDateTimeString;
}
function mostrarOuNaoBotaoDeletar(data, dateTimeSmall) {
if (data.val().uid == auth.currentUser.uid) {
const deleteGroup = document.createElement('div');
deleteGroup.className = 'btn-group';
const buttonDelete = document.createElement('button');
buttonDelete.className = 'btn btn-link dropdown-toggle';
buttonDelete.id = 'dd-' + data.key;
buttonDelete.setAttribute('data-bs-toggle', 'dropdown');
buttonDelete.ariaExpanded = false;
deleteGroup.appendChild(buttonDelete);
const ulDelete = document.createElement('ul');
ulDelete.className = 'dropdown-menu';
ulDelete.setAttribute('aria-labelledby', 'dd-' + data.key);
const liDelete = document.createElement('li');
const liDeleteButton = document.createElement('button');
liDeleteButton.id = 'button-' + data.key;
liDeleteButton.className = 'dropdown-item';
liDeleteButton.innerHTML = 'deletar';
liDeleteButton.addEventListener('click', (e) => {
removerRegistroDaBaseDados(collectionRef + data.key);
});
liDelete.appendChild(liDeleteButton);
ulDelete.appendChild(liDelete);
deleteGroup.appendChild(ulDelete);
dateTimeSmall.appendChild(deleteGroup);
}
}
function recuperarEApresentarFavoritos(data, post) {
const favorite = document.createElement('div');
favorite.className = 'favorite';
const favoriteCount = document.createElement('small');
const favoriteImg = document.createElement('img');
favoriteImg.style.width = '20px';
favoriteImg.className = 'favorite-img';
const favRef = query(ref(db, 'apresentacao/favorites/' + data.key), orderByChild('uid'));
onValue(favRef, (favs) => {
favoriteImg.src = 'https://firebasestorage.googleapis.com/v0/b/app-emendes-com.appspot.com/o/apresentacao%2Fhear-gray.svg?alt=media&token=6fda224e-1ae5-44eb-98e9-5b5c2fc1419e';
favoriteCount.innerHTML = '';
let isFavorite = false;
const favsList = [];
if (favs.exists()) {
if (Object.keys(favs.val()).some(uid => uid == auth.currentUser.uid)) {
isFavorite = true;
favoriteImg.src = 'https://firebasestorage.googleapis.com/v0/b/app-emendes-com.appspot.com/o/apresentacao%2Fhear-red.svg?alt=media&token=b1fcc706-872a-460b-8c94-184bbc6e3968';
}
if (favs.size > 0) {
favoriteCount.innerHTML = favs.size;
favsList.push('Curtiram:');
favs.forEach(f => {
favsList.push(f.val().displayName);
})
}
favoriteImg.title = favsList.join('\n');
}
favoriteImg.addEventListener('click', (e) => {
adicionarOuRemoverFavorito(data.key, isFavorite);
});
});
favorite.appendChild(favoriteImg);
favorite.appendChild(favoriteCount);
post.appendChild(favorite);
}
function adicionarOuRemoverFavorito(key, isFavorite) {
const path = `apresentacao/favorites/${key}/${auth.currentUser.uid}`;
if (isFavorite) {
removerRegistroDaBaseDados(path);
} else {
criarOuDefinirRegistroNaBaseDados(path, {
key: key,
uid: auth.currentUser.uid,
displayName: auth.currentUser.displayName
})
}
}
function aoClicarNoBotaoDePostar() {
postButton.addEventListener('click', (e) => {
criarNovoRegistroNaBaseDados(collectionRef, {
uid: auth.currentUser.uid,
displayName: auth.currentUser.displayName,
email: auth.currentUser.email,
photoURL: auth.currentUser.photoURL,
post: postText.value,
time: new Date().getTime(),
roomId: roomParam
}).then(() => {
console.log('post enviado!');
}).catch(e => {
console.error(e);
}).finally(() => {
postText.value = '';
})
});
}
aoClicarNoBotaoDePostar();
function aoDigitarNoCampoTextoDePostagem() {
postText.addEventListener('keyup', (e) => {
if (e.key == 'Enter' && postWithEnter) {
postButton.click();
}
})
}
aoDigitarNoCampoTextoDePostagem();
function aoCheckarPostarComEnter() {
document.querySelector('#post-enter').addEventListener('change', (e) => {
postWithEnter = e.target.checked;
})
}
aoCheckarPostarComEnter();
function aoClicarNoBotaoLogout() {
logoutButton.addEventListener('click', async (e) => {
await signOut(auth);
isLogin = false;
alternarTelasLoginCadastro();
});
}
aoClicarNoBotaoLogout();
function mostrarEsconderLoginProfile(user) {
if (user) {
if (!uploading)
mostrarDadosDePerfil();
} else {
mostrarLogin();
}
}
function mostrarLogin() {
loggedSection.style.display = 'none';
unloggedSection.style.display = 'block';
}
function mostrarDadosDePerfil() {
loggedSection.style.display = 'block';
unloggedSection.style.display = 'none';
preencherDadosDePerfilNaTela()
}
function preencherDadosDePerfilNaTela() {
loggedDisplayName.innerHTML = auth.currentUser.displayName;
if (auth.currentUser.photoURL) {
loggedPhotoURL.src = auth.currentUser.photoURL;
}
loggedEmail.innerHTML = auth.currentUser.email;
}
async function atualizarDadosDoUsuario(photoURL) {
await updateProfile(auth.currentUser, {
displayName: document.querySelector('#name').value,
photoURL: photoURL
});
uploading = false;
mostrarDadosDePerfil(auth.currentUser);
}
async function logarOuCriarUsuario(e) {
const email = e.target.email.value;
const password = e.target.password.value;
if (isLogin) {
const credentials = await signInWithEmailAndPassword(auth, email, password);
console.log('signIn', credentials)
} else {
if (fileToUpload) {
uploading = true;
const credentials = await createUserWithEmailAndPassword(auth, email, password);
console.log('createUser', credentials)
uploadFile(fileToUpload);
} else {
alert('Selecione uma imagem pra o perfil!');
}
}
}
function aoSubmeterFormulario() {
form.addEventListener('submit', async (e) => {
e.preventDefault();
logarOuCriarUsuario(e);
});
}
aoSubmeterFormulario();
function alternarTelasLoginCadastro() {
isLogin = !isLogin;
form.reset();
avatarRegister.src = 'https://firebasestorage.googleapis.com/v0/b/app-emendes-com.appspot.com/o/apresentacao%2F1814074_draw_edit_pencile_write_icon%20(1).png?alt=media&token=d004283b-50c1-47bd-98ca-6b6c3fe8c273';
if (isLogin) {
registerSection.style.display = 'none';
loginButton.innerHTML = 'Logar';
registerButton.innerHTML = 'Registrar-se';
document.querySelector('.card-header').innerHTML = 'Login';
progress.style.display = 'none';
} else {
registerSection.style.display = 'block';
loginButton.innerHTML = 'Registrar';
registerButton.innerHTML = 'Fazer Login';
document.querySelector('.card-header').innerHTML = 'Registrar usuário';
}
}
function aoClicarNoBotaoRegistrar() {
registerButton.addEventListener('click', async (e) => {
alternarTelasLoginCadastro();
});
}
aoClicarNoBotaoRegistrar();
function aoClicarNaImagemDeAvatar() {
avatarRegister.addEventListener('click', () => {
inputFile.click();
});
}
aoClicarNaImagemDeAvatar();
function aoMudarDeArquivoNoInputFile() {
inputFile.addEventListener('change', async (e) => {
fileToUpload = e.target.files[0];
let img = new Image()
img.src = window.URL.createObjectURL(event.target.files[0])
img.onload = () => {
console.log(img.width, img.height)
}
converterImagemEmBase64ParaMostrarNoAvatar();
});
}
aoMudarDeArquivoNoInputFile();
function aoClicarNoBotaoDeCriarParticiparSala() {
roomCreateButton.addEventListener('click', (e) => {
if (roomCreateText.value.trim().length > 0)
window.document.location = '?sala=' + roomCreateText.value;
})
}
aoClicarNoBotaoDeCriarParticiparSala();
async function converterImagemEmBase64ParaMostrarNoAvatar() {
const base64Img = await fileToBase64(fileToUpload);
avatarRegister.src = base64Img;
}
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
let encoded = reader.result.toString().replace(/^data:(.*,)?/, '');
if ((encoded.length % 4) > 0) {
encoded += '='.repeat(4 - (encoded.length % 4));
}
resolve(`data:${file.type};base64, ${encoded}`);
};
reader.onerror = error => reject(error);
});
}
function uploadFile(file) {
progress.style.display = 'block';
const storageRef = refs(storage, 'apresentacao/imagens/' + file.name);
const metadata = {
contentType: file.type
};
const uploadTask = uploadBytesResumable(storageRef, file, metadata);
uploadTask.on(
'state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload está em: ' + progress.toFixed(0) + '% enviado');
progressBar.style.width = progress.toFixed(0) + '%';
progressBar.innerHTML = progress.toFixed(0) + '% enviado';
switch (snapshot.state) {
case 'paused':
console.log('Upload está pausado');
break;
case 'running':
console.log('Upload está rodando');
break;
}
},
(error) => {
console.error(error);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
atualizarDadosDoUsuario(downloadURL);
console.log('arquivo disponível nesse endereço:', downloadURL);
});
}
);
}
function criarNovoRegistroNaBaseDados(path, data) {
return push(child(ref(db), path), data);
}
function criarOuDefinirRegistroNaBaseDados(path, data) {
return set(child(ref(db), path), data);
}
function atualizarRegistroNaBaseDados(path, data) {
return update(ref(db, path), data);
}
function removerRegistroDaBaseDados(path) {
return remove(ref(db, path));
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment