audiobookshelf/client/services/AchievementService.js
2025-09-10 06:53:19 +00:00

99 lines
2.5 KiB
JavaScript

// client/services/AchievementService.js
const json = (res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
}
function dispatchUnlocked (badges) {
try {
if (typeof window !== 'undefined' && badges && badges.length) {
window.dispatchEvent(new CustomEvent('achievement:unlocked', { detail: { badges } }))
}
} catch (_) {}
}
// Try best-effort ways ABS stores the access token
function getAccessToken () {
try {
if (typeof window === 'undefined') return null
const w = window
const nuxtStore = w.$nuxt?.$store || null
const nuxtState = w.__NUXT__?.state || null
const tryFromStore = (s) => {
if (!s) return null
if (s.user?.user?.accessToken) return s.user.user.accessToken
if (s.user?.accessToken) return s.user.accessToken
if (s.user?.token) return s.user.token
return null
}
const tokenFromStore = tryFromStore(nuxtStore) || tryFromStore(nuxtState)
const tokenFromStorage =
w.localStorage?.getItem('accessToken') ||
w.localStorage?.getItem('token') ||
null
return tokenFromStore || tokenFromStorage || null
} catch {
return null
}
}
function authHeaders () {
const t = getAccessToken()
return t ? { Authorization: `Bearer ${t}` } : {}
}
const normalizeProgress = (p) => ({
userId: p?.userId || 'guest',
counters: p?.counters || {},
unlocked: Array.isArray(p?.unlocked) ? p.unlocked : [],
history: Array.isArray(p?.history) ? p.history : []
})
async function postComplete (payload) {
const data = await fetch('/api/achievements/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders() },
credentials: 'include',
body: JSON.stringify(payload || {})
})
.then(json)
.catch(() => ({}))
if (data?.unlockedNow?.length) dispatchUnlocked(data.unlockedNow)
return data
}
export default {
async getCatalog () {
return fetch('/api/achievements/catalog', {
credentials: 'include'
}).then(json)
},
async getMy () {
return fetch('/api/achievements/me', {
credentials: 'include',
headers: { ...authHeaders() }
})
.then(json)
.then(normalizeProgress)
.catch(() => normalizeProgress(null))
},
async complete (payload) {
return postComplete(payload)
},
// NEW — convenience wrappers
loginPing () {
return postComplete({ event: 'login' })
},
markSearched () {
return postComplete({ event: 'search' })
},
markReadToday () {
return postComplete({ event: 'readUploadedToday' })
}
}