@ombori/ga-settings
Advanced tools
Comparing version 4.1.20 to 4.1.21
@@ -6,2 +6,13 @@ # Change Log | ||
## 4.1.21 (2024-08-25) | ||
### Bug Fixes | ||
* attempt at new caching at app level ([7a34054](https://github.com/ombori/gridapp/commit/7a34054a04b4c6ab07d664582c14bc5e638956fe)) | ||
## 4.1.20 (2024-08-25) | ||
@@ -8,0 +19,0 @@ |
@@ -13,2 +13,38 @@ "use strict"; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -20,2 +56,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var react_1 = require("react"); | ||
var pouchdb_browser_1 = __importDefault(require("pouchdb-browser")); | ||
var get_local_browser_settings_1 = __importDefault(require("./get-local-browser-settings")); | ||
@@ -27,2 +64,187 @@ var utils_1 = require("./utils"); | ||
var localStorageGridAppSettings = window.localStorage.getItem(settingsKey); | ||
function replaceUrlsAndCache(settings) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
// Helper function to recursively traverse the settings object | ||
function recursiveReplace(obj) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _i, obj_1, item, _a, _b, _c, key, mediaId, cachedBlobUrl, newBlobUrl; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
if (!Array.isArray(obj)) return [3 /*break*/, 5]; | ||
_i = 0, obj_1 = obj; | ||
_d.label = 1; | ||
case 1: | ||
if (!(_i < obj_1.length)) return [3 /*break*/, 4]; | ||
item = obj_1[_i]; | ||
return [4 /*yield*/, recursiveReplace(item)]; | ||
case 2: | ||
_d.sent(); | ||
_d.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [3 /*break*/, 13]; | ||
case 5: | ||
if (!(obj !== null && typeof obj === 'object')) return [3 /*break*/, 13]; | ||
_a = []; | ||
for (_b in obj) | ||
_a.push(_b); | ||
_c = 0; | ||
_d.label = 6; | ||
case 6: | ||
if (!(_c < _a.length)) return [3 /*break*/, 9]; | ||
key = _a[_c]; | ||
if (!(obj[key] && typeof obj[key] === 'object')) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, recursiveReplace(obj[key])]; | ||
case 7: | ||
_d.sent(); | ||
_d.label = 8; | ||
case 8: | ||
_c++; | ||
return [3 /*break*/, 6]; | ||
case 9: | ||
if (!(obj.type === 'MEDIA' && obj.media)) return [3 /*break*/, 13]; | ||
if (!(obj.media.url && !obj.media.url.startsWith('blob:'))) return [3 /*break*/, 13]; | ||
mediaId = pageIdentifier + "_" + obj.media.id; | ||
usedMediaIds.add(mediaId); | ||
// Set originalUrl to the current url if not already set | ||
if (!obj.media.originalUrl) { | ||
obj.media.originalUrl = obj.media.url; | ||
} | ||
return [4 /*yield*/, checkCacheAndGetBlobUrl(mediaId, db)]; | ||
case 10: | ||
cachedBlobUrl = _d.sent(); | ||
if (!cachedBlobUrl) return [3 /*break*/, 11]; | ||
// Use the cached blob URL if available | ||
obj.media.url = cachedBlobUrl; | ||
return [3 /*break*/, 13]; | ||
case 11: return [4 /*yield*/, downloadAndStoreMedia(obj.media.url, mediaId, db)]; | ||
case 12: | ||
newBlobUrl = _d.sent(); | ||
obj.media.url = newBlobUrl; | ||
_d.label = 13; | ||
case 13: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
// Function to check if a media file is already cached in IndexedDB | ||
function checkCacheAndGetBlobUrl(mediaId, db) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var doc, cachedBlob, error_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 3, , 4]); | ||
return [4 /*yield*/, db.get(mediaId)]; | ||
case 1: | ||
doc = _a.sent(); | ||
return [4 /*yield*/, db.getAttachment(mediaId, 'mediaFile')]; | ||
case 2: | ||
cachedBlob = _a.sent(); | ||
return [2 /*return*/, URL.createObjectURL(cachedBlob)]; | ||
case 3: | ||
error_1 = _a.sent(); | ||
// If the media is not found in the cache, return null | ||
return [2 /*return*/, null]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
// Function to download the media and store it in IndexedDB using PouchDB | ||
function downloadAndStoreMedia(url, mediaId, db) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var response, blob, doc, newBlobUrl, error_2; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 4, , 5]); | ||
return [4 /*yield*/, fetch(url)]; | ||
case 1: | ||
response = _a.sent(); | ||
return [4 /*yield*/, response.blob()]; | ||
case 2: | ||
blob = _a.sent(); | ||
doc = { | ||
_id: mediaId, | ||
_attachments: { | ||
mediaFile: { | ||
content_type: blob.type, | ||
data: blob, | ||
}, | ||
}, | ||
}; | ||
return [4 /*yield*/, db.put(doc)]; | ||
case 3: | ||
_a.sent(); | ||
newBlobUrl = URL.createObjectURL(blob); | ||
return [2 /*return*/, newBlobUrl]; | ||
case 4: | ||
error_2 = _a.sent(); | ||
console.error('Error downloading or storing media:', error_2); | ||
return [2 /*return*/, url]; // In case of error, return the original URL | ||
case 5: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
// Function to clean up unused media files from the cache | ||
function cleanupUnusedMedia(db, usedMediaIds, pageIdentifier) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var allDocs, _i, _a, doc, docId, error_3; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_b.trys.push([0, 6, , 7]); | ||
return [4 /*yield*/, db.allDocs()]; | ||
case 1: | ||
allDocs = _b.sent(); | ||
_i = 0, _a = allDocs.rows; | ||
_b.label = 2; | ||
case 2: | ||
if (!(_i < _a.length)) return [3 /*break*/, 5]; | ||
doc = _a[_i]; | ||
docId = doc.id; | ||
if (!(docId.startsWith(pageIdentifier) && !usedMediaIds.has(docId))) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, db.remove(docId, doc.value.rev)]; | ||
case 3: | ||
_b.sent(); | ||
_b.label = 4; | ||
case 4: | ||
_i++; | ||
return [3 /*break*/, 2]; | ||
case 5: return [3 /*break*/, 7]; | ||
case 6: | ||
error_3 = _b.sent(); | ||
console.error('Error cleaning up unused media:', error_3); | ||
return [3 /*break*/, 7]; | ||
case 7: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
var db, pageIdentifier, usedMediaIds; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
db = new pouchdb_browser_1.default('mediaDatabase'); | ||
pageIdentifier = document.location.pathname; | ||
usedMediaIds = new Set(); | ||
// Start the recursive process | ||
return [4 /*yield*/, recursiveReplace(settings)]; | ||
case 1: | ||
// Start the recursive process | ||
_a.sent(); | ||
// Cleanup unused media files from the cache | ||
return [4 /*yield*/, cleanupUnusedMedia(db, usedMediaIds, pageIdentifier)]; | ||
case 2: | ||
// Cleanup unused media files from the cache | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
var log = function () { | ||
@@ -157,22 +379,30 @@ var data = []; | ||
var localStorageSettings_1 = JSON.parse(localStorageGridAppSettings); | ||
console.log({ localStorageSettings: localStorageSettings_1 }); | ||
window.gridParams = { | ||
id: 'app-container', | ||
root: settingsKey, | ||
settings: localStorageSettings_1, | ||
}; | ||
window.gridapp = { | ||
addEventListener: function (event, listener) { | ||
window.addEventListener(event, listener); | ||
}, | ||
removeEventListener: function (event, listener) { | ||
window.removeEventListener(event, listener); | ||
}, | ||
send: function (_event) { | ||
// window.send(event); | ||
}, | ||
getSettings: function () { return localStorageSettings_1; }, | ||
}; | ||
window.dispatchEvent(new CustomEvent('ready')); | ||
window.dispatchEvent(new Event('GridappReady')); | ||
replaceUrlsAndCache(localStorageSettings_1) | ||
.then(function () { | ||
console.log('All URLs processed, cached in IndexedDB, and unused files cleaned up.'); | ||
console.log(JSON.stringify(localStorageSettings_1, null, 2)); | ||
console.log({ localStorageSettings: localStorageSettings_1 }); | ||
window.gridParams = { | ||
id: 'app-container', | ||
root: settingsKey, | ||
settings: localStorageSettings_1, | ||
}; | ||
window.gridapp = { | ||
addEventListener: function (event, listener) { | ||
window.addEventListener(event, listener); | ||
}, | ||
removeEventListener: function (event, listener) { | ||
window.removeEventListener(event, listener); | ||
}, | ||
send: function (_event) { | ||
// window.send(event); | ||
}, | ||
getSettings: function () { return localStorageSettings_1; }, | ||
}; | ||
window.dispatchEvent(new CustomEvent('ready')); | ||
window.dispatchEvent(new Event('GridappReady')); | ||
}) | ||
.catch(function (error) { | ||
console.error('Error processing URLs:', error); | ||
}); | ||
} | ||
@@ -179,0 +409,0 @@ catch (err) { |
{ | ||
"name": "@ombori/ga-settings", | ||
"version": "4.1.20", | ||
"version": "4.1.21", | ||
"main": "dist/index.js", | ||
@@ -22,3 +22,3 @@ "license": "UNLICENSED", | ||
}, | ||
"gitHead": "e1fbdb296abb246a5d97ac5a3b152bb716017bf2" | ||
"gitHead": "dc8463503c7732e24e133184fc630b3f35e0e738" | ||
} |
166
src/index.ts
import { useEffect, useState, useRef } from 'react'; | ||
import PouchDB from 'pouchdb-browser'; | ||
import getLocalBrowserSettings from './get-local-browser-settings'; | ||
@@ -20,2 +21,117 @@ import { isObjectEmpty, isDev } from './utils'; | ||
async function replaceUrlsAndCache(settings: any) { | ||
// Initialize PouchDB | ||
const db = new PouchDB('mediaDatabase'); | ||
// Use the page identifier based on the current pathname | ||
const pageIdentifier = document.location.pathname; | ||
// Track used media IDs for cleanup | ||
const usedMediaIds = new Set(); | ||
// Helper function to recursively traverse the settings object | ||
async function recursiveReplace(obj: any) { | ||
if (Array.isArray(obj)) { | ||
// If the object is an array, iterate over each element | ||
for (const item of obj) { | ||
await recursiveReplace(item); | ||
} | ||
} else if (obj !== null && typeof obj === 'object') { | ||
// If the object is a non-null object, iterate over its properties | ||
for (const key in obj) { | ||
if (obj[key] && typeof obj[key] === 'object') { | ||
await recursiveReplace(obj[key]); | ||
} | ||
} | ||
// Check if the object is of type MEDIA and has the media property | ||
if (obj.type === 'MEDIA' && obj.media) { | ||
if (obj.media.url && !obj.media.url.startsWith('blob:')) { | ||
// Create a unique media ID with the page identifier | ||
const mediaId = `${pageIdentifier}_${obj.media.id}`; | ||
usedMediaIds.add(mediaId); | ||
// Set originalUrl to the current url if not already set | ||
if (!obj.media.originalUrl) { | ||
obj.media.originalUrl = obj.media.url; | ||
} | ||
// Check if the media is already cached | ||
const cachedBlobUrl = await checkCacheAndGetBlobUrl(mediaId, db); | ||
if (cachedBlobUrl) { | ||
// Use the cached blob URL if available | ||
obj.media.url = cachedBlobUrl; | ||
} else { | ||
// Download the media, cache it, and replace the URL with a new Blob URL | ||
const newBlobUrl = await downloadAndStoreMedia(obj.media.url, mediaId, db); | ||
obj.media.url = newBlobUrl; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// Function to check if a media file is already cached in IndexedDB | ||
async function checkCacheAndGetBlobUrl(mediaId: any, db: any) { | ||
try { | ||
const doc = await db.get(mediaId); | ||
const cachedBlob = await db.getAttachment(mediaId, 'mediaFile'); | ||
return URL.createObjectURL(cachedBlob); | ||
} catch (error) { | ||
// If the media is not found in the cache, return null | ||
return null; | ||
} | ||
} | ||
// Function to download the media and store it in IndexedDB using PouchDB | ||
async function downloadAndStoreMedia(url: any, mediaId: any, db: any) { | ||
try { | ||
const response = await fetch(url); | ||
const blob = await response.blob(); | ||
// Store the blob in PouchDB with the media ID as the document ID | ||
const doc = { | ||
_id: mediaId, | ||
_attachments: { | ||
mediaFile: { | ||
content_type: blob.type, | ||
data: blob, | ||
}, | ||
}, | ||
}; | ||
await db.put(doc); | ||
// Generate a new Blob URL from the stored media | ||
const newBlobUrl = URL.createObjectURL(blob); | ||
return newBlobUrl; | ||
} catch (error) { | ||
console.error('Error downloading or storing media:', error); | ||
return url; // In case of error, return the original URL | ||
} | ||
} | ||
// Function to clean up unused media files from the cache | ||
async function cleanupUnusedMedia(db: any, usedMediaIds: any, pageIdentifier: any) { | ||
try { | ||
const allDocs = await db.allDocs(); | ||
for (const doc of allDocs.rows) { | ||
const docId = doc.id; | ||
if (docId.startsWith(pageIdentifier) && !usedMediaIds.has(docId)) { | ||
await db.remove(docId, doc.value.rev); | ||
} | ||
} | ||
} catch (error) { | ||
console.error('Error cleaning up unused media:', error); | ||
} | ||
} | ||
// Start the recursive process | ||
await recursiveReplace(settings); | ||
// Cleanup unused media files from the cache | ||
await cleanupUnusedMedia(db, usedMediaIds, pageIdentifier); | ||
} | ||
const log = (...data: any[]): void => { | ||
@@ -171,22 +287,32 @@ if (process.env.NODE_ENV === 'test') return; | ||
const localStorageSettings = JSON.parse(localStorageGridAppSettings); | ||
console.log({ localStorageSettings }); | ||
(window as any).gridParams = { | ||
id: 'app-container', | ||
root: settingsKey, | ||
settings: localStorageSettings, | ||
}; | ||
window.gridapp = { | ||
addEventListener: (event: any, listener: any) => { | ||
window.addEventListener(event, listener); | ||
}, | ||
removeEventListener: (event: any, listener: any) => { | ||
window.removeEventListener(event, listener); | ||
}, | ||
send: (_event: any) => { | ||
// window.send(event); | ||
}, | ||
getSettings: () => localStorageSettings, | ||
}; | ||
window.dispatchEvent(new CustomEvent('ready')); | ||
window.dispatchEvent(new Event('GridappReady')); | ||
replaceUrlsAndCache(localStorageSettings) | ||
.then(() => { | ||
console.log( | ||
'All URLs processed, cached in IndexedDB, and unused files cleaned up.', | ||
); | ||
console.log(JSON.stringify(localStorageSettings, null, 2)); | ||
console.log({ localStorageSettings }); | ||
(window as any).gridParams = { | ||
id: 'app-container', | ||
root: settingsKey, | ||
settings: localStorageSettings, | ||
}; | ||
window.gridapp = { | ||
addEventListener: (event: any, listener: any) => { | ||
window.addEventListener(event, listener); | ||
}, | ||
removeEventListener: (event: any, listener: any) => { | ||
window.removeEventListener(event, listener); | ||
}, | ||
send: (_event: any) => { | ||
// window.send(event); | ||
}, | ||
getSettings: () => localStorageSettings, | ||
}; | ||
window.dispatchEvent(new CustomEvent('ready')); | ||
window.dispatchEvent(new Event('GridappReady')); | ||
}) | ||
.catch((error) => { | ||
console.error('Error processing URLs:', error); | ||
}); | ||
} catch (err) { | ||
@@ -193,0 +319,0 @@ console.log('localStorageGridAppSettings error:', err); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
92817
1493
1