I am trying to show a list on screen but for some reason Vue 3 (with vuefire) does not propagate the change, despite the network calls being done. Since I added username support, the screen keeps showing "No scenes created yet". Typescript complains about wrong typing, but I can't figure out how to properly fix it.
Edit: I am not trying to fix the typing, but rather to fix the collection not being updated when doing the following. The value keeps being an empty array despite:
scenesCollection.value = useCollection(scenesCollectionRef);
Here are the two error for a document and a collection:
Type '_RefFirestore<UserInformation | undefined>' is not assignable to type 'null'. ts(2322)
Type '_RefFirestore<Scene[]>' is missing the following properties from type 'never[]': length, pop, push, concat, and 35 more. ts(2740)
I tried manually specifying the ref typing just as I did for the userId, but to no avail. And anyway it would fix the ts error, but the code would still not work.
I submitted my code to ChatGPT as well but it considers it as valid code.
Do you have any other tricks I could pull? Is there a better way to change the code to keep auto updates from the server?
Here the entire code for the component:
<template>
<div class="wrapper">
<div v-if="!userDocument">
<p>This user does not exist</p>
</div>
<div v-else-if="!scenesCollection">
<p>This user does have public scenes</p>
</div>
<v-container v-else-if="scenesCollection.length" fluid>
<v-row dense>
<v-col v-for="scene in scenesCollection" :key="scene.id">
<router-link :to="'/' + userId + '/' + scene.id" class="clickable">
<v-card>
<v-img
:src="scene.coverUrl"
class="align-end"
gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"
height="200px"
cover
>
<v-card-title class="text-white">{{
scene.friendlyName
}}</v-card-title>
</v-img>
</v-card>
</router-link>
</v-col>
</v-row>
</v-container>
<div v-else class="clickable">
<br onload="alert('banana')" />
<p>No scenes created yet</p>
</div>
</div>
</template>
import { ref, watchEffect } from 'vue';
import {
useFirestore,
useCurrentUser,
useCollection,
useDocument,
} from 'vuefire';
import { DocumentReference, collection, doc, getDoc } from 'firebase/firestore';
import { useRoute } from 'vue-router';
import type { Scene, UserInformation, UsernameInformation } from '@/types';
const db = useFirestore();
const route = useRoute();
const currentUser = useCurrentUser();
const username = route.params.username;
const isOnMePage = username === 'me';
const userId = ref<string | null>(null);
const fetchUserIdFromUsername = async (username: string) => {
const docRef = doc(
db,
'usernames',
username,
) as DocumentReference<UsernameInformation>;
try {
const usernameInfo = await getDoc(docRef);
if (!usernameInfo.exists()) return '';
return usernameInfo.data().uid;
} catch (error) {
console.error('Error fetching user ID:', error);
return '';
}
};
watchEffect(async () => {
// Logic for determining userId
if (isOnMePage && currentUser.value) {
userId.value = currentUser.value.uid;
} else if (typeof username === 'string') {
userId.value = await fetchUserIdFromUsername(username);
}
});
let userDocument = ref(null);
let scenesCollection = ref([]);
watchEffect(() => {
console.log('Watching effect', { userId: userId.value });
if (!userId.value) {
userDocument.value = null;
scenesCollection.value = [];
return;
}
const userDocumentRef = doc(
db,
'users',
userId.value,
).withConverter<UserInformation>({
fromFirestore: (snapshot) => {
// this avoids having `null` while the server timestamp is updating
const data = snapshot.data({
serverTimestamps: 'estimate',
}) as UserInformation;
console.log('New userInfo from firestore', { ...data, id: snapshot.id });
return { ...data, id: snapshot.id };
},
toFirestore: (data: UserInformation) => data,
});
const scenesCollectionRef = collection(
db,
'users',
userId.value,
'scenes',
).withConverter<Scene>({
fromFirestore: (snapshot) => {
const data = snapshot.data({ serverTimestamps: 'estimate' }) as Scene;
console.log('New scene from firestore', { ...data, id: snapshot.id });
return { ...data, id: snapshot.id };
},
toFirestore: (data: Scene) => data,
});
userDocument.value = useDocument(userDocumentRef);
scenesCollection.value = useCollection(scenesCollectionRef);
});