feat: use red offline icon for subplebbits that are most likely offline (ipns record fails to update)

This commit is contained in:
Tom (plebeius.eth)
2024-07-07 20:02:58 +02:00
parent a2347769e2
commit 9f7544936e
9 changed files with 46 additions and 30 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -39,6 +39,10 @@
image-rendering: pixelated; image-rendering: pixelated;
} }
.redOfflineIcon {
background-image: url('/public/assets/icons/offline-red.png') !important;
}
@media (max-width: 640px) { @media (max-width: 640px) {
.boardTitle { .boardTitle {
padding-top: 40px; padding-top: 40px;
+5 -12
View File
@@ -1,12 +1,11 @@
import { useState } from 'react'; import { useState } from 'react';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAccountComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { useAccountComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import styles from './board-header.module.css'; import styles from './board-header.module.css';
import { useMultisubMetadata } from '../../hooks/use-default-subplebbits'; import { useMultisubMetadata } from '../../hooks/use-default-subplebbits';
import useIsMobile from '../../hooks/use-is-mobile'; import useIsMobile from '../../hooks/use-is-mobile';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
const totalBanners = 60; const totalBanners = 60;
@@ -20,10 +19,8 @@ const ImageBanner = () => {
}; };
const BoardHeader = () => { const BoardHeader = () => {
const { t } = useTranslation();
const location = useLocation(); const location = useLocation();
const params = useParams(); const params = useParams();
const isInAllView = isAllView(location.pathname, params); const isInAllView = isAllView(location.pathname, params);
const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
@@ -31,18 +28,14 @@ const BoardHeader = () => {
const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress; const subplebbitAddress = params?.subplebbitAddress || accountComment?.subplebbitAddress;
const subplebbit = useSubplebbit({ subplebbitAddress }); const subplebbit = useSubplebbit({ subplebbitAddress });
const { address, shortAddress, updatedAt } = subplebbit || {}; const { address, shortAddress } = subplebbit || {};
const multisubMetadata = useMultisubMetadata(); const multisubMetadata = useMultisubMetadata();
const title = isInAllView ? multisubMetadata?.title || 'all' : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title; const title = isInAllView ? multisubMetadata?.title || 'all' : isInSubscriptionsView ? 'Subscriptions' : subplebbit?.title;
const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${address}`; const subtitle = isInAllView ? 'p/all' : isInSubscriptionsView ? 'p/subscriptions' : `p/${address}`;
const isBoardOffline = updatedAt && updatedAt < Date.now() / 1000 - 60 * 60; const { isOffline, isDefinitelyOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit);
const iconClass = `${styles.offlineIcon} ${isDefinitelyOffline ? styles.redOfflineIcon : ''}`;
const offlineMessage = updatedAt
? isBoardOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
return ( return (
<div className={styles.content}> <div className={styles.content}>
@@ -53,7 +46,7 @@ const BoardHeader = () => {
)} )}
<div className={styles.boardTitle}> <div className={styles.boardTitle}>
{title || `p/${shortAddress || subplebbitAddress}`} {title || `p/${shortAddress || subplebbitAddress}`}
{isBoardOffline && <span className={styles.offlineIcon} title={offlineMessage} />} {isOffline && <span className={`${styles.offlineIcon} ${iconClass}`} title={offlineTitle} />}
</div> </div>
<div className={styles.boardSubtitle}>{subtitle}</div> <div className={styles.boardSubtitle}>{subtitle}</div>
<hr /> <hr />
+4 -9
View File
@@ -12,7 +12,7 @@ import useReply from '../../hooks/use-reply';
import useChallengesStore from '../../stores/use-challenges-store'; import useChallengesStore from '../../stores/use-challenges-store';
import styles from './post-form.module.css'; import styles from './post-form.module.css';
import _ from 'lodash'; import _ from 'lodash';
import { getFormattedTimeAgo } from '../../lib/utils/time-utils'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
type SubmitState = { type SubmitState = {
subplebbitAddress: string | undefined; subplebbitAddress: string | undefined;
@@ -272,17 +272,12 @@ const PostForm = () => {
const [showForm, setShowForm] = useState(false); const [showForm, setShowForm] = useState(false);
const subplebbit = useSubplebbit({ subplebbitAddress: params?.subplebbitAddress }); const subplebbit = useSubplebbit({ subplebbitAddress: params?.subplebbitAddress });
const { updatedAt } = subplebbit || {}; const { isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbit);
const isBoardOffline = updatedAt && updatedAt < Date.now() / 1000 - 60 * 60;
const offlineMessage = updatedAt
? isBoardOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
return ( return (
<> <>
<div className={styles.postFormDesktop}> <div className={styles.postFormDesktop}>
{!(isInAllView || isInSubscriptionsView) && showForm && isBoardOffline && <div className={styles.offlineBoard}>{offlineMessage}</div>} {!(isInAllView || isInSubscriptionsView) && showForm && isOffline && <div className={styles.offlineBoard}>{offlineTitle}</div>}
{isThreadClosed ? ( {isThreadClosed ? (
<div className={styles.closed}> <div className={styles.closed}>
{t('thread_closed')} {t('thread_closed')}
@@ -302,7 +297,7 @@ const PostForm = () => {
)} )}
</div> </div>
<div className={styles.postFormMobile}> <div className={styles.postFormMobile}>
{!(isInAllView || isInSubscriptionsView) && showForm && isBoardOffline && <div className={styles.offlineBoard}>{offlineMessage}</div>} {!(isInAllView || isInSubscriptionsView) && showForm && isOffline && <div className={styles.offlineBoard}>{offlineTitle}</div>}
{isThreadClosed ? ( {isThreadClosed ? (
<div className={styles.closed}> <div className={styles.closed}>
{t('thread_closed')} {t('thread_closed')}
+19
View File
@@ -0,0 +1,19 @@
import { useTranslation } from 'react-i18next';
import { Subplebbit } from '@plebbit/plebbit-react-hooks';
import { getFormattedTimeAgo } from '../lib/utils/time-utils';
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 offlineTitle = updatedAt
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
return { isOffline, isDefinitelyOffline, offlineTitle };
};
export default useIsSubplebbitOffline;
+4
View File
@@ -30,6 +30,10 @@ hr {
text-transform: capitalize; text-transform: capitalize;
} }
.red {
color: red !important;
}
:root { :root {
color-scheme: dark; color-scheme: dark;
} }
+2 -2
View File
@@ -72,14 +72,14 @@ const Board = () => {
const loadingString = ( const loadingString = (
<div className={styles.stateString}> <div className={styles.stateString}>
{state === 'failed' ? ( {state === 'failed' ? (
state <span className='red'>{state}</span>
) : isInSubscriptionsView && subscriptions?.length === 0 ? ( ) : isInSubscriptionsView && subscriptions?.length === 0 ? (
t('not_subscribed_to_any_board') t('not_subscribed_to_any_board')
) : ( ) : (
<LoadingEllipsis string={loadingStateString} /> <LoadingEllipsis string={loadingStateString} />
)} )}
{error && ( {error && (
<div style={{ color: 'red' }}> <div className='red'>
<br /> <br />
{error.message} {error.message}
</div> </div>
+4 -7
View File
@@ -2,12 +2,12 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Subplebbit, useAccount, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks'; import { Subplebbit, useAccount, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js'; import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import { getFormattedTimeAgo } from '../../../lib/utils/time-utils';
import useHomeFiltersStore from '../../../stores/use-home-filters-store'; import useHomeFiltersStore from '../../../stores/use-home-filters-store';
import { useMultisubMetadata } from '../../../hooks/use-default-subplebbits'; import { useMultisubMetadata } from '../../../hooks/use-default-subplebbits';
import styles from '../home.module.css'; import styles from '../home.module.css';
import { nsfwTags } from '../home'; import { nsfwTags } from '../home';
import BoxModal from '../box-modal'; import BoxModal from '../box-modal';
import useIsSubplebbitOffline from '../../../hooks/use-is-subplebbit-offline';
const Board = ({ subplebbit, subplebbits }: { subplebbit: Subplebbit; subplebbits: any }) => { const Board = ({ subplebbit, subplebbits }: { subplebbit: Subplebbit; subplebbits: any }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -18,15 +18,12 @@ const Board = ({ subplebbit, subplebbits }: { subplebbit: Subplebbit; subplebbit
const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`; const boardLink = useCatalog ? `/p/${address}/catalog` : `/p/${address}`;
const subplebbitData = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address); const subplebbitData = subplebbits && subplebbits.find((sub: Subplebbit) => sub?.address === address);
const updatedAt = subplebbitData?.updatedAt; const { isDefinitelyOffline, isOffline, offlineTitle } = useIsSubplebbitOffline(subplebbitData);
const isOffline = updatedAt && updatedAt < Date.now() / 1000 - 60 * 60; const iconClass = `${styles.offlineIcon} ${isDefinitelyOffline ? styles.redOfflineIcon : ''}`;
const offlineTitle = updatedAt
? isOffline && t('posts_last_synced_info', { time: getFormattedTimeAgo(updatedAt), interpolation: { escapeValue: false } })
: t('subplebbit_offline_info');
return ( return (
<div className={styles.subplebbit} key={address}> <div className={styles.subplebbit} key={address}>
{isOffline && <span className={styles.offlineIcon} title={offlineTitle} />} {isOffline && <span className={`${styles.offlineIcon} ${iconClass}`} title={offlineTitle} />}
<Link to={boardLink}>{title || address}</Link> <Link to={boardLink}>{title || address}</Link>
{nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>} {nsfwTag && <span className={styles.nsfw}> ({t(nsfwTag)})</span>}
</div> </div>
+4
View File
@@ -187,6 +187,10 @@
image-rendering: pixelated; image-rendering: pixelated;
} }
.redOfflineIcon {
background-image: url('/public/assets/icons/offline-red.png') !important;
}
.boardsBoxContent { .boardsBoxContent {
font-size: 93%; font-size: 93%;
padding: 0.5em; padding: 0.5em;