- Como punto de partida tenemos que realizar varios ajustes en nuestra store. Más concretamente en el módulo
user.js
para gestionar los metadatos del usuario donde guardaremos la sala a la que está conectado y cuándo fue la última conexión. Adeás haremos uso de los nuevos métodos Firestore para tratar con matrices.
const actions = {
async updateMeta(context, { roomID, exit, uid }) {
const ref = db.collection("users").doc(uid);
const userDoc = await ref.get();
if (!userDoc.exists) await ref.set({});
const method = exit ? "arrayRemove" : "arrayUnion";
await ref.update({
connected: firebase.firestore.FieldValue[method](roomID),
[`joined.${roomID}`]: Date.now()
});
},
async getMeta({ state, commit }) {
const ref = db.collection("users").doc(state.user.uid);
await ref.update({ connected: [] });
const query = ref.onSnapshot(doSnapshot);
commit("setUserListener", query);
function doSnapshot(doc) {
commit("setMeta", doc.data());
}
},
}
- Ahora debemos invocar la acción
updateMeta
al acceder y al salir de nuestro componente/vista ViewRoom.vue
.
async created() {
this.userUid = this.$store.state.user.user.uid;
try {
await this.$store.dispatch("user/updateMeta", {
roomID: this.id,
exit: false,
uid: this.userUid
});
...
} catch (error) {
...
}
},
async destroyed() {
try {
await this.$store.dispatch("user/updateMeta", {
roomID: this.id,
exit: true,
uid: this.userUid
});
} catch (error) {
...
}
},
- Ya no podemos seguir escuchando los mensajes sólo cuando estamos conectados a una sala de conversación, sino que debemos hacerlo siempre. Es la única forma de saber si hay mensajes nuevos para un usuario que no está conectado a ninguna sala.
- Debemos ajustar nuestro módulo
messages.js
para hacer uso de collectionGroup
.
const actions = {
async getMessages({ commit }) {
const query = db
.collectionGroup("messages")
.orderBy("createdAt", "desc")
.onSnapshot(doSnapShot);
...
}
},
async createMessage({ rootState }, { roomID, message }) {
await db
.collection("rooms")
.doc(roomID)
.collection("messages")
.add({
...
userName: rootState.user.user.displayName,
roomId: roomID,
message,
...
});
}
};
- Con todo listo debemos decicir qué mensajes se consideran "no leídos" para el usuario. La forma más sencilla es crear una propiedad computada en
RoomsView.vue
y pasarle la información como custom prop al componente RoomsComponent.vue
.
<RoomsComponent :unread-messages="unreadMessages" :rooms="rooms" />
export default {
name: "RoomsView",
computed: {
...
...mapState("messages", ["messages"]),
unreadMessages() {
return this.messages.filter(message => {
return (
this.meta.joined[message.roomId] &&
this.meta.joined[message.roomId] < message.createdAt
);
});
}
},
components: {
RoomsComponent
}
};
- Ya casi lo tenemos. En este momento debemos ir al componente
RoomsComponent.vue
y actuar con la nueva información de mensajes sin leer mostrando la alerta. Todo depende de un nuevo método que indique si cada sala de conversación tiene mensajes sin leer.
methods: {
hasUnreadMessages(roomId) {
return this.unreadMessages.filter(message => {
return message.roomId === roomId;
});
}
}
- Por último queda ajustar nuestra de store
index.js
para invocar y desactivar los módulos cuando haya y no haya autentificación respectivamente.
actions: {
checkAuth({ dispatch, commit }) {
auth.onAuthStateChanged(async function(user) {
if (user) {
commit("user/setUser", user);
try {
await dispatch("user/getMeta");
await dispatch("rooms/getRooms");
await dispatch("messages/getMessages");
} catch (error) {
...
}
} else {
commit("user/setMeta", {});
commit("user/setUserListener", () => {});
commit("rooms/setRooms", []);
commit("rooms/setRoomsListener", () => {});
commit("messages/setMessages", []);
commit("messages/setMessagesListener", () => {});
commit("user/setUser", null);
}
});
}
},