modified localstorage data

This commit is contained in:
PhilReact 2025-05-03 23:38:33 +03:00
parent 9663ab2459
commit f0c1b956ac
6 changed files with 129 additions and 44 deletions

11
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "qapp-core",
"version": "1.0.19",
"version": "1.0.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "qapp-core",
"version": "1.0.19",
"version": "1.0.22",
"license": "MIT",
"dependencies": {
"@tanstack/react-virtual": "^3.13.2",
@ -15,6 +15,7 @@
"compressorjs": "^1.2.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"dexie": "^4.0.11",
"dompurify": "^3.2.4",
"react-dropzone": "^14.3.8",
"react-hot-toast": "^2.5.2",
@ -1871,6 +1872,12 @@
}
}
},
"node_modules/dexie": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz",
"integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==",
"license": "Apache-2.0"
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "qapp-core",
"version": "1.0.22",
"version": "1.0.24",
"description": "Qortal's core React library with global state, UI components, and utilities",
"main": "dist/index.js",
"module": "dist/index.mjs",
@ -29,6 +29,7 @@
"compressorjs": "^1.2.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"dexie": "^4.0.11",
"dompurify": "^3.2.4",
"react-dropzone": "^14.3.8",
"react-hot-toast": "^2.5.2",

View File

@ -4,7 +4,7 @@ import { useResources } from "../hooks/useResources";
import { useAppInfo } from "../hooks/useAppInfo";
import { useIdentifiers } from "../hooks/useIdentifiers";
import { Toaster } from "react-hot-toast";
import { useLocalStorage } from "../hooks/useLocalStorage";
import { usePersistentStore } from "../hooks/usePersistentStore";
import { IndexManager } from "../components/IndexManager/IndexManager";
import { useIndexes } from "../hooks/useIndexes";
@ -18,7 +18,7 @@ auth: ReturnType<typeof useAuth>;
lists: ReturnType<typeof useResources>;
appInfo: ReturnType<typeof useAppInfo>;
identifierOperations: ReturnType<typeof useIdentifiers>
localStorageOperations: ReturnType<typeof useLocalStorage>
persistentOperations: ReturnType<typeof usePersistentStore>
indexOperations: ReturnType<typeof useIndexes>
}
@ -48,10 +48,10 @@ export const GlobalProvider = ({ children, config, toastStyle = {} }: GlobalProv
const appInfo = useAppInfo(config.appName, config?.publicSalt)
const lists = useResources()
const identifierOperations = useIdentifiers(config.publicSalt, config.appName)
const localStorageOperations = useLocalStorage(config.publicSalt, config.appName)
const persistentOperations = usePersistentStore(config.publicSalt, config.appName, auth?.address)
const indexOperations = useIndexes()
// ✅ Merge all hooks into a single `contextValue`
const contextValue = useMemo(() => ({ auth, lists, appInfo, identifierOperations, localStorageOperations, indexOperations }), [auth, lists, appInfo, identifierOperations, localStorageOperations]);
const contextValue = useMemo(() => ({ auth, lists, appInfo, identifierOperations, persistentOperations, indexOperations }), [auth, lists, appInfo, identifierOperations, persistentOperations]);
return (

View File

@ -1,37 +0,0 @@
import React, { useCallback, useEffect, useMemo } from "react";
import { useAppStore } from "../state/app";
import { EnumCollisionStrength, hashWord } from "../utils/encryption";
export const useLocalStorage = (publicSalt: string, appName: string) => {
const setTimestamp = useCallback(async (timestamp: number, storageId: string)=> {
const hashedString = await hashWord(`${appName}-${storageId}`, EnumCollisionStrength.HIGH, publicSalt)
localStorage.setItem(hashedString, JSON.stringify(timestamp));
return true
}, [appName, publicSalt])
const getTimestamp = useCallback(async ( storageId: string)=> {
const hashedString = await hashWord(`${appName}-${storageId}`, EnumCollisionStrength.HIGH, publicSalt)
const stored = localStorage.getItem(hashedString);
if(stored){
return JSON.parse(stored)
} else return null
}, [appName, publicSalt])
const isNewTimestamp = useCallback(async( storageId: string, differenceTimestamp: number)=> {
const hashedString = await hashWord(`${appName}-${storageId}`, EnumCollisionStrength.HIGH, publicSalt)
const stored = localStorage.getItem(hashedString);
if(stored){
const storedTimestamp = JSON.parse(stored)
return (Date.now() - storedTimestamp) > differenceTimestamp
} else return true
}, [appName, publicSalt])
return {
setTimestamp,
getTimestamp,
isNewTimestamp
};
};

View File

@ -0,0 +1,88 @@
import { useCallback } from 'react';
import { EnumCollisionStrength, hashWord } from '../utils/encryption';
import { db } from '../utils/persistentDb';
export const usePersistentStore = (
publicSalt: string,
appName: string,
qortalAddress?: string | null
) => {
const getHashedId = useCallback(
async (id: string) => {
const key = `${appName}-${qortalAddress ?? 'no-address'}-${id}`;
return await hashWord(key, EnumCollisionStrength.HIGH, publicSalt);
},
[appName, publicSalt, qortalAddress]
);
// --- TIMESTAMP FUNCTIONS ---
const setTimestamp = useCallback(
async (timestamp: number, storageId: string) => {
const id = await getHashedId(storageId);
await db.timestamps.put({ id, timestamp });
return true;
},
[getHashedId]
);
const getTimestamp = useCallback(
async (storageId: string) => {
const id = await getHashedId(storageId);
const entry = await db.timestamps.get(id);
return entry?.timestamp ?? null;
},
[getHashedId]
);
const isNewTimestamp = useCallback(
async (storageId: string, differenceTimestamp: number) => {
const id = await getHashedId(storageId);
const entry = await db.timestamps.get(id);
if (!entry) return true;
return Date.now() - entry.timestamp > differenceTimestamp;
},
[getHashedId]
);
// --- GENERIC CRUD FOR DYNAMIC DATA ---
const saveData = useCallback(
async (id: string, data: any) => {
const hashedId = await getHashedId(id);
await db.dynamicData.put({ id: hashedId, data });
},
[getHashedId]
);
const getData = useCallback(
async (id: string) => {
const hashedId = await getHashedId(id);
const entry = await db.dynamicData.get(hashedId);
return entry?.data ?? null;
},
[getHashedId]
);
const deleteData = useCallback(
async (id: string) => {
const hashedId = await getHashedId(id);
await db.dynamicData.delete(hashedId);
},
[getHashedId]
);
const listAllData = useCallback(async () => {
return await db.dynamicData.toArray();
}, []);
return {
setTimestamp,
getTimestamp,
isNewTimestamp,
saveData,
getData,
deleteData,
listAllData,
};
};

26
src/utils/persistentDb.ts Normal file
View File

@ -0,0 +1,26 @@
import Dexie, { Table } from 'dexie';
export interface TimestampEntry {
id: string; // hashed key
timestamp: number;
}
export interface DynamicEntry {
id: string;
data: any;
}
class AppDatabase extends Dexie {
timestamps!: Table<TimestampEntry>;
dynamicData!: Table<DynamicEntry>;
constructor() {
super('MyAppDB');
this.version(1).stores({
timestamps: 'id',
dynamicData: 'id',
});
}
}
export const db = new AppDatabase();