From 040f67e83cf541d08047741c66ea98d397156479 Mon Sep 17 00:00:00 2001 From: FlaminSarge Date: Mon, 18 May 2026 03:36:51 -0700 Subject: [PATCH] Refresh button: if manual refresh returns the same data as before, show flashing alternate icon for a short duration to indicate user has refreshed too soon This replaces the 1-second enforced fake spin on the refresh icon TODO find good indicator to persist for auto refresh manual refresh can be transient since it was user-initiated, auto refresh needs to be non-missable --- src/app/shell/RefreshButton.m.scss | 8 +++++ src/app/shell/RefreshButton.m.scss.d.ts | 1 + src/app/shell/RefreshButton.tsx | 41 +++++++++++++++--------- src/app/shell/icons/AppIcon.tsx | 11 ++++++- src/app/shell/icons/Library.js | 1 + src/app/shell/icons/font-awesome.scss | 3 ++ src/data/webfonts/fa-solid-900.ttf | Bin 11064 -> 11196 bytes src/data/webfonts/fa-solid-900.woff2 | Bin 4636 -> 4664 bytes 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/app/shell/RefreshButton.m.scss b/src/app/shell/RefreshButton.m.scss index 7ab74fa840..1c1ca33ff8 100644 --- a/src/app/shell/RefreshButton.m.scss +++ b/src/app/shell/RefreshButton.m.scss @@ -1,4 +1,5 @@ @use 'sass:color'; +@use 'sass:math'; @use '../variables' as *; @use '../dim-ui/tooltip-mixins' as *; @@ -53,3 +54,10 @@ margin-bottom: 1em; } } + +.fixedIcon { + display: inline-block; + width: math.div(20em, 16); // $fa-fw-width from AppIcon + text-align: center; + line-height: 1; +} diff --git a/src/app/shell/RefreshButton.m.scss.d.ts b/src/app/shell/RefreshButton.m.scss.d.ts index 4182555767..adb6944c67 100644 --- a/src/app/shell/RefreshButton.m.scss.d.ts +++ b/src/app/shell/RefreshButton.m.scss.d.ts @@ -3,6 +3,7 @@ interface CssExports { 'errorDetails': string; 'errorTooltip': string; + 'fixedIcon': string; 'outOfDate': string; 'refreshButton': string; 'userIsPlaying': string; diff --git a/src/app/shell/RefreshButton.tsx b/src/app/shell/RefreshButton.tsx index beae5f19c0..786b8d7eab 100644 --- a/src/app/shell/RefreshButton.tsx +++ b/src/app/shell/RefreshButton.tsx @@ -16,13 +16,13 @@ import { useSelector } from 'react-redux'; import { useSubscription } from 'use-subscription'; import ErrorPanel from './ErrorPanel'; import * as styles from './RefreshButton.m.scss'; -import { AppIcon, faClock, faExclamationTriangle, refreshIcon } from './icons'; +import { AppIcon, faClock, faExclamationTriangle, faHourglass, refreshIcon } from './icons'; import { loadingTracker } from './loading-tracker'; import { refresh } from './refresh-events'; /** We consider the profile stale if it's out of date with respect to the game data by this much */ const STALE_PROFILE_THRESHOLD = 90_000; -const MIN_SPIN = 1000; // 1 second +const WAIT_TIME = 1_000; export default function RefreshButton({ className }: { className?: string }) { const [disabled, setDisabled] = useState(false); @@ -34,24 +34,33 @@ export default function RefreshButton({ className }: { className?: string }) { ); const active = useSubscription(loadingTracker.active$); - // Always show the spinner for at least MIN_SPIN milliseconds - const [spin, setSpin] = useState(active ? Date.now() : 0); + // Track the minted date to detect if a refresh brought new data + const [lastMintedDate, setLastMintedDate] = useState(new Date(-1)); + const [showHourglass, setShowHourglass] = useState(false); + const [wasRecentlyActive, setWasRecentlyActive] = useState(false); + + const profileMintedDate = useSelector(profileMintedSelector); + useEffect(() => { - if (active && spin === 0) { - setSpin(Date.now()); - } else if (!active && spin !== 0) { - const elapsed = Date.now() - spin; - const remainingTime = Math.max(0, MIN_SPIN - elapsed); - if (remainingTime > 0) { + if (active && !wasRecentlyActive) { + // Refresh just started + setWasRecentlyActive(true); + } else if (!active && wasRecentlyActive) { + // Refresh just completed - check if we got new data + if (profileMintedDate.getTime() === lastMintedDate.getTime()) { + // No new data from this refresh - show hourglass + setShowHourglass(true); const timer = window.setTimeout(() => { - setSpin(0); - }, remainingTime); + setShowHourglass(false); + }, WAIT_TIME); return () => window.clearTimeout(timer); } else { - setSpin(0); + // New data received - update our tracking + setLastMintedDate(profileMintedDate); } + setWasRecentlyActive(false); } - }, [active, spin]); + }, [active, profileMintedDate, lastMintedDate, wasRecentlyActive]); useEventBusListener(isDragging$, handleChanges); @@ -80,7 +89,9 @@ export default function RefreshButton({ className }: { className?: string }) { title={t('Header.Refresh') + (autoRefresh ? `\n${t('Header.AutoRefresh')}` : '')} aria-keyshortcuts="R" > - + {(showHourglass && ( + + )) || } {autoRefresh &&
} {(profileError || showOutOfDateWarning) && (
diff --git a/src/app/shell/icons/AppIcon.tsx b/src/app/shell/icons/AppIcon.tsx index ca6d04748d..e7371b0f4e 100644 --- a/src/app/shell/icons/AppIcon.tsx +++ b/src/app/shell/icons/AppIcon.tsx @@ -10,17 +10,25 @@ function AppIcon({ title, spinning, ariaHidden, + fading, }: { icon: string | IconDefinition; className?: string; title?: string; spinning?: boolean; ariaHidden?: boolean; + fading?: boolean; }) { if (typeof icon === 'string') { return ( @@ -33,6 +41,7 @@ function AppIcon({ icon={icon} title={title} spin={spinning} + fade={fading} /> ); } diff --git a/src/app/shell/icons/Library.js b/src/app/shell/icons/Library.js index 39df537991..ef1b64d94a 100644 --- a/src/app/shell/icons/Library.js +++ b/src/app/shell/icons/Library.js @@ -96,3 +96,4 @@ export const lessThanIcon = 'fas fa-less-than-equal'; export const equalsIcon = 'fas fa-equals'; export const stackIcon = 'fas fa-layer-group'; export const slashIcon = 'fas fa-slash'; +export const faHourglass = 'fas fa-hourglass'; diff --git a/src/app/shell/icons/font-awesome.scss b/src/app/shell/icons/font-awesome.scss index 73ceaf0f1d..cc79a5dce3 100644 --- a/src/app/shell/icons/font-awesome.scss +++ b/src/app/shell/icons/font-awesome.scss @@ -117,6 +117,9 @@ .fa-th:before { content: fa-content($fa-var-th); } +.fa-hourglass:before { + content: fa-content($fa-var-hourglass); +} .fa-list:before { content: fa-content($fa-var-list); } diff --git a/src/data/webfonts/fa-solid-900.ttf b/src/data/webfonts/fa-solid-900.ttf index 9d62a092f165b0203ee59f01de05aeccb63dc1ea..d3878813872a2d3852ec043c19bf18a4098a29fc 100644 GIT binary patch delta 479 zcmdlHwkN!vfsuiMfrp`iftkU<-Nn_7L5RT@C~^ge#r%WyjhtIT$`}|JJAnLzkDEsdbg=861lQ=a||Op zqvAt86~1h~?R*dTx%h+lZwRmpxC%50>=)z^3=}*kBr9Y&xrONi6Jx{XBIdh{+&_RO zRWUd)urgRpj$(~w44J%zwHiq3u~o{y;Pv3O=6S@^#AC#*$1TAv%+1gBh3gjA9xgvl za|XuA7uj+qKVakEY|HK?T@Q4}{{xH-j0YGrfi4nZW0zwTH4>8rvXm8-)NC0|%#2OU z%=H*ep&UhsBx8n)si_MWA0OAh4ImQ6V+;WD7#p0kg@m%5`M3l?azFx|4-7`2y?+>g KZ0=K9#Q^~68E#Mj delta 338 zcmdlJz9X!jfsuiMfrp`iftkU<-Nn_7L5RTzC~^ge#r%WyjhtIT$`}|JJAnLzsky~O}(G|h)3TRjhP|zSJKRGej)Jq1)egRZ4D>t#CfMFeD zERg*Mh!ye@b5mvRnQ1UEFmIW-L!aB5fe9!K@`2C9PqDl{ASR=Co61tvth1YA7}*&W zU-0SkRr4+3JI61^KZpOc0H=VPK(oLZK~cd;f)54%GB8YTVfwJSg84Qh_XnUc6$}mx ztPB>D<5;67?`Ex&|P%^N^>J$BTbx^nn~&==*G;b7T)v!U42+X6vQ=#M NVE2~Z%&okU0|4g%RrLS> diff --git a/src/data/webfonts/fa-solid-900.woff2 b/src/data/webfonts/fa-solid-900.woff2 index f1cf9d66aca3c242ee7e24b778cc602f29f94a3d..0b1e208a9aad0db96a541e118b783b153ff01243 100644 GIT binary patch literal 4664 zcmV-8636X#Pew8T0RR9101`L=3;+NC04xvy01@Z_0RR9100000000000000000000 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 literal 4636 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