Add network management features and sync functionality; introduce NetworkBanner component

This commit is contained in:
Mo Tarbin
2025-04-12 01:03:00 -04:00
parent 06288af18d
commit 33d46209ef
15 changed files with 1484 additions and 381 deletions

View File

@@ -1,28 +1,36 @@
import Cookies from 'js-cookie'
import { API_URL } from '../Config'
import { RefreshToken } from './Fetcher'
import { Network } from '@capacitor/network'
import { Preferences } from '@capacitor/preferences'
import Cookies from 'js-cookie'
import murmurhash from 'murmurhash'
import { API_URL } from '../Config'
import { networkManager } from '../hooks/NetworkManager'
import { RefreshToken } from './Fetcher'
import { localStore } from './LocalStore'
class ApiManager {
constructor() {
this.customServerURL = `${API_URL}/api/v1`
this.initialized = false
}
async init() {
if (this.initialized) {
return
}
const { value: serverURL } = await Preferences.get({
key: 'customServerUrl',
})
this.customServerURL = `${serverURL || API_URL}/api/v1`
await localStore.initDatabase()
this.initialized = true
}
getApiURL() {
return this.customServerURL
}
updateApiURL(url) {
this.customServerURL = url
this.init()
@@ -31,23 +39,51 @@ class ApiManager {
export const apiManager = new ApiManager()
export function Fetch(url, options) {
export async function Fetch(url, options) {
if (!isTokenValid()) {
// store current location in cookie
Cookies.set('ca_redirect', window.location.pathname)
// Assuming you have a function isTokenValid() that checks token validity
window.location.href = '/login' // Redirect to login page
// return Promise.reject("Token is not valid");
window.location.href = '/login'
}
if (!options) {
options = {}
}
options.headers = { ...options.headers, ...HEADERS() }
const baseURL = apiManager.getApiURL()
const fullURL = `${baseURL}${url}`
return fetch(fullURL, options)
const networkStatus = await Network.getStatus()
if (!networkStatus.connected) {
return handleOfflineRequest(fullURL, options)
}
// Online: Perform the fetch
try {
const response = await fetch(fullURL, options)
if (response.ok) {
const data = await response.clone().json()
const optionsHash = murmurhash.v3(JSON.stringify(options))
await localStore.saveToCache(fullURL + optionsHash, data)
networkManager.setOnline()
} else if (
response.status === 503 ||
response.type === 'opaque' ||
response.status === 0
) {
networkManager.setOffline()
return handleOfflineRequest(fullURL, options)
}
// return promise that resolves to response object:
return Promise.resolve(response)
} catch (error) {
networkManager.setOffline()
console.error('Fetch error:', error)
// throw error
return handleOfflineRequest(fullURL, options)
}
}
export const HEADERS = () => {
@@ -61,14 +97,13 @@ export const isTokenValid = () => {
const expiration = localStorage.getItem('ca_expiration')
const token = localStorage.getItem('ca_token')
if (localStorage.getItem('ca_token')) {
if (token) {
const now = new Date()
const expire = new Date(expiration)
if (now < expire) {
if (now.getTime() + 24 * 60 * 60 * 1000 > expire.getTime()) {
refreshAccessToken()
}
return true
} else {
localStorage.removeItem('ca_token')
@@ -92,3 +127,38 @@ export const refreshAccessToken = () => {
}
})
}
async function handleOfflineRequest(url, options) {
// if get request then attempt to fetch from cache otherewise queue it :
if (options.method === 'GET') {
return attemptFetchFromCache(url, options)
} else {
// Queue the request for later processing
const requestId = murmurhash.v3(JSON.stringify({ url, options }))
await localStore.queueRequest(requestId, { url, options })
console.log('Request queued for later processing:', requestId)
return Promise.reject(new Error('Offline and request queued: ' + requestId))
}
}
async function attemptFetchFromCache(url, options) {
const optionsHash = murmurhash.v3(JSON.stringify(options))
const cachedData = await localStore.getFromCache(url + optionsHash)
networkManager.setOffline()
if (cachedData) {
return Promise.resolve({
ok: true,
status: 200,
json: async () => cachedData,
})
} else {
return Promise.reject(
new Error(
'No cached data found for URL: ' +
url +
' with options hash: ' +
optionsHash,
),
)
}
}