|
- import { addTime, TimeDivision } from '../utilities/Date'
- import * as Serialization from '../utilities/Serialization'
- import NoteModel from '../models/Note'
- import * as ColumnTypes from '../utilities/ColumnTypes'
- import LocalStorage from './LocalStorage'
-
- type StorageParams = {
- id: string,
- url: string,
- }
-
- type Note = ColumnTypes.InferModel<typeof NoteModel>
-
- type LoadItems = <T extends Record<string, unknown>>(params: StorageParams) => () => Promise<T[]>
-
- const loadItems: LoadItems = <T extends Record<string, unknown>>(params) => async (): Promise<T[]> => {
- const { id, url, } = params
- const storage = new LocalStorage(
- window.localStorage,
- Serialization.serialize,
- Serialization.deserialize
- )
- const localData = storage.getItem(id)
- if (localData === null) {
- const remoteItems = await window.fetch(url)
- // TODO add custom serialization method
- const theItems: T[] = await remoteItems.json()
- storage.setItem(id, {
- // TODO backend should set expiry
- expiry: addTime(new Date(), 30)(TimeDivision.DAYS).getTime(),
- items: theItems,
- })
- return theItems
- }
-
- const dataExpiry = new Date(Number(localData.expiry))
- const now = new Date()
- if (now.getTime() > dataExpiry.getTime()) {
- storage.removeItem(id)
- const loader: () => Promise<T[]> = loadItems(params)
- return loader()
- }
- return localData.items
- }
-
- type SaveItem = <T extends Record<string, unknown>>(p: StorageParams) => (item: T) => Promise<T>
-
- const saveItem: SaveItem = <T extends Record<string, unknown>>(params) => async (item) => {
- const { id: storageId, url } = params
- const storage = new LocalStorage(
- window.localStorage,
- Serialization.serialize,
- Serialization.deserialize
- )
- const localData = storage.getItem(storageId, {
- expiry: addTime(new Date(), 30)(TimeDivision.DAYS).getTime(),
- items: [],
- })
- const { items: localItems } = localData
- const { id: itemId, ...theBody } = item
- const theItems: T[] = (
- localItems.some(i => i.id === itemId)
- ? localItems.map(i => i.id === itemId ? item : i)
- : [...localItems, item]
- )
- storage.setItem(storageId, { ...localData, items: theItems })
- const response = await window.fetch(`${url}/${itemId}`, {
- method: 'put',
- body: JSON.stringify(theBody),
- headers: {
- 'Content-Type': 'application/json',
- },
- })
-
- const responseBody = await response.json()
- if (response.status !== 201 && response.status !== 200) {
- throw responseBody
- }
- return responseBody as T
- }
-
- type DeleteItem = <T extends Record<string, unknown>>(p: StorageParams) => (item: T) => Promise<boolean>
-
- const deleteItem: DeleteItem = <T extends Record<string, unknown>>(params) => async (item) => {
- const { id: storageId, url } = params
- const storage = new LocalStorage(
- window.localStorage,
- Serialization.serialize,
- Serialization.deserialize
- )
-
- const localData = storage.getItem(storageId, {
- expiry: addTime(new Date(), 30)(TimeDivision.DAYS).getTime(),
- items: [],
- })
- const { items: localItems } = localData
- const { id: itemId } = item
- const theItems: T[] = localItems.filter(i => i.id !== itemId)
- storage.setItem(storageId, { ...localData, items: theItems })
- const response = await window.fetch(`${url}/${itemId}`, {
- method: 'delete',
- })
- return response.status === 204
- }
-
- export const loadNotes = loadItems<Note>({ id: 'notes', url: '/api/notes' })
- export const loadFolders = loadItems({ id: 'folders', url: '/api/folders' })
- export const saveNote = saveItem<Note>({ id: 'notes', url: '/api/notes' })
- export const saveFolder = saveItem({ id: 'folders', url: '/api/folders' })
- export const deleteNote = deleteItem<Note>({ id: 'notes', url: '/api/notes' })
|