Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/app/_cache/AssigneeCacheGetter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { setAssigneeList } from '@/redux/features/taskBoardSlice'
import store from '@/redux/store'
import { useEffect } from 'react'
import { getAssignees, migrateAssignees } from '@/app/_cache/forageStorage'
import { getAssignees } from '@/app/_cache/forageStorage'

interface ClientAssigneeCacheGetterProps {
lookupKey: string
Expand All @@ -12,7 +12,6 @@ interface ClientAssigneeCacheGetterProps {
export const AssigneeCacheGetter = ({ lookupKey }: ClientAssigneeCacheGetterProps) => {
useEffect(() => {
const run = async () => {
await migrateAssignees(lookupKey) //migrate from localStorage to localForage if required. Remember to remove this after a while.
const assignee = await getAssignees(lookupKey)
if (assignee.length) {
store.dispatch(setAssigneeList(assignee))
Expand Down
45 changes: 45 additions & 0 deletions src/app/_cache/forageStorage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const mockGetItem = jest.fn()
const mockSetItem = jest.fn()

jest.mock('localforage', () => ({
__esModule: true,
default: {
config: jest.fn(),
getItem: (...args: unknown[]) => mockGetItem(...args),
setItem: (...args: unknown[]) => mockSetItem(...args),
},
}))

import { getAssignees, setAssignees } from '@/app/_cache/forageStorage'

const denied = () => new DOMException('Access is denied for this document.', 'SecurityError')

describe('assignee cache', () => {
beforeAll(() => {
globalThis.window = {} as Window & typeof globalThis
})
afterAll(() => {
delete (globalThis as { window?: unknown }).window
})
beforeEach(() => jest.clearAllMocks())

it('returns an empty list when storage access is denied', async () => {
mockGetItem.mockRejectedValue(denied())

await expect(getAssignees('lookup-key')).resolves.toEqual([])
expect(mockGetItem).toHaveBeenCalledWith('assignees.lookup-key')
})

it('swallows write failures when storage access is denied', async () => {
mockSetItem.mockRejectedValue(denied())

await expect(setAssignees('lookup-key', [])).resolves.toBeUndefined()
expect(mockSetItem).toHaveBeenCalledWith('assignees.lookup-key', [])
})

it('returns an empty list when nothing is cached', async () => {
mockGetItem.mockResolvedValue(null)

await expect(getAssignees('lookup-key')).resolves.toEqual([])
})
})
35 changes: 3 additions & 32 deletions src/app/_cache/forageStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,23 @@ localforage.config({
storeName: 'assignees',
})

export async function migrateAssignees(lookupKey: string) {
const lKey = `assignees.${lookupKey}`
const existing = localStorage.getItem(lKey)

if (existing) {
try {
const parsed = JSON.parse(existing)
await localforage.setItem(lKey, parsed)
localStorage.removeItem(lKey)
} catch (err) {
console.error('Migration failed', err)
}
}
} //a utility function to migrate existing assignee data from localStorage to localForage

export async function getAssignees(lookupKey: string): Promise<IAssigneeCombined[]> {
if (typeof window === 'undefined') return []

try {
if (!(await document.hasStorageAccess())) {
console.info('Browswer has no storage access')
await document.requestStorageAccess()
}

return (await localforage.getItem<IAssigneeCombined[]>(`assignees.${lookupKey}`)) ?? []
} catch (error: unknown) {
console.error(
"Storage access not granted. Under Chrome's Settings > Privacy and Security, make sure 'Third-party cookies' is allowed.",
)
console.info('Assignee cache unavailable, falling back to network', error)
return []
}
}

export async function setAssignees(lookupKey: string, value: any) {
export async function setAssignees(lookupKey: string, value: IAssigneeCombined[]) {
if (typeof window === 'undefined') return

try {
if (!(await document.hasStorageAccess())) {
console.info('Browswer has no storage access')
await document.requestStorageAccess()
}

return await localforage.setItem(`assignees.${lookupKey}`, value)
} catch (error: unknown) {
console.error(
"Storage access not granted. Under Chrome's Settings > Privacy and Security, make sure 'Third-party cookies' is allowed.",
)
console.info('Assignee cache write skipped, storage unavailable', error)
}
}
Loading