The final pieces of functionality before completing the frontend, is to let the user select what kind of schedule to use for sending tweets. My biggest gripe with using TweetDeck or Twitter's own scheduling, is how many clicks it takes to schedule a tweet. For the kind of tweeting I want this bot to do, I just need it to automatically tweet a few times a day, spread out over the day. In fact, I don't really care exactly when, only that it's roughly at the hours that I want.
So I've decided to give users a 24x7 grid of hours to pick from, if the user checks a grid square, then a tweet will go out at some time in that hour slot.
The code for this is here and the majority of the changes are in the new Schedule.vue
file
The Schedule Template
Here, I make extensive use of v-for
to render the 24x7 grid of checkboxes in a table:
<template>
<v-container>
<Section>
<template v-slot:title>Schedule</template>
<p>
When to post? CurateBot will post sometime within each hour. Times are UTC. Current hour is marked with "now"
</p>
<v-simple-table dense>
<thead>
<tr>
<th class="text-center">Time</th>
<th
v-for="(dayName, dayIdx) in ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']"
:key="'day'+dayName"
class="text-center px-0"
>
<v-chip v-if="(dayIdx-1) == currentDay" small color="primary">{{ dayName}}</v-chip>
<span v-else>{{ dayName }}</span>
</th>
</tr>
</thead>
<tbody>
<tr
v-for="hourIdx in 24"
:key="'row'+hourIdx"
>
<td class="text-center">
<v-chip v-if="(hourIdx-1) == currentHour" small color="primary">{{ hourIdx-1 }}:00 now</v-chip>
<span v-else>{{ hourIdx-1 }}:00</span>
</td>
<td
v-for="dayIdx in 7"
:key="(dayIdx-1)*24+hourIdx-1"
class="text-center"
>
<v-simple-checkbox
:disabled="loading"
v-model="schedule[(dayIdx-1)*24+hourIdx-1]"
color="primary"
:ripple="false"
></v-simple-checkbox>
</td>
</tr>
</tbody>
</v-simple-table>
<template v-slot:actions>
<v-btn
:disabled="loading"
color="warning"
@click="clearAction"
>
Clear Schedule
</v-btn>
<v-spacer></v-spacer>
<v-btn
:disabled="loading"
color="success"
@click="saveAction"
>
Save Schedule
</v-btn>
</template>
</Section>
</v-container>
</template>
The data for this is actually a 24*7 1D array (schedule: Array<boolean> = new Array(24*7).fill(false);
), which has been rendered out in a 2D grid using the two v-for loops. Each checkbox's v-model
points to a different member of this array.
Saving and loading schedule
Because later on we need to be able to figure out which user has a particular slot in their schedule, we convert the 168 (24*7) array of bools, into just an array of enabled slots (an array of the indices). We do the reverse transform when loading.
saveAction() {
if (!this.loading) {
this.loading = true;
const enabled = [];
for (const idx in this.schedule) {
if (this.schedule[idx]) {
enabled.push(idx);
}
}
return firestore.collection('users').doc(this.uid).update({
scheduleEnabled: enabled
})
.then(() => {
this.showSuccess("Schedule updated");
this.loading = false;
})
.catch(err => {
console.error(err);
this.showError("Something went wrong, could not update schedule");
})
}
}
mounted() {
this.loading = true;
return firestore.collection('users').doc(this.uid).get()
.then(doc => {
const scheduleEnabled = doc.get("scheduleEnabled")
if (scheduleEnabled) {
this.schedule = new Array(24*7).fill(false);
for (const idx of scheduleEnabled) {
this.schedule[idx] = true;
}
}
return firestore.collection('system').doc('stats').get()
})
.then(doc => {
this.lastRun = doc.get('lastRun');
})
.catch(err => {
console.error(err);
this.showError("Something went wrong, could not load tweets");
})
.finally(() => {
this.loading = false;
})
}
Displaying the current time
Since I'm not bothered at this stage to add in timezone support, this grid will run on UTC time, and to help a user select the right times, I will highlight the current time. This is simply done with a component with a conditional when the hour index matches the current hour returned by JavaScript's new Date().getUTCHours();
<v-chip v-if="hourIdx == current" small color="primary">{{ hourIdx-1 }}:00 now</v-chip>
At this point, we are now done with the frontend (other than some style changes needed - we're still using all of Vuetify's default blue theme, something snazzier would be nice).
I'll be moving onto the backend next
Top comments (0)