mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Create a service for interacting with cached read positions
This commit is contained in:
parent
fbfa934479
commit
a40f5bed55
2 changed files with 118 additions and 26 deletions
|
|
@ -1,17 +1,14 @@
|
|||
import { redisDataSource } from '../redis_data_source'
|
||||
|
||||
type ReadingProgressCacheItem = {
|
||||
readingProgressPercent: number
|
||||
readingProgressTopPercent: number | undefined
|
||||
readingProgressAnchorIndex: number | undefined
|
||||
updatedAt: string
|
||||
}
|
||||
import {
|
||||
ReadingProgressCacheItem,
|
||||
fetchCachedReadingPosition,
|
||||
keyForCachedReadingPosition,
|
||||
pushCachedReadingPosition,
|
||||
} from '../services/cached_reading_position'
|
||||
|
||||
export class ReadingProgressDataSource {
|
||||
private cacheItems: { [id: string]: ReadingProgressCacheItem } = {}
|
||||
|
||||
constructor() {}
|
||||
|
||||
async getReadingProgress(
|
||||
uid: string,
|
||||
libraryItemID: string
|
||||
|
|
@ -21,7 +18,7 @@ export class ReadingProgressDataSource {
|
|||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
return this.valueFromRedis(cacheKey)
|
||||
return fetchCachedReadingPosition(uid, libraryItemID)
|
||||
}
|
||||
|
||||
async updateReadingProgress(
|
||||
|
|
@ -33,11 +30,14 @@ export class ReadingProgressDataSource {
|
|||
readingProgressAnchorIndex: number | undefined
|
||||
}
|
||||
): Promise<void> {
|
||||
const cacheKey = `omnivore:reading-progress:${uid}:${libraryItemID}`
|
||||
const cacheItem: ReadingProgressCacheItem = {
|
||||
...progress,
|
||||
uid,
|
||||
libraryItemID,
|
||||
updatedAt: new Date().toISOString(),
|
||||
...progress,
|
||||
}
|
||||
const cacheKey = keyForCachedReadingPosition(uid, libraryItemID)
|
||||
pushCachedReadingPosition(uid, libraryItemID, cacheItem)
|
||||
|
||||
this.cacheItems[cacheKey] = cacheItem
|
||||
if (
|
||||
|
|
@ -51,18 +51,4 @@ export class ReadingProgressDataSource {
|
|||
console.log('failed to cache reading progress')
|
||||
}
|
||||
}
|
||||
|
||||
async valueFromRedis(
|
||||
cacheKey: string
|
||||
): Promise<ReadingProgressCacheItem | undefined> {
|
||||
const redisCached = await redisDataSource.redisClient?.lrange(
|
||||
cacheKey,
|
||||
0,
|
||||
0
|
||||
)
|
||||
if (redisCached && redisCached.length > 0) {
|
||||
return JSON.parse(redisCached[0])
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
|
|||
106
packages/api/src/services/cached_reading_position.ts
Normal file
106
packages/api/src/services/cached_reading_position.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { redisDataSource } from '../redis_data_source'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
export type ReadingProgressCacheItem = {
|
||||
uid: string
|
||||
libraryItemID: string
|
||||
readingProgressPercent: number
|
||||
readingProgressTopPercent: number | undefined
|
||||
readingProgressAnchorIndex: number | undefined
|
||||
updatedAt: string | undefined
|
||||
}
|
||||
|
||||
export const keyForCachedReadingPosition = (
|
||||
uid: string,
|
||||
libraryItemID: string
|
||||
): string => {
|
||||
return `omnivore:reading-progress:${uid}:${libraryItemID}`
|
||||
}
|
||||
|
||||
// Reading positions are cached as an array of positions, when
|
||||
// we fetch them from the cache we find the maximum values
|
||||
export const clearCachedReadingPosition = async (
|
||||
uid: string,
|
||||
libraryItemID: string
|
||||
): Promise<boolean> => {
|
||||
const cacheKey = keyForCachedReadingPosition(uid, libraryItemID)
|
||||
try {
|
||||
const res = await redisDataSource.redisClient?.del(cacheKey)
|
||||
return res ? res > 0 : false
|
||||
} catch (error) {
|
||||
logger.error('exception clearing cached reading position', {
|
||||
cacheKey,
|
||||
error,
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const pushCachedReadingPosition = async (
|
||||
uid: string,
|
||||
libraryItemID: string,
|
||||
position: ReadingProgressCacheItem
|
||||
): Promise<boolean> => {
|
||||
const cacheKey = keyForCachedReadingPosition(uid, libraryItemID)
|
||||
try {
|
||||
const result = await redisDataSource.redisClient?.lpush(
|
||||
cacheKey,
|
||||
JSON.stringify(position)
|
||||
)
|
||||
return result ? result > 0 : false
|
||||
} catch (error) {
|
||||
logger.error('error writing cached reading position', { cacheKey, error })
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Reading positions are cached as an array of positions, when
|
||||
// we fetch them from the cache we find the maximum values
|
||||
export const fetchCachedReadingPosition = async (
|
||||
uid: string,
|
||||
libraryItemID: string
|
||||
): Promise<ReadingProgressCacheItem | undefined> => {
|
||||
const cacheKey = keyForCachedReadingPosition(uid, libraryItemID)
|
||||
try {
|
||||
const cacheItemList = await redisDataSource.redisClient?.lrange(
|
||||
cacheKey,
|
||||
0,
|
||||
-1
|
||||
)
|
||||
const items = cacheItemList?.map((item) => JSON.parse(item))
|
||||
if (!items || items.length < 1) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const percent = Math.max(
|
||||
...items.map((o) =>
|
||||
'readingProgressPercent' in o ? o.readingProgressPercent : 0
|
||||
)
|
||||
)
|
||||
const top = Math.max(
|
||||
...items.map((o) =>
|
||||
'readingProgressTopPercent' in o ? o.readingProgressTopPercent : 0
|
||||
)
|
||||
)
|
||||
const anchor = Math.max(
|
||||
...items.map((o) =>
|
||||
'readingProgressAnchorIndex' in o ? o.readingProgressAnchorIndex : 0
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
uid,
|
||||
libraryItemID,
|
||||
readingProgressPercent: percent,
|
||||
readingProgressTopPercent: top,
|
||||
readingProgressAnchorIndex: anchor,
|
||||
updatedAt: undefined,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('exception looking up cached reading position', {
|
||||
cacheKey,
|
||||
error,
|
||||
})
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
Loading…
Reference in a new issue