}
+ {!(isInAllView || isInSubscriptionsView) && showForm && (isOffline || isOffline) &&
{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;