CaioAugustoo /
let-me-ask
💬 Aplicação web que permite interação entre usuários. Desenvolvida com React.js, TypeScript e Firebase.
34/100 healthLoading repository data…
eduardorerick / repository
Aplicação que me garantiu uma bolsa de estudos no Ignite! Desenvolvida durante a NLW com outras funcionalidades adicionadas por mim.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
Esse projeto foi desenvolvido com as seguintes tecnologias:
O letmeask é um app desenvolvido durante a NLW que permite que alguém realizando lives crie uma sala para receber perguntas, tendo maior interação com o usuário.
if (result.user) {
const { displayName, photoURL, uid} = result.user;
if(!displayName || !photoURL) {
throw new Error('Missing information from Google Account.')
};
setUser({
id: uid,
name: displayName,
avatar: photoURL
});
};
import { useContext } from 'react';
import { AuthContext } from '../contexts/AuthContext';
export function useAuth() {
const value = useContext(AuthContext)
return value
};
const roomRef = database.ref('rooms');
const firebaseRoom = await roomRef.push({
title: newRoom,
authorId: user?.id,
})
const params = useParams();
type RoomParams = {
id: string;
}
então fica:
const params = useParams<RoomParams>();
agora a função sabe quais parametros vai receber.
e agora usamos o componente criado e passamos esse parametro como uma prop:
<RoomCode code={params.id} />
Criamos uma pequena função para o botão de copiar o numero da sala
function copyRoomCodeToClipboard() {
navigator.clipboard.writeText(props.code)
}
const [newQuestion, setNewQuestion] = useState('')
e importamos também o user que guardamos com a função signInWithGoogle()
agora é só checar se a perguntava enviada tem mesmo algum conteúdo.
if (newQuestion.trim() === "") {
return
}
e verificar se o usuário está logado.
if(!user) {
throw new Error('You must be logged in');
}
se passar por essas condições, definimos um objeto com os dados da nova pergunta e os dados do usuário.
const question = {
content: newQuestion,
author: {
name: user?.name,
avatar: user.avatar,
},
isHighlighted: false,
isAnswered: false
}
isHighlighted e isAnswered com valores booleanos para no futuro termos um controle da interface de acordo com seus valores.
então passamos esse objeto para a database com
await database.ref(`rooms/${roomId}/questions`).push(question)
E para consumir questões da database do Firebase vamos utilizar o hook useEffect(() => {}, []) para buscar no firebase os dados das perguntas.
useEffect(() => {
const roomRef = database.ref(`rooms/${roomId}`);
roomRef.on('value', room => {
console.log(room.val());
})
}, [])
Este evento .on irá disparar uma vez com os dados iniciais armazenados neste local e, em seguida, disparar novamente cada vez que os dados forem alterados.
O console.log(room.val()) vai nos devolver um objeto com authorId:string, questions:object, title:string.
então definimos o tipo de objeto
//Record para tipar objetos, e dentro de <> fica o tipo da chave
type FirebaseQuestions = Record<string, {
author: {
name: string;
avatar: string;
}
content: string;
isAnswered: boolean;
isHighlighted: boolean;
}>
então definimos a constante:
const firebaseQuestions: FirebaseQuestions = databaseRoom.questions;
e transformamos esse objeto em um vetor com Object.entries();
const parsedQuestions = Object.entries(firebaseQuestions)
dessa forma o objeto {"name": "Eduardo", "cidade": "belém"} vai retornar [["name", "eduardo"], ["cidade","belém"]]
então podemos utilizar o .map(value => {}) tratando o value como um vetor, fazendo uma desestruturação sabendo que o primeiro valor é a chave e o segundo valor é o valor dessa chave. [key, value]
const parsedQuestions = Object.entries(firebaseQuestions).map(([key, value]) => {
return {
id: key,
content: value.content,
author: value.author,
isHighlighted: value.isHighlighted,
isAnswered: value.isAnswered,
}
})
agora que temos um [] que contém um object com as perguntas, precisamos salvar isso em algum estado.
Criamos um para as perguntas e um para o titulo.
const [questions,setQuestions] = useState<Question[]>([])
const [title, setTitle] = useState('')
e definimos o tipo do estado das perguntas:
type Question = {
id:string;
author: {
name: string;
avatar: string;
}
content: string;
isAnswered: boolean;
isHighlighted: boolean;
}
e passamos os valores para os estados ainda dentro de <codeuseEffect()
setTitle(databaseRoom.title)
setQuestions(parsedQuestions)
Agora basta usarmos essas informações na interface.
Foi feito um componente Question com HTML e CSS para servir como a div que vai conter as perguntas. Esse componente foi importado para Room.tsx onde foi feito um .map() nele.
{questions.map(question => {
return (
<Question
key={question.id}
content={question.content}
author={question.author}
/>
)
})}
Agora todo item contido em questions vai retornar como um componente Question.
Criamos uma função chamada useRoom() e agora temos que trazer todas as funcionalidades que vão ser utilizadas tanto na página do usuário quanto na página do admin . Então pegamos a parte de carregamento das questões
Passamos as funções de useEffect() do arquivo Room.tsx, suas tipagens, os estados:questions e title, e então exportamos dessa função useRoom() apenas as perguntas e os titulos, para que possamos importar de volta no Room.tsx.
return { questions, title }.
Mas para que o firebase consiga localizar aonde queremos fazer a referência no banco de dados é necessário do roomId, que é os pedaços dinâmicos do URL da página que colocamos como placeholder no path, precisamos passar essa rota para o useRoom(), então : useRoom(roomId: string)
Agora quando usarmos o hook na page Room.tsx passamos o roomId, que é o id da pagina no Route que foi inserido pelo handleCreateRoom na page NewRoom.tsx
const { questions, title } = useRoom(roomId)
Feito isso, o código na page Room.tsx já parece muito mais limpo e podemos aproveitar essa funcionalidade na página do admin!
Criamos a page AdminRoom.tsx copiando toda a page Room.tsx, retiramos todo o form e adicionamos o componente Button no header.
No componente Button foi passado um type { isOutlined?: boolean } e nas props da function agora podemos passar ({isOutlined = false, ...props})
Então colocamos uma condicional no className:
className={`button ${isOutlined? 'outlined' : ''}`}
E agora caso isOutlined seja true a classe outlined também é aplicada.
Depois de feito o CSS do botão do like, é criado na page Room.tsx uma função assíncrona que recebe a questionId e a informação se já foi dado o like ou não.
handleLikeQuestion(questionId:string, likeId: string | undefined) {}
essa função vai fazer o push para a database com o authorId.
Primeiro fazemos uma condição para saber se o usuário já deu o like ou não.
if (likeId) {
await database.ref(`rooms/${roomId}/questions/${questionId}/likes/${likeId}`).remove()
}
Caso retorne false então selecionamos a localização na database.
await database.ref(`rooms/${roomId}/questions/${questionId}/likes`)
e enviamos os dados nessa localização
await database.ref(`rooms/${roomId}/questions/${questionId}/likes`).push({
authorId: user?.id,
})
Para contarmos os números de likes é necessário voltarmos no nosso hook useRoom()
Adicionamos a linha likeCount: Object.values(value.likes ?? {}).length para que a gente r
Selected from shared topics, language and repository description—not editorial ratings.
CaioAugustoo /
💬 Aplicação web que permite interação entre usuários. Desenvolvida com React.js, TypeScript e Firebase.
34/100 health