From fe434cb6343a6a00da1467c4b9094cb507a1fbef Mon Sep 17 00:00:00 2001 From: "Tom (plebeius.eth)" Date: Tue, 9 Jul 2024 14:59:18 +0200 Subject: [PATCH] feat(board header): add yellow offline icon for loading online status, red icon for offline status --- public/assets/icons/offline-red.png | Bin 1205 -> 1226 bytes .../icons/{offline.png => offline-yellow.png} | Bin 1270 -> 1217 bytes .../board-header/board-header.module.css | 5 -- src/components/board-header/board-header.tsx | 5 +- src/components/post-form/post-form.tsx | 4 +- src/hooks/use-is-subplebbit-offline.ts | 34 +++++++++--- src/index.css | 8 +++ src/stores/use-subplebbit-offline-store.ts | 51 ++++++++++++++++++ src/views/home/boards-box/boards-box.tsx | 5 +- src/views/home/home.module.css | 5 -- 10 files changed, 93 insertions(+), 24 deletions(-) rename public/assets/icons/{offline.png => offline-yellow.png} (71%) create mode 100644 src/stores/use-subplebbit-offline-store.ts diff --git a/public/assets/icons/offline-red.png b/public/assets/icons/offline-red.png index a02271059af19014cbc76b2eb05623f4e86aae7a..5759f7d6ca9c6c39beb4aa70840c2357b0583f42 100644 GIT binary patch delta 317 zcmV-D0mA;Z3CanumjizuNkl7Z zx~_E>3PEH+GKo~Swe!ALQ3r7xL(r=OkO^QZVoKmWaTJ2eFaYYh&Noe?-S^uUf4X1m zzN#vZNg|40S6`)s>lnYcMl#1f)icT!o2&i!j2% z9bBHuYlZbp6Dx#PlAaeGbbzLi_5&K4@GzH5c@9`NE+^nif)^!yL zfm^obdCu5GLI@`$OaSDj+5fhWwry>_*!TGv$I*hL5JWPPNu+=B)p=)rSFs9;qJW@U z1t1eZE8YV0#8C(;aR9V!o6hrWP1L?oE$=5W41;1)h@_;6=mV|n7Lvgpuyj@u1%N(U z`PPF(-3KTW_uy#3m+m<+25?!Hc$pxC+%!!u{y|DCP^IfSJC%5q?w87rkC>lUv++3h ucZ8R>Kp@C+cncuR`}M7W2mTfRV&Mi`ccvysY@;Ip0000Zt~5Zpw(f##k=L&F1-Cr~0H@dR|SsPcw9 zASp;gdEcNrA)R7W?7a>h|fk|-c$Mc z`~t*Y4y8hkF%K?h6(BLsa~Xy~PH)D=Ns=Ud))!Dn1wgKCF{_ zNJKJ;RQ6|QeNs^eS(ZW2ssoS-;3zU>;4apQ1KI8C9iVX>ZQu8rzO||S{+Pc++qM>) zLL_fOL=GG!lmV^N$tnOvca*;}po3pe*o1&Pf^VI~jRO<`EQ&&!M&$K*4iZ0+Z#^i0 zMl-QF;|x@xed;V!37r_j&mx4~)%^^1fTi#lYWz$6o)NBKbe@_iLCr@10000I*TzllK&JRjLL1B3X z1>px+8FGb{EwSJqXuR5j#sU**t~Rn(})SM&y6GE(&r0ghYVJaU2UE zNs>RH4}!$9EG3#kEHVZeL?}B8zcV}Pt%}q=&tpL)4wwu8M#(pFpx#gij>)=-gh5Ty zsH&=zvskOUrwcjp5AsVMUgymHn00000NkvXX Hu0mjf*kY=g diff --git a/src/components/board-header/board-header.module.css b/src/components/board-header/board-header.module.css index cc553a7f..dc0414d5 100644 --- a/src/components/board-header/board-header.module.css +++ b/src/components/board-header/board-header.module.css @@ -29,7 +29,6 @@ } .offlineIcon { - background-image: url('/public/assets/icons/offline.png'); display: inline-block; margin-left: 10px; width: 16px; @@ -39,10 +38,6 @@ image-rendering: pixelated; } -.redOfflineIcon { - background-image: url('/public/assets/icons/offline-red.png') !important; -} - @media (max-width: 640px) { .boardTitle { padding-top: 40px; diff --git a/src/components/board-header/board-header.tsx b/src/components/board-header/board-header.tsx index e891ac77..dbcc7bd5 100644 --- a/src/components/board-header/board-header.tsx +++ b/src/components/board-header/board-header.tsx @@ -34,8 +34,7 @@ const BoardHeader = () => { const title = isInAllView ? multisubMetadata?.title || 'all' : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title; const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${address}`; - const { isOffline, isDefinitelyOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit); - const iconClass = `${styles.offlineIcon} ${isDefinitelyOffline ? styles.redOfflineIcon : ''}`; + const { isOffline, isOnlineStatusLoading, offlineIconClass, offlineTitle } = useIsSubplebbitOffline(subplebbit); return (
@@ -46,7 +45,7 @@ const BoardHeader = () => { )}
{title || `p/${shortAddress || subplebbitAddress}`} - {isOffline && } + {(isOffline || isOnlineStatusLoading) && }
{subtitle}

diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index 287cbbf8..eb63ce62 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -277,7 +277,7 @@ const PostForm = () => { return ( <>
- {!(isInAllView || isInSubscriptionsView) && showForm && isOffline &&
{offlineTitle}
} + {!(isInAllView || isInSubscriptionsView) && showForm && (isOffline || isOffline) &&
{offlineTitle}
} {isThreadClosed ? (
{t('thread_closed')} @@ -297,7 +297,7 @@ const PostForm = () => { )}
- {!(isInAllView || isInSubscriptionsView) && showForm && isOffline &&
{offlineTitle}
} + {!(isInAllView || isInSubscriptionsView) && showForm && (isOffline || isOffline) &&
{offlineTitle}
} {isThreadClosed ? (
{t('thread_closed')} diff --git a/src/hooks/use-is-subplebbit-offline.ts b/src/hooks/use-is-subplebbit-offline.ts index 33875c20..91d914d7 100644 --- a/src/hooks/use-is-subplebbit-offline.ts +++ b/src/hooks/use-is-subplebbit-offline.ts @@ -1,19 +1,41 @@ import { useTranslation } from 'react-i18next'; +import { useEffect } from 'react'; import { Subplebbit } from '@plebbit/plebbit-react-hooks'; import { getFormattedTimeAgo } from '../lib/utils/time-utils'; +import useSubplebbitOfflineStore from '../stores/use-subplebbit-offline-store'; const useIsSubplebbitOffline = (subplebbit: Subplebbit) => { const { t } = useTranslation(); - const { updatedAt, updatingState } = subplebbit || {}; - const isProbablyOffline = !updatedAt || (updatedAt && updatedAt < Date.now() / 1000 - 60 * 60); - const isDefinitelyOffline = updatingState === 'failed'; - const isOffline = isProbablyOffline || isDefinitelyOffline; + const { address, state, updatedAt, updatingState } = subplebbit || {}; + const { subplebbitOfflineState, setSubplebbitOfflineState, initializesubplebbitOfflineState } = useSubplebbitOfflineStore(); - const offlineTitle = updatedAt + useEffect(() => { + if (address && !subplebbitOfflineState[address]) { + initializesubplebbitOfflineState(address); + } + }, [address, subplebbitOfflineState, initializesubplebbitOfflineState]); + + useEffect(() => { + if (address) { + setSubplebbitOfflineState(address, { state, updatedAt, updatingState }); + } + }, [address, state, updatedAt, updatingState, setSubplebbitOfflineState]); + + const subplebbitData = subplebbitOfflineState[address] || { initialLoad: true }; + + const isLoading = (updatingState === 'resolving-address' && state !== 'stopped') || (subplebbitData.initialLoad && updatingState === 'fetching-ipns' && !updatedAt); + const isOffline = + !isLoading && ((updatedAt && updatedAt < Date.now() / 1000 - 60 * 60) || updatingState === 'failed' || (updatingState === 'fetching-ipns' && !updatedAt)); + + const offlineIconClass = isLoading ? 'yellowOfflineIcon' : isOffline ? 'redOfflineIcon' : ''; + + const offlineTitle = isLoading + ? t('loading') + : updatedAt ? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } }) : t('subplebbit_offline_info'); - return { isOffline, isDefinitelyOffline, offlineTitle }; + return { isOffline, isOnlineStatusLoading: isLoading, offlineIconClass, offlineTitle }; }; export default useIsSubplebbitOffline; diff --git a/src/index.css b/src/index.css index ae102085..918e52c5 100644 --- a/src/index.css +++ b/src/index.css @@ -38,6 +38,14 @@ hr { color-scheme: dark; } +.yellowOfflineIcon { + background-image: url('/public/assets/icons/offline-yellow.png'); +} + +.redOfflineIcon { + background-image: url('/public/assets/icons/offline-red.png'); +} + @media (max-width: 640px) { .button { font-size: var(--button-font-size-mobile); diff --git a/src/stores/use-subplebbit-offline-store.ts b/src/stores/use-subplebbit-offline-store.ts new file mode 100644 index 00000000..4fcb8b0d --- /dev/null +++ b/src/stores/use-subplebbit-offline-store.ts @@ -0,0 +1,51 @@ +import { create } from 'zustand'; + +interface SubplebbitOfflineState { + state?: string; + updatedAt?: number; + updatingState?: string; + initialLoad: boolean; +} + +interface SubplebbitOfflineStore { + subplebbitOfflineState: Record; + setSubplebbitOfflineState: (address: string, state: Partial) => void; + initializesubplebbitOfflineState: (address: string) => void; +} + +const useSubplebbitOfflineStore = create((set) => ({ + subplebbitOfflineState: {}, + setSubplebbitOfflineState: (address, newState) => + set((state) => ({ + subplebbitOfflineState: { + ...state.subplebbitOfflineState, + [address]: { + ...state.subplebbitOfflineState[address], + ...newState, + }, + }, + })), + initializesubplebbitOfflineState: (address) => { + set((state) => ({ + subplebbitOfflineState: { + ...state.subplebbitOfflineState, + [address]: { + initialLoad: true, + }, + }, + })); + setTimeout(() => { + set((state) => ({ + subplebbitOfflineState: { + ...state.subplebbitOfflineState, + [address]: { + ...state.subplebbitOfflineState[address], + initialLoad: false, + }, + }, + })); + }, 7000); + }, +})); + +export default useSubplebbitOfflineStore; diff --git a/src/views/home/boards-box/boards-box.tsx b/src/views/home/boards-box/boards-box.tsx index 6f9cda02..81902c00 100644 --- a/src/views/home/boards-box/boards-box.tsx +++ b/src/views/home/boards-box/boards-box.tsx @@ -18,12 +18,11 @@ const Board = ({ subplebbit, subplebbits }: { subplebbit: Subplebbit; subplebbit const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`; const subplebbitData = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address); - const { isDefinitelyOffline, isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbitData); - const iconClass = `${styles.offlineIcon} ${isDefinitelyOffline ? styles.redOfflineIcon : ''}`; + const { isOffline, isOnlineStatusLoading, offlineIconClass, offlineTitle } = useIsSubplebbitOffline(subplebbitData); return (
- {isOffline && } + {(isOffline || isOnlineStatusLoading) && } {title || address} {nsfwTag && ({t(nsfwTag)})}
diff --git a/src/views/home/home.module.css b/src/views/home/home.module.css index 9e28a197..d3d71f61 100644 --- a/src/views/home/home.module.css +++ b/src/views/home/home.module.css @@ -176,7 +176,6 @@ } .offlineIcon { - background-image: url('/public/assets/icons/offline.png'); display: inline-block; vertical-align: text-top; margin-right: 2px; @@ -187,10 +186,6 @@ image-rendering: pixelated; } -.redOfflineIcon { - background-image: url('/public/assets/icons/offline-red.png') !important; -} - .boardsBoxContent { font-size: 93%; padding: 0.5em;