Change to use `@tanstack/react-query` Add Global ErrorProvider Add limited Support for offline version Fix issue with calendar display +1 day
224 lines
5.8 KiB
JavaScript
224 lines
5.8 KiB
JavaScript
import { CapacitorSQLite } from '@capacitor-community/sqlite'
|
|
|
|
const CACHE_TABLE = 'offline_cache'
|
|
const QUEUE_TABLE = 'offline_request_queue'
|
|
const OFFLINE_TASK = 'offlineTasks' // For storing offline tasks
|
|
|
|
class LocalStore {
|
|
constructor() {
|
|
this.db = null
|
|
// this.useLocalStorage = !Capacitor.isNativePlatform()
|
|
this.useLocalStorage = true // default to localStorage for now.
|
|
}
|
|
|
|
async initDatabase() {
|
|
if (this.useLocalStorage) return null
|
|
if (this.db) return this.db
|
|
|
|
const db = await CapacitorSQLite.createConnection({
|
|
database: 'offline_data',
|
|
version: 1,
|
|
})
|
|
await db.open()
|
|
|
|
// Create tables if they don't exist
|
|
await db.execute(`
|
|
CREATE TABLE IF NOT EXISTS ${CACHE_TABLE} (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT,
|
|
timestamp INTEGER
|
|
);
|
|
CREATE TABLE IF NOT EXISTS ${QUEUE_TABLE} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
url TEXT,
|
|
requestBody TEXT
|
|
);
|
|
`)
|
|
|
|
this.db = db
|
|
return db
|
|
}
|
|
|
|
async saveToCache(key, data) {
|
|
const timestamp = Date.now() // Current timestamp in milliseconds
|
|
|
|
if (this.useLocalStorage) {
|
|
localStorage.setItem(key, JSON.stringify({ value: data, timestamp }))
|
|
|
|
return
|
|
}
|
|
|
|
const db = await this.initDatabase()
|
|
await db.run(
|
|
`
|
|
INSERT OR REPLACE INTO ${CACHE_TABLE} (key, value, timestamp)
|
|
VALUES (?, ?, ?);
|
|
`,
|
|
[key, JSON.stringify(data), timestamp],
|
|
)
|
|
}
|
|
|
|
// async saveTemporaryTask(task) {
|
|
// const baseURL = apiManager.getApiURL()
|
|
// const fullURL = `${baseURL}/chores/${task.tempId}`
|
|
// const options = {
|
|
// method: 'GET',
|
|
// headers: HEADERS(),
|
|
// url: fullURL,
|
|
// }
|
|
// const respond = { res: task }
|
|
// const requestId = murmurhash.v3(JSON.stringify({ fullURL, options }))
|
|
|
|
// if (this.useLocalStorage) {
|
|
// this.saveToCache(requestId, respond)
|
|
// return
|
|
// }
|
|
// const db = await this.initDatabase()
|
|
|
|
// await db.run(
|
|
// `
|
|
// INSERT INTO ${CACHE_TABLE} (url, requestBody)
|
|
// VALUES (?, ?);
|
|
// `,
|
|
// [
|
|
// requestId,
|
|
// JSON.stringify({
|
|
// url: fullURL,
|
|
// options: { method: 'GET', headers: HEADERS() },
|
|
// }),
|
|
// ],
|
|
// )
|
|
// console.log('Saved temporary task to queue:', task)
|
|
// return
|
|
// }
|
|
|
|
async getFromCache(key, ttl = 0) {
|
|
const now = Date.now()
|
|
|
|
if (this.useLocalStorage) {
|
|
const cachedItem = localStorage.getItem(key)
|
|
if (!cachedItem) return null
|
|
|
|
const { value, timestamp } = JSON.parse(cachedItem)
|
|
if (ttl > 0 && now - timestamp > ttl) {
|
|
localStorage.removeItem(key) // Remove expired item
|
|
return null
|
|
}
|
|
return value
|
|
}
|
|
|
|
const db = await this.initDatabase()
|
|
const result = await db.query(
|
|
`
|
|
SELECT value, timestamp FROM ${CACHE_TABLE} WHERE key = ?;
|
|
`,
|
|
[key],
|
|
)
|
|
|
|
if (result.values.length === 0) return null
|
|
|
|
const { value, timestamp } = result.values[0]
|
|
if (ttl > 0 && now - timestamp > ttl) {
|
|
// Remove expired item
|
|
await db.run(`DELETE FROM ${CACHE_TABLE} WHERE key = ?;`, [key])
|
|
return null
|
|
}
|
|
|
|
return JSON.parse(value)
|
|
}
|
|
|
|
async cleanExpiredCache(ttl) {
|
|
const now = Date.now()
|
|
|
|
if (this.useLocalStorage) {
|
|
const keys = Object.keys(localStorage)
|
|
for (const key of keys) {
|
|
const cachedItem = localStorage.getItem(key)
|
|
if (!cachedItem) continue
|
|
|
|
const { timestamp } = JSON.parse(cachedItem)
|
|
if (now - timestamp > ttl) {
|
|
localStorage.removeItem(key) // Remove expired item
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
const db = await this.initDatabase()
|
|
await db.run(
|
|
`
|
|
DELETE FROM ${CACHE_TABLE} WHERE ? - timestamp > ?;
|
|
`,
|
|
[now, ttl],
|
|
)
|
|
}
|
|
|
|
async queueRequest(requestId, requestPayload) {
|
|
if (this.useLocalStorage) {
|
|
const queue = JSON.parse(localStorage.getItem(QUEUE_TABLE)) || []
|
|
console.log('requestPayload', requestPayload)
|
|
|
|
if (typeof requestPayload?.options?.body['id'] === 'string') {
|
|
requestPayload['id'] = null
|
|
}
|
|
queue.push({ requestId, requestBody: requestPayload })
|
|
localStorage.setItem(QUEUE_TABLE, JSON.stringify(queue))
|
|
return
|
|
}
|
|
|
|
const db = await this.initDatabase()
|
|
await db.run(
|
|
`
|
|
INSERT INTO ${QUEUE_TABLE} (url, requestBody)
|
|
VALUES (?, ?);
|
|
`,
|
|
[requestId, JSON.stringify(requestPayload)],
|
|
)
|
|
}
|
|
|
|
async syncQueuedRequests() {
|
|
console.log('Syncing queued requests...')
|
|
|
|
var queueSize = 0
|
|
if (this.useLocalStorage) {
|
|
const queue = JSON.parse(localStorage.getItem(QUEUE_TABLE)) || []
|
|
console.log('LocalStore: queue: ', queue)
|
|
queueSize = queue.length
|
|
for (const request of queue) {
|
|
try {
|
|
await fetch(request.requestBody.url, request.requestBody.options)
|
|
console.log('LocalStore: Synced request:', request)
|
|
} catch (error) {
|
|
console.error('LocalStore: Failed to sync request:', request, error)
|
|
}
|
|
}
|
|
|
|
// Clear the queue after syncing
|
|
localStorage.removeItem(QUEUE_TABLE)
|
|
localStorage.removeItem(OFFLINE_TASK)
|
|
return queueSize > 0
|
|
}
|
|
|
|
const db = await this.initDatabase()
|
|
const result = await db.query(`SELECT * FROM ${QUEUE_TABLE};`)
|
|
queueSize = result.values.length
|
|
for (const request of result.values) {
|
|
try {
|
|
await fetch(
|
|
request.requestBody.url,
|
|
JSON.parse(request.requestBody.options),
|
|
)
|
|
console.log('Synced request:', request)
|
|
} catch (error) {
|
|
console.error('Failed to sync request:', request, error)
|
|
}
|
|
}
|
|
|
|
// Clear the queue after syncing
|
|
await db.run(`DELETE FROM ${QUEUE_TABLE};`)
|
|
return queueSize > 0
|
|
}
|
|
}
|
|
|
|
export const localStore = new LocalStore()
|