@rotki/ui-library 2.14.1 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/logos/RuiLogo.vue.js +90 -52
- package/dist/components/logos/RuiLogo.vue.js.map +1 -1
- package/dist/components/logos/use-logo-sources.d.ts +20 -1
- package/dist/components/logos/use-logo-sources.js +47 -2
- package/dist/components/logos/use-logo-sources.js.map +1 -1
- package/dist/style.css +3 -3
- package/dist/web-types.json +1 -1
- package/package.json +3 -6
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { defineComponent,
|
|
2
|
-
import {
|
|
1
|
+
import { defineComponent, ref, useTemplateRef, computed, watch, watchEffect, useSSRContext, onServerPrefetch, openBlock, createElementBlock, normalizeStyle, createElementVNode, normalizeClass, unref, createCommentVNode, toDisplayString } from "vue";
|
|
2
|
+
import { isClient, set, get } from "@vueuse/shared";
|
|
3
3
|
import fallback from "./logo.svg.js";
|
|
4
|
-
import { getLogoSources } from "./use-logo-sources.js";
|
|
4
|
+
import { getCachedLogoSources, getVerifiedLogoUrl, getLogoSources, removeVerifiedLogoUrl, setVerifiedLogoUrl } from "./use-logo-sources.js";
|
|
5
5
|
const _hoisted_1 = ["src"];
|
|
6
6
|
const _hoisted_2 = ["src"];
|
|
7
7
|
const _hoisted_3 = {
|
|
8
|
-
key:
|
|
8
|
+
key: 0,
|
|
9
9
|
class: "text-h4 text-rui-primary dark:text-white"
|
|
10
10
|
};
|
|
11
11
|
const appName = "rotki";
|
|
@@ -23,47 +23,87 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
23
23
|
src: {}
|
|
24
24
|
},
|
|
25
25
|
setup(__props) {
|
|
26
|
-
const
|
|
27
|
-
const error = ref(false);
|
|
28
|
-
const decoding = ref(!!__props.logo && !__props.src);
|
|
29
|
-
const pendingSources = ref(false);
|
|
30
|
-
const emptyLinks = {
|
|
26
|
+
const emptyLinks = () => ({
|
|
31
27
|
app: void 0,
|
|
32
28
|
website: void 0,
|
|
33
29
|
about: void 0,
|
|
34
30
|
emptyScreen: void 0
|
|
35
|
-
};
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
});
|
|
32
|
+
const error = ref(false);
|
|
33
|
+
const seasonalReady = ref(false);
|
|
34
|
+
const externalSources = ref(emptyLinks());
|
|
35
|
+
const customImageRef = useTemplateRef("customImageRef");
|
|
36
|
+
if (isClient && __props.logo && !__props.src) {
|
|
37
|
+
const cached = getCachedLogoSources(__props.branch);
|
|
38
|
+
if (cached)
|
|
39
|
+
set(externalSources, cached);
|
|
40
|
+
}
|
|
41
|
+
function buildExternalUrl(sources) {
|
|
42
|
+
if (!__props.logo || !sources[__props.logo])
|
|
43
43
|
return void 0;
|
|
44
|
-
}
|
|
45
44
|
const url = `https://raw.githubusercontent.com/rotki/data/${__props.branch}/assets/icons/${sources[__props.logo]}`;
|
|
46
45
|
if (__props.uniqueKey !== void 0)
|
|
47
46
|
return `${url}?key=${__props.uniqueKey}`;
|
|
48
47
|
return url;
|
|
48
|
+
}
|
|
49
|
+
const externalSource = computed(() => {
|
|
50
|
+
if (__props.src)
|
|
51
|
+
return __props.src;
|
|
52
|
+
return buildExternalUrl(get(externalSources));
|
|
49
53
|
});
|
|
54
|
+
const showCustom = computed(() => !!__props.src || !!get(externalSource) && get(seasonalReady) && !get(error));
|
|
55
|
+
if (isClient && __props.logo && !__props.src) {
|
|
56
|
+
const verifiedUrl = getVerifiedLogoUrl(__props.branch, String(__props.logo));
|
|
57
|
+
const currentUrl = get(externalSource);
|
|
58
|
+
if (verifiedUrl && currentUrl && verifiedUrl === currentUrl)
|
|
59
|
+
set(seasonalReady, true);
|
|
60
|
+
}
|
|
61
|
+
function preloadImage(url) {
|
|
62
|
+
if (!isClient)
|
|
63
|
+
return;
|
|
64
|
+
const existing = document.head.querySelectorAll('link[rel="preload"][as="image"]');
|
|
65
|
+
for (const link2 of existing) {
|
|
66
|
+
if (link2.href === url)
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const link = document.createElement("link");
|
|
70
|
+
link.rel = "preload";
|
|
71
|
+
link.as = "image";
|
|
72
|
+
link.href = url;
|
|
73
|
+
document.head.appendChild(link);
|
|
74
|
+
}
|
|
75
|
+
if (isClient && __props.logo && !__props.src) {
|
|
76
|
+
const url = get(externalSource);
|
|
77
|
+
if (url)
|
|
78
|
+
preloadImage(url);
|
|
79
|
+
}
|
|
50
80
|
async function fetchSources() {
|
|
51
81
|
if (!__props.logo || __props.src)
|
|
52
82
|
return;
|
|
53
|
-
set(pendingSources, true);
|
|
54
83
|
const links = await getLogoSources(__props.branch);
|
|
55
84
|
if (links)
|
|
56
85
|
set(externalSources, links);
|
|
57
|
-
set(pendingSources, false);
|
|
58
86
|
}
|
|
59
|
-
|
|
60
|
-
(
|
|
61
|
-
(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
function onImageError() {
|
|
88
|
+
set(error, true);
|
|
89
|
+
if (__props.logo)
|
|
90
|
+
removeVerifiedLogoUrl(__props.branch, String(__props.logo));
|
|
91
|
+
}
|
|
92
|
+
function onImageLoaded() {
|
|
93
|
+
const img = get(customImageRef);
|
|
94
|
+
if (!img)
|
|
95
|
+
return;
|
|
96
|
+
img.decode().then(() => {
|
|
97
|
+
set(seasonalReady, true);
|
|
98
|
+
const url = get(externalSource);
|
|
99
|
+
if (url && __props.logo)
|
|
100
|
+
setVerifiedLogoUrl(__props.branch, String(__props.logo), url);
|
|
101
|
+
}).catch(() => set(error, true));
|
|
102
|
+
}
|
|
103
|
+
watch(() => get(customImageRef)?.complete, (complete) => {
|
|
104
|
+
if (complete)
|
|
105
|
+
onImageLoaded();
|
|
106
|
+
});
|
|
67
107
|
watchEffect(fetchSources);
|
|
68
108
|
if (!isClient)
|
|
69
109
|
useSSRContext();
|
|
@@ -75,30 +115,28 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
75
115
|
class: "gap-x-4 flex items-center relative",
|
|
76
116
|
style: normalizeStyle({ height: `${__props.size}rem` })
|
|
77
117
|
}, [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
style: normalizeStyle({ width: `${__props.size}rem` })
|
|
101
|
-
}, null, 12, _hoisted_2)),
|
|
118
|
+
createElementVNode("div", {
|
|
119
|
+
class: "relative",
|
|
120
|
+
style: normalizeStyle({ width: `${__props.size}rem`, height: `${__props.size}rem` })
|
|
121
|
+
}, [
|
|
122
|
+
createElementVNode("img", {
|
|
123
|
+
src: unref(fallback),
|
|
124
|
+
alt: appName,
|
|
125
|
+
"data-image": "fallback",
|
|
126
|
+
class: normalizeClass(["absolute inset-0 h-full w-full transition ease-out duration-200", unref(showCustom) ? "opacity-0" : "opacity-100"])
|
|
127
|
+
}, null, 10, _hoisted_1),
|
|
128
|
+
unref(externalSource) && !unref(error) ? (openBlock(), createElementBlock("img", {
|
|
129
|
+
key: 0,
|
|
130
|
+
ref_key: "customImageRef",
|
|
131
|
+
ref: customImageRef,
|
|
132
|
+
src: unref(externalSource),
|
|
133
|
+
alt: appName,
|
|
134
|
+
"data-image": "custom",
|
|
135
|
+
class: normalizeClass(["absolute inset-0 h-full w-full transition ease-out duration-200", unref(showCustom) ? "opacity-100" : "opacity-0 pointer-events-none"]),
|
|
136
|
+
onError: _cache[0] || (_cache[0] = ($event) => onImageError()),
|
|
137
|
+
onLoad: _cache[1] || (_cache[1] = ($event) => onImageLoaded())
|
|
138
|
+
}, null, 42, _hoisted_2)) : createCommentVNode("", true)
|
|
139
|
+
], 4),
|
|
102
140
|
__props.text ? (openBlock(), createElementBlock("div", _hoisted_3, toDisplayString(appName))) : createCommentVNode("", true)
|
|
103
141
|
], 4);
|
|
104
142
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiLogo.vue.js","sources":["../../../src/components/logos/RuiLogo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { isClient } from '@vueuse/shared';\nimport { useSSRContext } from 'vue';\nimport fallback from '@/components/logos/logo.svg';\nimport { getLogoSources } from '@/components/logos/use-logo-sources';\n\nexport interface ExternalLinks {\n drawer?: string;\n app?: string;\n website?: string;\n about?: string;\n emptyScreen?: string;\n [key: string]: string | undefined;\n}\n\nexport interface Props {\n text?: boolean;\n branch?: 'develop' | 'main' | string;\n logo?: keyof ExternalLinks;\n size?: string | number; // in rems\n uniqueKey?: string | number;\n src?: string;\n}\n\ndefineOptions({\n name: 'RuiLogo',\n});\n\nconst { text = false, branch = 'develop', logo, size = 3, uniqueKey, src } = defineProps<Props>();\n\nconst
|
|
1
|
+
{"version":3,"file":"RuiLogo.vue.js","sources":["../../../src/components/logos/RuiLogo.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { isClient } from '@vueuse/shared';\nimport { useSSRContext } from 'vue';\nimport fallback from '@/components/logos/logo.svg';\nimport { getCachedLogoSources, getLogoSources, getVerifiedLogoUrl, removeVerifiedLogoUrl, setVerifiedLogoUrl } from '@/components/logos/use-logo-sources';\n\nexport interface ExternalLinks {\n drawer?: string;\n app?: string;\n website?: string;\n about?: string;\n emptyScreen?: string;\n [key: string]: string | undefined;\n}\n\nexport interface Props {\n text?: boolean;\n branch?: 'develop' | 'main' | string;\n logo?: keyof ExternalLinks;\n size?: string | number; // in rems\n uniqueKey?: string | number;\n src?: string;\n}\n\ndefineOptions({\n name: 'RuiLogo',\n});\n\nconst { text = false, branch = 'develop', logo, size = 3, uniqueKey, src } = defineProps<Props>();\n\nconst appName = 'rotki';\nconst emptyLinks: () => ExternalLinks = () => ({\n app: undefined,\n website: undefined,\n about: undefined,\n emptyScreen: undefined,\n});\n\nconst error = ref<boolean>(false);\nconst seasonalReady = ref<boolean>(false);\nconst externalSources = ref<ExternalLinks>(emptyLinks());\n\nconst customImageRef = useTemplateRef<HTMLImageElement>('customImageRef');\n\n// Synchronously hydrate from sessionStorage so the URL is available immediately on refresh\nif (isClient && logo && !src) {\n const cached = getCachedLogoSources(branch);\n if (cached)\n set(externalSources, cached);\n}\n\nfunction buildExternalUrl(sources: ExternalLinks): string | undefined {\n if (!logo || !sources[logo])\n return undefined;\n\n const url = `https://raw.githubusercontent.com/rotki/data/${branch}/assets/icons/${sources[logo]}`;\n\n if (uniqueKey !== undefined)\n return `${url}?key=${uniqueKey}`;\n\n return url;\n}\n\nconst externalSource = computed<string | undefined>(() => {\n if (src)\n return src;\n\n return buildExternalUrl(get(externalSources));\n});\n\nconst showCustom = computed<boolean>(() => !!src || (!!get(externalSource) && get(seasonalReady) && !get(error)));\n\n// If we have a previously verified URL that matches the current source, trust it immediately\nif (isClient && logo && !src) {\n const verifiedUrl = getVerifiedLogoUrl(branch, String(logo));\n const currentUrl = get(externalSource);\n if (verifiedUrl && currentUrl && verifiedUrl === currentUrl)\n set(seasonalReady, true);\n}\n\nfunction preloadImage(url: string): void {\n if (!isClient)\n return;\n\n const existing = document.head.querySelectorAll<HTMLLinkElement>('link[rel=\"preload\"][as=\"image\"]');\n for (const link of existing) {\n if (link.href === url)\n return;\n }\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'image';\n link.href = url;\n document.head.appendChild(link);\n}\n\n// Inject a preload hint for the seasonal image so the browser starts fetching early\nif (isClient && logo && !src) {\n const url = get(externalSource);\n if (url)\n preloadImage(url);\n}\n\nasync function fetchSources(): Promise<void> {\n if (!logo || src)\n return;\n\n const links = await getLogoSources(branch);\n\n if (links)\n set(externalSources, links);\n}\n\nfunction onImageError(): void {\n set(error, true);\n if (logo)\n removeVerifiedLogoUrl(branch, String(logo));\n}\n\nfunction onImageLoaded(): void {\n const img = get(customImageRef);\n if (!img)\n return;\n\n img.decode()\n .then(() => {\n set(seasonalReady, true);\n // Cache the verified URL so subsequent page loads can trust it immediately\n const url = get(externalSource);\n if (url && logo)\n setVerifiedLogoUrl(branch, String(logo), url);\n })\n .catch(() => set(error, true));\n}\n\nwatch(() => get(customImageRef)?.complete, (complete) => {\n if (complete)\n onImageLoaded();\n});\n\nwatchEffect(fetchSources);\n\nif (!isClient)\n useSSRContext();\n\nonServerPrefetch(async () => {\n await fetchSources();\n});\n</script>\n\n<template>\n <div\n class=\"gap-x-4 flex items-center relative\"\n :style=\"{ height: `${size}rem` }\"\n >\n <div\n class=\"relative\"\n :style=\"{ width: `${size}rem`, height: `${size}rem` }\"\n >\n <!-- Fallback: always in DOM, fades out when custom image is ready -->\n <img\n :src=\"fallback\"\n :alt=\"appName\"\n data-image=\"fallback\"\n class=\"absolute inset-0 h-full w-full transition ease-out duration-200\"\n :class=\"showCustom ? 'opacity-0' : 'opacity-100'\"\n />\n\n <!-- Custom/seasonal image: fades in when decoded -->\n <img\n v-if=\"externalSource && !error\"\n ref=\"customImageRef\"\n :src=\"externalSource\"\n :alt=\"appName\"\n data-image=\"custom\"\n class=\"absolute inset-0 h-full w-full transition ease-out duration-200\"\n :class=\"showCustom ? 'opacity-100' : 'opacity-0 pointer-events-none'\"\n @error=\"onImageError()\"\n @load=\"onImageLoaded()\"\n />\n </div>\n\n <div\n v-if=\"text\"\n class=\"text-h4 text-rui-primary dark:text-white\"\n >\n {{ appName }}\n </div>\n </div>\n</template>\n"],"names":["link","_createElementBlock","_createElementVNode","_normalizeStyle","_unref","_normalizeClass","_toDisplayString"],"mappings":"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"}
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import type { ExternalLinks } from '../../components/logos/RuiLogo.vue.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns cached logo sources synchronously from sessionStorage.
|
|
4
|
+
* Returns undefined if nothing is cached or sessionStorage is unavailable.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getCachedLogoSources(branch: string): ExternalLinks | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Returns a previously verified (successfully decoded) seasonal logo URL
|
|
9
|
+
* for the given branch and logo type from sessionStorage.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getVerifiedLogoUrl(branch: string, logo: string): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Stores a verified seasonal logo URL in sessionStorage after successful decode.
|
|
14
|
+
*/
|
|
15
|
+
export declare function setVerifiedLogoUrl(branch: string, logo: string, url: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Removes a previously verified seasonal logo URL from sessionStorage.
|
|
18
|
+
* Called when an image fails to load so stale entries aren't trusted on next load.
|
|
19
|
+
*/
|
|
20
|
+
export declare function removeVerifiedLogoUrl(branch: string, logo: string): void;
|
|
2
21
|
/**
|
|
3
22
|
* Fetches external logo sources for a given branch, deduplicating concurrent requests.
|
|
4
23
|
* Uses a module-level cache so all RuiLogo instances share a single fetch per branch.
|
|
5
24
|
*/
|
|
6
25
|
export declare function getLogoSources(branch: string): Promise<ExternalLinks | undefined>;
|
|
7
26
|
/**
|
|
8
|
-
* Clears the module-level cache. Exposed for testing only.
|
|
27
|
+
* Clears the module-level cache and sessionStorage entries. Exposed for testing only.
|
|
9
28
|
*/
|
|
10
29
|
export declare function clearLogoSourcesCache(): void;
|
|
@@ -1,11 +1,50 @@
|
|
|
1
1
|
import { transformCase } from "../../utils/helpers.js";
|
|
2
2
|
const ASSET_MAPPINGS_URL = "https://raw.githubusercontent.com/rotki/data";
|
|
3
|
+
const SESSION_SOURCES_PREFIX = "rui-logo-sources";
|
|
4
|
+
const SESSION_VERIFIED_PREFIX = "rui-logo-verified";
|
|
3
5
|
const sourcesCache = /* @__PURE__ */ new Map();
|
|
4
6
|
function isExternalLinks(value) {
|
|
5
7
|
if (typeof value !== "object" || value === null)
|
|
6
8
|
return false;
|
|
7
9
|
return Object.values(value).every((v) => v === void 0 || typeof v === "string");
|
|
8
10
|
}
|
|
11
|
+
function writeSessionStorage(key, value) {
|
|
12
|
+
try {
|
|
13
|
+
sessionStorage.setItem(key, value);
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function readSessionStorage(key) {
|
|
18
|
+
try {
|
|
19
|
+
return sessionStorage.getItem(key) ?? void 0;
|
|
20
|
+
} catch {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function getCachedLogoSources(branch) {
|
|
25
|
+
const raw = readSessionStorage(`${SESSION_SOURCES_PREFIX}:${branch}`);
|
|
26
|
+
if (!raw)
|
|
27
|
+
return void 0;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
if (isExternalLinks(parsed))
|
|
31
|
+
return parsed;
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
function getVerifiedLogoUrl(branch, logo) {
|
|
37
|
+
return readSessionStorage(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`);
|
|
38
|
+
}
|
|
39
|
+
function setVerifiedLogoUrl(branch, logo, url) {
|
|
40
|
+
writeSessionStorage(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`, url);
|
|
41
|
+
}
|
|
42
|
+
function removeVerifiedLogoUrl(branch, logo) {
|
|
43
|
+
try {
|
|
44
|
+
sessionStorage.removeItem(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
9
48
|
async function fetchSourcesFromNetwork(branch) {
|
|
10
49
|
try {
|
|
11
50
|
const response = await fetch(
|
|
@@ -15,8 +54,10 @@ async function fetchSourcesFromNetwork(branch) {
|
|
|
15
54
|
return void 0;
|
|
16
55
|
const json = await response.json();
|
|
17
56
|
const links = transformCase(json?.logo, "camelCase");
|
|
18
|
-
if (isExternalLinks(links))
|
|
57
|
+
if (isExternalLinks(links)) {
|
|
58
|
+
writeSessionStorage(`${SESSION_SOURCES_PREFIX}:${branch}`, JSON.stringify(links));
|
|
19
59
|
return links;
|
|
60
|
+
}
|
|
20
61
|
return void 0;
|
|
21
62
|
} catch {
|
|
22
63
|
return void 0;
|
|
@@ -31,6 +72,10 @@ function getLogoSources(branch) {
|
|
|
31
72
|
return promise;
|
|
32
73
|
}
|
|
33
74
|
export {
|
|
34
|
-
|
|
75
|
+
getCachedLogoSources,
|
|
76
|
+
getLogoSources,
|
|
77
|
+
getVerifiedLogoUrl,
|
|
78
|
+
removeVerifiedLogoUrl,
|
|
79
|
+
setVerifiedLogoUrl
|
|
35
80
|
};
|
|
36
81
|
//# sourceMappingURL=use-logo-sources.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-logo-sources.js","sources":["../../../src/components/logos/use-logo-sources.ts"],"sourcesContent":["import type { ExternalLinks } from '@/components/logos/RuiLogo.vue';\nimport { transformCase } from '@/utils/helpers';\n\nconst ASSET_MAPPINGS_URL = 'https://raw.githubusercontent.com/rotki/data';\n\n// Module-level cache: deduplicates asset-mappings fetches across all RuiLogo instances.\n// Keyed by branch so different branches don't share stale data.\nconst sourcesCache = new Map<string, Promise<ExternalLinks | undefined>>();\n\nfunction isExternalLinks(value: unknown): value is ExternalLinks {\n if (typeof value !== 'object' || value === null)\n return false;\n\n return Object.values(value).every(v => v === undefined || typeof v === 'string');\n}\n\nasync function fetchSourcesFromNetwork(branch: string): Promise<ExternalLinks | undefined> {\n try {\n const response = await fetch(\n `${ASSET_MAPPINGS_URL}/${branch}/constants/asset-mappings.json`,\n );\n\n if (!response.ok)\n return undefined;\n\n const json = await response.json();\n const links = transformCase(json?.logo, 'camelCase');\n if (isExternalLinks(links))\n return links;\n\n return undefined;\n }\n catch {\n return undefined;\n }\n}\n\n/**\n * Fetches external logo sources for a given branch, deduplicating concurrent requests.\n * Uses a module-level cache so all RuiLogo instances share a single fetch per branch.\n */\nexport function getLogoSources(branch: string): Promise<ExternalLinks | undefined> {\n const cached = sourcesCache.get(branch);\n if (cached)\n return cached;\n\n const promise = fetchSourcesFromNetwork(branch);\n sourcesCache.set(branch, promise);\n return promise;\n}\n\n/**\n * Clears the module-level cache. Exposed for testing only.\n */\nexport function clearLogoSourcesCache(): void {\n sourcesCache.clear();\n}\n"],"names":[],"mappings":";AAGA,MAAM,qBAAqB;
|
|
1
|
+
{"version":3,"file":"use-logo-sources.js","sources":["../../../src/components/logos/use-logo-sources.ts"],"sourcesContent":["import type { ExternalLinks } from '@/components/logos/RuiLogo.vue';\nimport { transformCase } from '@/utils/helpers';\n\nconst ASSET_MAPPINGS_URL = 'https://raw.githubusercontent.com/rotki/data';\nconst SESSION_SOURCES_PREFIX = 'rui-logo-sources';\nconst SESSION_VERIFIED_PREFIX = 'rui-logo-verified';\n\n// Module-level cache: deduplicates asset-mappings fetches across all RuiLogo instances.\n// Keyed by branch so different branches don't share stale data.\nconst sourcesCache = new Map<string, Promise<ExternalLinks | undefined>>();\n\nfunction isExternalLinks(value: unknown): value is ExternalLinks {\n if (typeof value !== 'object' || value === null)\n return false;\n\n return Object.values(value).every(v => v === undefined || typeof v === 'string');\n}\n\nfunction writeSessionStorage(key: string, value: string): void {\n try {\n sessionStorage.setItem(key, value);\n }\n catch {\n // sessionStorage may be full or unavailable\n }\n}\n\nfunction readSessionStorage(key: string): string | undefined {\n try {\n return sessionStorage.getItem(key) ?? undefined;\n }\n catch {\n return undefined;\n }\n}\n\nfunction clearSessionStorage(prefix: string): void {\n try {\n const keysToRemove: string[] = [];\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i);\n if (key?.startsWith(prefix))\n keysToRemove.push(key);\n }\n keysToRemove.forEach(key => sessionStorage.removeItem(key));\n }\n catch {\n // sessionStorage may be unavailable\n }\n}\n\n/**\n * Returns cached logo sources synchronously from sessionStorage.\n * Returns undefined if nothing is cached or sessionStorage is unavailable.\n */\nexport function getCachedLogoSources(branch: string): ExternalLinks | undefined {\n const raw = readSessionStorage(`${SESSION_SOURCES_PREFIX}:${branch}`);\n if (!raw)\n return undefined;\n\n try {\n const parsed: unknown = JSON.parse(raw);\n if (isExternalLinks(parsed))\n return parsed;\n }\n catch {\n // Invalid JSON, ignore\n }\n return undefined;\n}\n\n/**\n * Returns a previously verified (successfully decoded) seasonal logo URL\n * for the given branch and logo type from sessionStorage.\n */\nexport function getVerifiedLogoUrl(branch: string, logo: string): string | undefined {\n return readSessionStorage(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`);\n}\n\n/**\n * Stores a verified seasonal logo URL in sessionStorage after successful decode.\n */\nexport function setVerifiedLogoUrl(branch: string, logo: string, url: string): void {\n writeSessionStorage(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`, url);\n}\n\n/**\n * Removes a previously verified seasonal logo URL from sessionStorage.\n * Called when an image fails to load so stale entries aren't trusted on next load.\n */\nexport function removeVerifiedLogoUrl(branch: string, logo: string): void {\n try {\n sessionStorage.removeItem(`${SESSION_VERIFIED_PREFIX}:${branch}:${logo}`);\n }\n catch {\n // sessionStorage may be unavailable\n }\n}\n\nasync function fetchSourcesFromNetwork(branch: string): Promise<ExternalLinks | undefined> {\n try {\n const response = await fetch(\n `${ASSET_MAPPINGS_URL}/${branch}/constants/asset-mappings.json`,\n );\n\n if (!response.ok)\n return undefined;\n\n const json = await response.json();\n const links = transformCase(json?.logo, 'camelCase');\n if (isExternalLinks(links)) {\n writeSessionStorage(`${SESSION_SOURCES_PREFIX}:${branch}`, JSON.stringify(links));\n return links;\n }\n\n return undefined;\n }\n catch {\n return undefined;\n }\n}\n\n/**\n * Fetches external logo sources for a given branch, deduplicating concurrent requests.\n * Uses a module-level cache so all RuiLogo instances share a single fetch per branch.\n */\nexport function getLogoSources(branch: string): Promise<ExternalLinks | undefined> {\n const cached = sourcesCache.get(branch);\n if (cached)\n return cached;\n\n const promise = fetchSourcesFromNetwork(branch);\n sourcesCache.set(branch, promise);\n return promise;\n}\n\n/**\n * Clears the module-level cache and sessionStorage entries. Exposed for testing only.\n */\nexport function clearLogoSourcesCache(): void {\n sourcesCache.clear();\n clearSessionStorage(SESSION_SOURCES_PREFIX);\n clearSessionStorage(SESSION_VERIFIED_PREFIX);\n}\n"],"names":[],"mappings":";AAGA,MAAM,qBAAqB;AAC3B,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAIhC,MAAM,mCAAmB,IAAA;AAEzB,SAAS,gBAAgB,OAAwC;AAC/D,MAAI,OAAO,UAAU,YAAY,UAAU;AACzC,WAAO;AAET,SAAO,OAAO,OAAO,KAAK,EAAE,MAAM,OAAK,MAAM,UAAa,OAAO,MAAM,QAAQ;AACjF;AAEA,SAAS,oBAAoB,KAAa,OAAqB;AAC7D,MAAI;AACF,mBAAe,QAAQ,KAAK,KAAK;AAAA,EACnC,QACM;AAAA,EAEN;AACF;AAEA,SAAS,mBAAmB,KAAiC;AAC3D,MAAI;AACF,WAAO,eAAe,QAAQ,GAAG,KAAK;AAAA,EACxC,QACM;AACJ,WAAO;AAAA,EACT;AACF;AAqBO,SAAS,qBAAqB,QAA2C;AAC9E,QAAM,MAAM,mBAAmB,GAAG,sBAAsB,IAAI,MAAM,EAAE;AACpE,MAAI,CAAC;AACH,WAAO;AAET,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,gBAAgB,MAAM;AACxB,aAAO;AAAA,EACX,QACM;AAAA,EAEN;AACA,SAAO;AACT;AAMO,SAAS,mBAAmB,QAAgB,MAAkC;AACnF,SAAO,mBAAmB,GAAG,uBAAuB,IAAI,MAAM,IAAI,IAAI,EAAE;AAC1E;AAKO,SAAS,mBAAmB,QAAgB,MAAc,KAAmB;AAClF,sBAAoB,GAAG,uBAAuB,IAAI,MAAM,IAAI,IAAI,IAAI,GAAG;AACzE;AAMO,SAAS,sBAAsB,QAAgB,MAAoB;AACxE,MAAI;AACF,mBAAe,WAAW,GAAG,uBAAuB,IAAI,MAAM,IAAI,IAAI,EAAE;AAAA,EAC1E,QACM;AAAA,EAEN;AACF;AAEA,eAAe,wBAAwB,QAAoD;AACzF,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,kBAAkB,IAAI,MAAM;AAAA,IAAA;AAGjC,QAAI,CAAC,SAAS;AACZ,aAAO;AAET,UAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,UAAM,QAAQ,cAAc,MAAM,MAAM,WAAW;AACnD,QAAI,gBAAgB,KAAK,GAAG;AAC1B,0BAAoB,GAAG,sBAAsB,IAAI,MAAM,IAAI,KAAK,UAAU,KAAK,CAAC;AAChF,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,QACM;AACJ,WAAO;AAAA,EACT;AACF;AAMO,SAAS,eAAe,QAAoD;AACjF,QAAM,SAAS,aAAa,IAAI,MAAM;AACtC,MAAI;AACF,WAAO;AAET,QAAM,UAAU,wBAAwB,MAAM;AAC9C,eAAa,IAAI,QAAQ,OAAO;AAChC,SAAO;AACT;"}
|
package/dist/style.css
CHANGED
|
@@ -850,6 +850,9 @@ html[data-theme="dark"], html.dark {
|
|
|
850
850
|
.sticky {
|
|
851
851
|
position: sticky;
|
|
852
852
|
}
|
|
853
|
+
.inset-0 {
|
|
854
|
+
inset: 0px;
|
|
855
|
+
}
|
|
853
856
|
.inset-y-0 {
|
|
854
857
|
top: 0px;
|
|
855
858
|
bottom: 0px;
|
|
@@ -2410,9 +2413,6 @@ html[data-theme="dark"], html.dark {
|
|
|
2410
2413
|
.delay-0 {
|
|
2411
2414
|
transition-delay: 0s;
|
|
2412
2415
|
}
|
|
2413
|
-
.delay-100 {
|
|
2414
|
-
transition-delay: 100ms;
|
|
2415
|
-
}
|
|
2416
2416
|
.delay-200 {
|
|
2417
2417
|
transition-delay: 200ms;
|
|
2418
2418
|
}
|
package/dist/web-types.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rotki/ui-library",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "A vue design system and component library for rotki",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -77,14 +77,14 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@vueuse/core": ">13.0.0",
|
|
81
|
-
"@vueuse/shared": ">13.0.0",
|
|
82
80
|
"dayjs": ">=1.11.13 <12",
|
|
83
81
|
"vue": ">=3.5.0"
|
|
84
82
|
},
|
|
85
83
|
"web-types": "dist/web-types.json",
|
|
86
84
|
"dependencies": {
|
|
87
85
|
"@popperjs/core": "2.11.8",
|
|
86
|
+
"@vueuse/core": "14.2.1",
|
|
87
|
+
"@vueuse/shared": "14.2.1",
|
|
88
88
|
"scule": "1.3.0",
|
|
89
89
|
"tinycolor2": "1.6.0"
|
|
90
90
|
},
|
|
@@ -107,9 +107,6 @@
|
|
|
107
107
|
"@vitest/ui": "4.1.0",
|
|
108
108
|
"@vue/test-utils": "2.4.6",
|
|
109
109
|
"@vue/tsconfig": "0.9.0",
|
|
110
|
-
"@vueuse/core": "14.2.1",
|
|
111
|
-
"@vueuse/math": "14.2.1",
|
|
112
|
-
"@vueuse/shared": "14.2.1",
|
|
113
110
|
"autoprefixer": "10.4.27",
|
|
114
111
|
"cac": "7.0.0",
|
|
115
112
|
"consola": "3.4.2",
|