import { db } from '../sync'
import { Q } from '@nozbe/watermelondb'
import { STATE } from '../sync/models/ContentProgress'
import { lessonRecentTypes, SONG_TYPES } from '../../contentTypeConfig.js'
import dayjs from 'dayjs'
interface Activity {
contentId: number
action: 'Start' | 'Complete'
contentType: 'lesson' | 'song'
date: string
brand: string
}
/**
* @param offlineTimestamp - Minimum `updated_at` epoch ms to include
* @param page
* @param limit
* @param tabName
* @param options.page - Page number (default 1)
* @param options.limit - Results per page (default 5)
* @param options.tabName - Restrict to `'lessons'`, `'songs'`, or both when `null`
* @returns {Promise<{currentPage: number, totalPages: number, data: Activity[]}>}
*/
export async function getRecentActivityOffline(
offlineTimestamp: number,
{
page = 1,
limit = 5,
tabName = null
}: {
page?: number,
limit?: number,
tabName?: 'lessons'|'songs'|null
} = {}): Promise<any> {
// Note: this is kind of a hack. We're really just getting RADFOP: Recent Activities Derived From Offline Progress,
// because setting up watermelon user activities table is extremely complicated.
// Note: this implementation does not persist "activities" beyond when the corresponding record is deleted. That's ok right now.
const clauses = getClauses(offlineTimestamp, tabName)
const query = await db.contentProgress.queryAll(...clauses)
const progress = query.data
const activities = deriveActivitiesFromProgress(progress)
const totalPages = Math.ceil(activities.length / limit)
const currentPage = Math.min(page, totalPages)
const sorted = activities.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
const data = sorted.slice((currentPage - 1) * limit, currentPage * limit)
return {
currentPage,
totalPages,
data,
}
}
function getClauses(offlineTimestamp: number, tabName: string|null) {
let clauses: Q.Clause[] = [
Q.where('updated_at', Q.gte(offlineTimestamp)),
Q.sortBy('created_at', 'desc'),
]
if (tabName === 'lessons') {
clauses.push(Q.where('content_type', Q.oneOf(lessonRecentTypes)))
} else if (tabName === 'songs') {
clauses.push(Q.where('content_type', Q.oneOf(SONG_TYPES)))
} else {
clauses.push(Q.where('content_type', Q.oneOf([...lessonRecentTypes, ...SONG_TYPES])))
}
return clauses
}
function deriveActivitiesFromProgress(progress: Record<any, any>) {
const activities: Activity[] = []
progress.forEach(p => {
const type = lessonRecentTypes.includes(p.content_type) ? 'lesson' : 'song'
activities.push({
contentId: p.content_id,
action: 'Start',
contentType: type,
date: dayjs(p.created_at).toISOString(),
brand: p.content_brand,
})
if (p.state === STATE.COMPLETED) {
activities.push({
contentId: p.content_id,
action: 'Complete',
contentType: type,
date: dayjs(p.updated_at).toISOString(),
brand: p.content_brand,
})
}
})
return activities
}