With tweets loaded, I now need to display them one-by-one to the user, so the user can decide what to do with each of them. The code can be seen here, most of the changes are in the new Curate.vue
file
The Curate view
The curate isn't that different from the load - it gains a couple of buttons, so I won't go into too much detail here (take a look at the code)
Pre-fetch tweets
The more interesting thing is how it loads the tweets. Since I expect a user to go through the tweets relatively quickly, I want to pre-load the next tweet so that the user doesn't have to wait for a load. So, as soon as the component is mounted, I have some code that runs on mount, which loads 2 tweets (.limit(2)
):
mounted() {
firestore.collection('users').doc(this.uid).get()
.then(doc => {
this.count = doc?.get("newCount") || 0;
})
this.currentLoading = true;
this.nextLoading = true;
return firestore.collection('users').doc(this.uid).collection('tweets')
.where('queued', '==', false).orderBy('added').limit(2).get()
.then(query => {
if (query.size) {
this.currentId = query.docs[0].id;
this.currentTweet = query.docs[0].get("tweet");
this.currentDocRef = query.docs[0].ref;
if (query.size > 1) {
this.nextId = query.docs[1].id;
this.nextTweet = query.docs[1].get("tweet");
this.nextDocRef = query.docs[1].ref;
}
}
this.currentLoading = false;
this.nextLoading = false;
})
.catch(err => {
console.error(err);
this.showError("Something went wrong, could not load tweets");
})
}
This then sets the currentTweet
and nextTweet
variables accordingly. Then, when the currentTweet
is dealt with, it's cleared, the nextTweet
takes it's place, and then a new request issued to fetch the next tweet after that:
advance(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
if (this.nextLoading) {
// if we're already loading, that means the next one will appear shortly,
// so just clear display and wait
this.currentId = "";
this.currentTweet = "";
this.currentDocRef = null;
return
}
this.currentId = this.nextId;
this.currentTweet = this.nextTweet;
this.currentDocRef = this.nextDocRef;
this.nextId = ""
this.nextTweet = ""
this.nextDocRef = null
if (this.currentDocRef) {
this.nextLoading = true;
return firestore.collection('users').doc(this.uid).collection('tweets')
.where('queued', '==', false).orderBy('added').limit(2).get()
.then(query => {
this.nextLoading = false;
if (query.size > 1) {
this.nextId = query.docs[1].id;
this.nextTweet = query.docs[1].get("tweet");
this.nextDocRef = query.docs[1].ref;
if(!this.currentDocRef) { // in case current tweet was already dealt with while a document was being fetched
return this.advance()
}
}
})
.catch(err => {
console.error(err)
this.showError("Something went wrong, could not load next tweet");
})
}
}
I actually improve this mechanism by using .beginAfter()
method rather than .limit(2)
, but this appears in a later commit.
Editing
Allowing editing the tweet is relatively straightfoward - I already opted to display the tweet using a textarea, so it was a case of enabling the textarea, and then providing a function to write the results into the Firestore database once completed editing.
saveAction() {
if (this.currentDocRef && this.editing && this.currentTweet) {
this.currentLoading = true;
this.currentDocRef.update({
tweet: this.currentTweet
})
.then(() => {
this.currentLoading = false;
this.editing = false;
})
.catch(err => {
console.error(err)
this.showError("Something went wrong, could not edit tweet");
})
}
}
Enqueue and Delete
Enqueueing a tweet is simply a case of updating it's property to queued: true
, and then running the above advance function. Conversely, Deleting a tweet actually deletes it from the database using a delete()
method.
I can actually make this go faster, as it synchronously waits for the delete/enqueue action to complete before switching. However, I think it is going fast enough, and lets users get feedback of any failures when they happen.
Swipe Controls
Since I want to be able to use this on my phone, I enabled swiping left/right to delete/enqueue. Vuetify has these out of the box. The visual feedback of swipe isn't very good (I should add transitions later), but it's very easy to add, the simply gains a v-touch
directive, to trigger the already existing deleteAction()
and enqueueAction()
, the true
argument causes those functions to show an alert to give users some better feedback. I am considering replacing this with a transition animation in future.
<v-textarea
v-model="displayTweet"
outlined
label="Tweet"
counter
:readonly="!editing"
:disabled="currentLoading"
:loading="loading"
v-touch="{
left: () => deleteAction(true),
right: () => enqueueAction(true),
}"
>
Top comments (0)