mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-10-31 05:59:48 +00:00
- dev ^0.86.4 at the root and in packages/apps; directives renamed by pezkuwi-dev-migrate-eslint10. - eslint --fix with the 0.86.4 config; one fix broke syntax (typescript-eslint prefer-optional-chain turned `(isStarted && onStart) && onStart()` into `(isStarted && onStart?.();` in TxButton) and is corrected by hand. - The remaining findings fixed rather than disabled: optional chains (each suggestion reviewed), `cause` on rethrown errors, `if` instead of `a && promise` in effects, parallel instead of sequential awaits in Promise.all, Error rejections, no `delete` on arrays (the storage query cache is a Map; an upload's file list no longer keeps a hole), no `[object Object]` stringification (TFunction's second argument is text or options), dead code removed (referenda's never-shown expand button, react-query's unused CoreDescriptor).
157 lines
4.4 KiB
TypeScript
157 lines
4.4 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/apps authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { IFavoriteChainProps, IFavoriteChainsStorage } from './types.js';
|
|
|
|
import { createWsEndpoints } from '@pezkuwi/apps-config';
|
|
|
|
export const FAVORITE_CHAINS_KEY = 'pezkuwi-app-favorite-chains';
|
|
const chainsConfig = createWsEndpoints((k, v) => (typeof v === 'string' && v) || k);
|
|
|
|
export const toggleFavoriteChain = (
|
|
chainInfo: IFavoriteChainProps
|
|
) => {
|
|
try {
|
|
const chainName = chainInfo.chainName;
|
|
const meta = { paraId: chainInfo.paraId ?? -1, relay: chainInfo.relay ?? 'Unknown' };
|
|
|
|
const favoriteChains = getFavoriteChains();
|
|
const existingEntries = favoriteChains[chainName] ?? [];
|
|
|
|
const alreadyExists = existingEntries.some(
|
|
(entry) => entry.relay === meta.relay && entry.paraId === meta.paraId
|
|
);
|
|
|
|
let updatedEntries: IFavoriteChainsStorage[string];
|
|
|
|
if (alreadyExists) {
|
|
// Remove the matching entry
|
|
updatedEntries = existingEntries.filter(
|
|
(entry) => entry.relay !== meta.relay || entry.paraId !== meta.paraId
|
|
);
|
|
} else {
|
|
// Add new entry
|
|
updatedEntries = [...existingEntries, meta];
|
|
}
|
|
|
|
const updatedChains: IFavoriteChainsStorage = { ...favoriteChains };
|
|
|
|
if (updatedEntries.length === 0) {
|
|
delete updatedChains[chainName];
|
|
} else {
|
|
updatedChains[chainName] = updatedEntries;
|
|
}
|
|
|
|
localStorage.setItem(FAVORITE_CHAINS_KEY, JSON.stringify(updatedChains));
|
|
} catch {}
|
|
};
|
|
|
|
export const getFavoriteChains = (): IFavoriteChainsStorage => {
|
|
try {
|
|
const favoriteChains = localStorage.getItem(FAVORITE_CHAINS_KEY);
|
|
|
|
if (!favoriteChains) {
|
|
return {};
|
|
}
|
|
|
|
const parsed: unknown = JSON.parse(favoriteChains);
|
|
|
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
throw new Error('Invalid favorite chains format');
|
|
}
|
|
|
|
const result: IFavoriteChainsStorage = {};
|
|
|
|
for (const [key, value] of Object.entries(parsed) as [string, IFavoriteChainsStorage[string]][]) {
|
|
if (!Array.isArray(value)) {
|
|
throw new Error(`Invalid value for key "${key}": not an array`);
|
|
}
|
|
|
|
const allValid = value.every(
|
|
(entry: IFavoriteChainsStorage[string][number]) =>
|
|
typeof entry === 'object' &&
|
|
entry !== null &&
|
|
typeof entry.relay === 'string' &&
|
|
typeof entry.paraId === 'number'
|
|
);
|
|
|
|
if (!allValid) {
|
|
throw new Error(`Invalid entries under key "${key}"`);
|
|
}
|
|
|
|
// Make sure key exists in chain config
|
|
if (chainsConfig.find((e) => e.text === key)) {
|
|
// Make sure "relay" value also exists in chain config
|
|
const matchingFavorites = value.filter((v) => v.relay === 'Unknown' ? true : !!chainsConfig.find((e) => e.text === v.relay));
|
|
|
|
if (matchingFavorites.length > 0) {
|
|
result[key] = matchingFavorites;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set filtered result to localStorage
|
|
localStorage.setItem(FAVORITE_CHAINS_KEY, JSON.stringify(result));
|
|
|
|
return result;
|
|
} catch (e) {
|
|
console.error('Failed to parse favorite chains:', e);
|
|
localStorage.removeItem(FAVORITE_CHAINS_KEY);
|
|
|
|
return {};
|
|
}
|
|
};
|
|
|
|
export const isFavoriteChain = (
|
|
favoriteChains: IFavoriteChainsStorage,
|
|
chainInfo: IFavoriteChainProps
|
|
): boolean => {
|
|
try {
|
|
const chainName = chainInfo.chainName;
|
|
const meta = { paraId: chainInfo.paraId ?? -1, relay: chainInfo.relay ?? 'Unknown' };
|
|
|
|
const list = favoriteChains[chainName];
|
|
|
|
if (!Array.isArray(list)) {
|
|
return false;
|
|
}
|
|
|
|
return list.some(
|
|
(entry) =>
|
|
entry.relay === meta.relay && entry.paraId === meta.paraId
|
|
);
|
|
} catch (e) {
|
|
console.error('Failed to check favorite chain:', e);
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export function getContrastingColor (hexColor: string): string {
|
|
if (typeof hexColor !== 'string') {
|
|
return '#000000';
|
|
}
|
|
|
|
let hex = hexColor.replace('#', '').trim();
|
|
|
|
if (hex.length === 3) {
|
|
hex = hex.split('').map((c) => c + c).join('');
|
|
}
|
|
|
|
if (hex.length !== 6 || /[^0-9a-f]/i.test(hex)) {
|
|
return '#000000';
|
|
}
|
|
|
|
try {
|
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
const g = parseInt(hex.substring(2, 4), 16);
|
|
const b = parseInt(hex.substring(4, 6), 16);
|
|
|
|
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
|
|
return luminance > 0.5 ? '#000000' : '#FFFFFF';
|
|
} catch {
|
|
return '#000000';
|
|
}
|
|
}
|