2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-06 07:20:40 +00:00

Use browser storage with API

This commit is contained in:
Vjacheslav Trushkin 2022-06-28 23:40:20 +03:00
parent 0e0b6b66ef
commit 882be420f9
12 changed files with 171 additions and 234 deletions

View File

@ -85,10 +85,6 @@
"require": "./lib/browser-storage/index.cjs",
"import": "./lib/browser-storage/index.mjs"
},
"./lib/browser-storage/load": {
"require": "./lib/browser-storage/load.cjs",
"import": "./lib/browser-storage/load.mjs"
},
"./lib/browser-storage/mock": {
"require": "./lib/browser-storage/mock.cjs",
"import": "./lib/browser-storage/mock.mjs"
@ -105,10 +101,6 @@
"require": "./lib/builder/functions.cjs",
"import": "./lib/builder/functions.mjs"
},
"./lib/cache": {
"require": "./lib/cache.cjs",
"import": "./lib/cache.mjs"
},
"./lib/icon/list": {
"require": "./lib/icon/list.cjs",
"import": "./lib/icon/list.mjs"

View File

@ -12,7 +12,7 @@ import { getStorage, addIconSet } from '../storage/storage';
import { listToIcons } from '../icon/list';
import { allowSimpleNames, getIconData } from '../storage/functions';
import { sendAPIQuery } from './query';
import { cache } from '../cache';
import { storeInBrowserStorage } from '../browser-storage/store';
// Empty abort callback for loadIcons()
function emptyCallback(): void {
@ -207,9 +207,10 @@ function loadNewIcons(provider: string, prefix: string, icons: string[]): void {
});
// Cache API response
if (cache.store) {
cache.store(provider, data as IconifyJSON);
}
storeInBrowserStorage(
provider,
data as IconifyJSON
);
} catch (err) {
console.error(err);
}

View File

@ -2,6 +2,7 @@ import type {
BrowserStorageConfig,
BrowserStorageCount,
BrowserStorageEmptyList,
BrowserStorageStatus,
} from './types';
/**
@ -31,8 +32,8 @@ export const browserStorageEmptyItems: BrowserStorageEmptyList = {
/**
* Flag to check if storage has been loaded
*/
export let browserStorageLoaded = false;
export let browserStorageStatus: BrowserStorageStatus = false;
export function setBrowserStorageStatus(loaded: boolean) {
browserStorageLoaded = loaded;
export function setBrowserStorageStatus(status: BrowserStorageStatus) {
browserStorageStatus = status;
}

View File

@ -1,16 +0,0 @@
import { browserCachePrefix } from './config';
import { getBrowserStorageItemsCount } from './count';
/**
* Destroy old cache
*/
export function destroyBrowserStorage(storage: typeof localStorage): void {
try {
const total = getBrowserStorageItemsCount(storage);
for (let i = 0; i < total; i++) {
storage.removeItem(browserCachePrefix + i.toString());
}
} catch (err) {
//
}
}

View File

@ -1,11 +1,137 @@
import { cache } from '../cache';
import { loadBrowserStorageCache } from './load';
import { storeInBrowserStorage } from './store';
import { addIconSet, getStorage } from '../storage/storage';
import {
browserCachePrefix,
browserCacheVersion,
browserCacheVersionKey,
browserStorageCacheExpiration,
browserStorageHour,
} from './config';
import {
getBrowserStorageItemsCount,
setBrowserStorageItemsCount,
} from './count';
import {
browserStorageConfig,
browserStorageEmptyItems,
browserStorageStatus,
setBrowserStorageStatus,
} from './data';
import { getBrowserStorage } from './global';
import type { BrowserStorageConfig, BrowserStorageItem } from './types';
/**
* Init browser storage
* Load icons from cache
*/
export function initBrowserStorage() {
cache.store = storeInBrowserStorage;
loadBrowserStorageCache();
if (browserStorageStatus === true) {
return;
}
setBrowserStorageStatus('loading');
// Minimum time
const minTime =
Math.floor(Date.now() / browserStorageHour) -
browserStorageCacheExpiration;
// Load data from storage
function load(key: keyof BrowserStorageConfig): void {
const func = getBrowserStorage(key);
if (!func) {
return;
}
// Get one item from storage
const getItem = (index: number): boolean => {
const name = browserCachePrefix + index.toString();
const item = func.getItem(name);
if (typeof item !== 'string') {
// Does not exist
return false;
}
// Get item, validate it
let valid = true;
try {
// Parse, check time stamp
const data = JSON.parse(item) as BrowserStorageItem;
if (
typeof data !== 'object' ||
typeof data.cached !== 'number' ||
data.cached < minTime ||
typeof data.provider !== 'string' ||
typeof data.data !== 'object' ||
typeof data.data.prefix !== 'string'
) {
valid = false;
} else {
// Add icon set
const provider = data.provider;
const prefix = data.data.prefix;
const storage = getStorage(provider, prefix);
valid = addIconSet(storage, data.data).length > 0;
}
} catch (err) {
valid = false;
}
if (!valid) {
func.removeItem(name);
}
return valid;
};
try {
// Get version
const version = func.getItem(browserCacheVersionKey);
if (version !== browserCacheVersion) {
if (version) {
// Version is set, but invalid - remove old entries
try {
const total = getBrowserStorageItemsCount(func);
for (let i = 0; i < total; i++) {
func.removeItem(browserCachePrefix + i.toString());
}
} catch (err) {
//
}
}
// Empty data
try {
func.setItem(browserCacheVersionKey, browserCacheVersion);
} catch (err) {
//
}
setBrowserStorageItemsCount(func, key, 0);
return;
}
// Get number of stored items
let total = getBrowserStorageItemsCount(func);
for (let i = total - 1; i >= 0; i--) {
if (!getItem(i)) {
// Remove item
if (i === total - 1) {
// Last item - reduce country
total--;
} else {
// Mark as empty
browserStorageEmptyItems[key].push(i);
}
}
}
// Update total
setBrowserStorageItemsCount(func, key, total);
} catch (err) {
//
}
}
for (const key in browserStorageConfig) {
load(key as keyof BrowserStorageConfig);
}
setBrowserStorageStatus(true);
}

View File

@ -1,151 +0,0 @@
import { addIconSet, getStorage } from '../storage/storage';
import {
browserCachePrefix,
browserCacheVersion,
browserCacheVersionKey,
browserStorageCacheExpiration,
browserStorageHour,
} from './config';
import {
getBrowserStorageItemsCount,
setBrowserStorageItemsCount,
} from './count';
import {
browserStorageConfig,
browserStorageEmptyItems,
browserStorageLoaded,
setBrowserStorageStatus,
} from './data';
import { getBrowserStorage } from './global';
import type { BrowserStorageConfig, BrowserStorageItem } from './types';
/**
* Destroy old cache
*/
function destroyBrowserStorage(storage: typeof localStorage): void {
try {
const total = getBrowserStorageItemsCount(storage);
for (let i = 0; i < total; i++) {
storage.removeItem(browserCachePrefix + i.toString());
}
} catch (err) {
//
}
}
/**
* Initialize storage
*/
function initBrowserStorage(
storage: typeof localStorage,
key: keyof BrowserStorageConfig
): void {
try {
storage.setItem(browserCacheVersionKey, browserCacheVersion);
} catch (err) {
//
}
setBrowserStorageItemsCount(storage, key, 0);
}
/**
* Load icons from cache
*/
export function loadBrowserStorageCache() {
if (browserStorageLoaded) {
return;
}
setBrowserStorageStatus(true);
// Minimum time
const minTime =
Math.floor(Date.now() / browserStorageHour) -
browserStorageCacheExpiration;
// Load data from storage
function load(key: keyof BrowserStorageConfig): void {
const func = getBrowserStorage(key);
if (!func) {
return;
}
// Get one item from storage
const getItem = (index: number): boolean => {
const name = browserCachePrefix + index.toString();
const item = func.getItem(name);
if (typeof item !== 'string') {
// Does not exist
return false;
}
// Get item, validate it
let valid = true;
try {
// Parse, check time stamp
const data = JSON.parse(item) as BrowserStorageItem;
if (
typeof data !== 'object' ||
typeof data.cached !== 'number' ||
data.cached < minTime ||
typeof data.provider !== 'string' ||
typeof data.data !== 'object' ||
typeof data.data.prefix !== 'string'
) {
valid = false;
} else {
// Add icon set
const provider = data.provider;
const prefix = data.data.prefix;
const storage = getStorage(provider, prefix);
valid = addIconSet(storage, data.data).length > 0;
}
} catch (err) {
valid = false;
}
if (!valid) {
func.removeItem(name);
}
return valid;
};
try {
// Get version
const version = func.getItem(browserCacheVersionKey);
if (version !== browserCacheVersion) {
if (version) {
// Version is set, but invalid - remove old entries
destroyBrowserStorage(func);
}
// Empty data
initBrowserStorage(func, key);
return;
}
// Get number of stored items
let total = getBrowserStorageItemsCount(func);
for (let i = total - 1; i >= 0; i--) {
if (!getItem(i)) {
// Remove item
if (i === total - 1) {
// Last item - reduce country
total--;
} else {
// Mark as empty
browserStorageEmptyItems[key].push(i);
}
}
}
// Update total
setBrowserStorageItemsCount(func, key, total);
} catch (err) {
//
}
}
for (const key in browserStorageConfig) {
load(key as keyof BrowserStorageConfig);
}
}

View File

@ -1,26 +1,22 @@
import type { IconifyJSON } from '@iconify/types';
import type { CacheIcons } from '../cache';
import { browserCachePrefix, browserStorageHour } from './config';
import { setBrowserStorageItemsCount } from './count';
import {
browserStorageConfig,
browserStorageEmptyItems,
browserStorageItemsCount,
browserStorageLoaded,
browserStorageStatus,
} from './data';
import { getBrowserStorage } from './global';
import { loadBrowserStorageCache } from './load';
import { initBrowserStorage } from './index';
import type { BrowserStorageConfig, BrowserStorageItem } from './types';
/**
* Function to cache icons
*/
export const storeInBrowserStorage: CacheIcons = (
provider: string,
data: IconifyJSON
): void => {
if (!browserStorageLoaded) {
loadBrowserStorageCache();
export function storeInBrowserStorage(provider: string, data: IconifyJSON) {
if (!browserStorageStatus) {
initBrowserStorage();
}
function store(key: keyof BrowserStorageConfig): boolean {
@ -75,4 +71,4 @@ export const storeInBrowserStorage: CacheIcons = (
if (!store('local')) {
store('session');
}
};
}

View File

@ -21,3 +21,6 @@ export interface BrowserStorageItem {
provider: string;
data: IconifyJSON;
}
// Status: not loaded, loading, loaded
export type BrowserStorageStatus = false | 'loading' | true;

View File

@ -1,15 +0,0 @@
import type { IconifyJSON } from '@iconify/types';
/**
* Function to cache loaded icons set
*/
export type CacheIcons = (provider: string, data: IconifyJSON) => void;
/**
* Module
*/
interface CacheModule {
store?: CacheIcons;
}
export const cache: CacheModule = {};

View File

@ -1,4 +1,4 @@
import { loadBrowserStorageCache } from '../../lib/browser-storage/load';
import { initBrowserStorage } from '../../lib/browser-storage';
import {
browserStorageItemsCount,
browserStorageConfig,
@ -28,7 +28,7 @@ describe('Testing mocked localStorage', () => {
});
// Attempt to load
loadBrowserStorageCache();
initBrowserStorage();
// Everything should be disabled
expect(browserStorageConfig).toEqual({
@ -59,7 +59,7 @@ describe('Testing mocked localStorage', () => {
});
// Attempt to load
loadBrowserStorageCache();
initBrowserStorage();
// sessionStorage should be disabled
expect(browserStorageConfig).toEqual({
@ -118,7 +118,7 @@ describe('Testing mocked localStorage', () => {
});
// Attempt to load
loadBrowserStorageCache();
initBrowserStorage();
// Everything should be disabled because read-only mock throws errors
expect(browserStorageConfig).toEqual({
@ -172,7 +172,7 @@ describe('Testing mocked localStorage', () => {
});
// Attempt to load
loadBrowserStorageCache();
initBrowserStorage();
// sessionStorage should be disabled
expect(browserStorageConfig).toEqual({
@ -204,7 +204,7 @@ describe('Testing mocked localStorage', () => {
});
// Attempt to load
loadBrowserStorageCache();
initBrowserStorage();
// Everything should be working
expect(browserStorageConfig).toEqual({

View File

@ -1,6 +1,6 @@
import type { IconifyJSON } from '@iconify/types';
import type { BrowserStorageItem } from '../../lib/browser-storage/types';
import { loadBrowserStorageCache } from '../../lib/browser-storage/load';
import { initBrowserStorage } from '../../lib/browser-storage';
import {
browserStorageItemsCount,
browserStorageConfig,
@ -52,7 +52,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should exist now
expect(iconExists(icons, 'foo')).toBe(true);
@ -109,7 +109,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons2, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should exist now
expect(iconExists(icons, 'foo')).toBe(true);
@ -166,7 +166,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should not have loaded
expect(iconExists(icons, 'foo')).toBe(false);
@ -220,7 +220,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should not have loaded
expect(iconExists(icons, 'foo')).toBe(false);
@ -272,7 +272,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should not have loaded
expect(iconExists(icons, 'foo')).toBe(false);
@ -324,7 +324,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icon should exist now
expect(iconExists(icons, 'foo')).toBe(true);
@ -392,7 +392,7 @@ describe('Testing loading from localStorage', () => {
expect(iconExists(icons, 'foo4')).toBe(false);
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icons should exist now
expect(iconExists(icons, 'foo1')).toBe(true);
@ -476,7 +476,7 @@ describe('Testing loading from localStorage', () => {
}
// Load localStorage
loadBrowserStorageCache();
initBrowserStorage();
// Icons should exist now, except for number 4
for (let i = 0; i < 6; i++) {

View File

@ -1,7 +1,7 @@
import type { IconifyJSON } from '@iconify/types';
import type { BrowserStorageItem } from '../../lib/browser-storage/types';
import { storeInBrowserStorage } from '../../lib/browser-storage/store';
import { loadBrowserStorageCache } from '../../lib/browser-storage/load';
import { initBrowserStorage } from '../../lib/browser-storage';
import {
browserStorageItemsCount,
browserStorageConfig,
@ -188,7 +188,7 @@ describe('Testing saving to localStorage', () => {
});
// Load data
loadBrowserStorageCache();
initBrowserStorage();
// Check data
expect(browserStorageConfig).toEqual({
@ -281,7 +281,7 @@ describe('Testing saving to localStorage', () => {
});
// Load data
loadBrowserStorageCache();
initBrowserStorage();
// Check data
expect(browserStorageConfig).toEqual({
@ -399,7 +399,7 @@ describe('Testing saving to localStorage', () => {
expect(iconExists(icons, 'foo1')).toBe(false);
// Load cache
loadBrowserStorageCache();
initBrowserStorage();
expect(browserStorageConfig).toEqual({
local: true,
@ -516,7 +516,7 @@ describe('Testing saving to localStorage', () => {
});
// Load data
loadBrowserStorageCache();
initBrowserStorage();
// Check data
expect(browserStorageConfig).toEqual({
@ -631,7 +631,7 @@ describe('Testing saving to localStorage', () => {
});
// Load data
loadBrowserStorageCache();
initBrowserStorage();
// Check data
expect(browserStorageConfig).toEqual({