mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(state string): shorten browser p2p loading text
This commit is contained in:
@@ -62,6 +62,7 @@ const FeedStateStringHarness = ({ addresses }: { addresses?: string[] }) => {
|
|||||||
describe('use-state-string', () => {
|
describe('use-state-string', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
latestValue = undefined;
|
latestValue = undefined;
|
||||||
|
localStorage.removeItem('5chan:pure-p2p-browser-enabled');
|
||||||
testState.clientsStates = {};
|
testState.clientsStates = {};
|
||||||
testState.community = undefined;
|
testState.community = undefined;
|
||||||
testState.communitiesStates = {};
|
testState.communitiesStates = {};
|
||||||
@@ -88,6 +89,29 @@ describe('use-state-string', () => {
|
|||||||
expect(latestValue).toBe('Resolving address, downloading board via IPFS');
|
expect(latestValue).toBe('Resolving address, downloading board via IPFS');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('formats browser libp2p client state strings as peer downloads', () => {
|
||||||
|
testState.clientsStates = {
|
||||||
|
'fetching-ipns': ['libp2pjs'],
|
||||||
|
'resolving-address': ['https://ens.example.com'],
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(StateStringHarness, { value: { state: 'updating' } }));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(latestValue).toBe('Resolving address, downloading board from peers');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats browser p2p fallback publishing states as peer downloads', () => {
|
||||||
|
localStorage.setItem('5chan:pure-p2p-browser-enabled', 'true');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(StateStringHarness, { value: { publishingState: 'fetching-ipfs', state: 'publishing' } }));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(latestValue).toBe('Downloading thread from peers');
|
||||||
|
});
|
||||||
|
|
||||||
it('falls back to publishing and updating states when no client states are available', () => {
|
it('falls back to publishing and updating states when no client states are available', () => {
|
||||||
act(() => {
|
act(() => {
|
||||||
root.render(createElement(StateStringHarness, { value: { publishingState: 'fetching-ipfs', state: 'publishing' } }));
|
root.render(createElement(StateStringHarness, { value: { publishingState: 'fetching-ipfs', state: 'publishing' } }));
|
||||||
@@ -100,6 +124,20 @@ describe('use-state-string', () => {
|
|||||||
expect(latestValue).toBe('Downloading board via IPFS');
|
expect(latestValue).toBe('Downloading board via IPFS');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('formats browser p2p single-board feed fallback states as peer downloads', () => {
|
||||||
|
localStorage.setItem('5chan:pure-p2p-browser-enabled', 'true');
|
||||||
|
testState.community = {
|
||||||
|
state: 'updating',
|
||||||
|
updatingState: 'fetching-ipfs',
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth'] }));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(latestValue).toBe('Downloading board from peers');
|
||||||
|
});
|
||||||
|
|
||||||
it('sanitizes single-board feed state strings to board wording', () => {
|
it('sanitizes single-board feed state strings to board wording', () => {
|
||||||
testState.community = {
|
testState.community = {
|
||||||
state: 'updating',
|
state: 'updating',
|
||||||
@@ -140,6 +178,29 @@ describe('use-state-string', () => {
|
|||||||
expect(latestValue).toBe('Resolving 2 board addresses, downloading 2 boards (music-posting.eth, tech-posting.eth), 1 thread, 1 page via IPFS');
|
expect(latestValue).toBe('Resolving 2 board addresses, downloading 2 boards (music-posting.eth, tech-posting.eth), 1 thread, 1 page via IPFS');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('aggregates browser libp2p feed states as peer downloads', () => {
|
||||||
|
testState.communitiesStates = {
|
||||||
|
'fetching-ipfs': {
|
||||||
|
clientUrls: ['libp2pjs'],
|
||||||
|
communityAddresses: ['music-posting.eth'],
|
||||||
|
},
|
||||||
|
'fetching-ipns': {
|
||||||
|
clientUrls: ['libp2pjs'],
|
||||||
|
communityAddresses: ['music-posting.eth', 'tech-posting.eth'],
|
||||||
|
},
|
||||||
|
'page-1': {
|
||||||
|
clientUrls: ['libp2pjs'],
|
||||||
|
communityAddresses: ['music-posting.eth'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth', 'tech-posting.eth'] }));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(latestValue).toBe('Downloading 2 boards (music-posting.eth, tech-posting.eth), 1 thread, 1 page from peers');
|
||||||
|
});
|
||||||
|
|
||||||
it('shows an immediate board-specific loading string before detailed multi-board states arrive', () => {
|
it('shows an immediate board-specific loading string before detailed multi-board states arrive', () => {
|
||||||
act(() => {
|
act(() => {
|
||||||
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth', 'tech-posting.eth'] }));
|
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth', 'tech-posting.eth'] }));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMemo } from 'react';
|
|||||||
import { useClientsStates, useCommunity, useCommunitiesStates } from '@bitsocial/bitsocial-react-hooks';
|
import { useClientsStates, useCommunity, useCommunitiesStates } from '@bitsocial/bitsocial-react-hooks';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import getShortAddress from '../lib/get-short-address';
|
import getShortAddress from '../lib/get-short-address';
|
||||||
|
import { isBrowserPureP2PEnabled } from '../lib/p2p-runtime';
|
||||||
import { useCommunityIdentifiers } from './use-community-identifiers';
|
import { useCommunityIdentifiers } from './use-community-identifiers';
|
||||||
|
|
||||||
interface CommentOrCommunity {
|
interface CommentOrCommunity {
|
||||||
@@ -22,6 +23,11 @@ type CommunityLoadingState = {
|
|||||||
const isCommunityLoadingState = (state: string[] | CommunityLoadingState | undefined): state is CommunityLoadingState =>
|
const isCommunityLoadingState = (state: string[] | CommunityLoadingState | undefined): state is CommunityLoadingState =>
|
||||||
Boolean(state && !Array.isArray(state) && 'communityAddresses' in state && 'clientUrls' in state);
|
Boolean(state && !Array.isArray(state) && 'communityAddresses' in state && 'clientUrls' in state);
|
||||||
|
|
||||||
|
const isBrowserLibp2pClient = (clientUrl: string) => clientUrl === 'libp2pjs';
|
||||||
|
|
||||||
|
const getDownloadSourceSuffix = (clientUrls: string[], isBrowserPureP2P: boolean) =>
|
||||||
|
(clientUrls.length > 0 ? clientUrls.every(isBrowserLibp2pClient) : isBrowserPureP2P) ? ' from peers' : ' via IPFS';
|
||||||
|
|
||||||
const friendlyStateNames: Record<string, string> = {
|
const friendlyStateNames: Record<string, string> = {
|
||||||
'fetching-ipns': 'downloading board',
|
'fetching-ipns': 'downloading board',
|
||||||
'fetching-ipfs': 'downloading thread',
|
'fetching-ipfs': 'downloading thread',
|
||||||
@@ -49,6 +55,7 @@ const sanitizeSingleFeedLoadingState = (stateString?: string): string | undefine
|
|||||||
|
|
||||||
const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): string | undefined => {
|
const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): string | undefined => {
|
||||||
const { states: rawStates } = useClientsStates({ comment: commentOrCommunity }) as { states: States };
|
const { states: rawStates } = useClientsStates({ comment: commentOrCommunity }) as { states: States };
|
||||||
|
const isBrowserPureP2P = isBrowserPureP2PEnabled();
|
||||||
|
|
||||||
const debouncedStates = useMemo(() => {
|
const debouncedStates = useMemo(() => {
|
||||||
const debouncedValue = debounce((value: States) => value, 300);
|
const debouncedValue = debounce((value: States) => value, 300);
|
||||||
@@ -59,6 +66,7 @@ const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): str
|
|||||||
let stateString: string | undefined = '';
|
let stateString: string | undefined = '';
|
||||||
const resolvingParts: string[] = [];
|
const resolvingParts: string[] = [];
|
||||||
const downloadingParts: string[] = [];
|
const downloadingParts: string[] = [];
|
||||||
|
const downloadingClientUrls: string[] = [];
|
||||||
|
|
||||||
for (const state in debouncedStates) {
|
for (const state in debouncedStates) {
|
||||||
if (debouncedStates[state].length === 0) continue;
|
if (debouncedStates[state].length === 0) continue;
|
||||||
@@ -67,6 +75,7 @@ const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): str
|
|||||||
resolvingParts.push(friendlyName);
|
resolvingParts.push(friendlyName);
|
||||||
} else {
|
} else {
|
||||||
downloadingParts.push(friendlyName);
|
downloadingParts.push(friendlyName);
|
||||||
|
downloadingClientUrls.push(...debouncedStates[state]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +84,7 @@ const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): str
|
|||||||
}
|
}
|
||||||
if (downloadingParts.length) {
|
if (downloadingParts.length) {
|
||||||
if (stateString) stateString += ', ';
|
if (stateString) stateString += ', ';
|
||||||
stateString += downloadingParts.join(', ') + ' via IPFS';
|
stateString += downloadingParts.join(', ') + getDownloadSourceSuffix(downloadingClientUrls, isBrowserPureP2P);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stateString && commentOrCommunity?.state !== 'succeeded') {
|
if (!stateString && commentOrCommunity?.state !== 'succeeded') {
|
||||||
@@ -94,7 +103,7 @@ const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): str
|
|||||||
.replace('community community', 'board')
|
.replace('community community', 'board')
|
||||||
.replace('downloading community', 'downloading board');
|
.replace('downloading community', 'downloading board');
|
||||||
if (isIpfsRelated) {
|
if (isIpfsRelated) {
|
||||||
stateString += ' via IPFS';
|
stateString += getDownloadSourceSuffix([], isBrowserPureP2P);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,10 +113,11 @@ const useStateString = (commentOrCommunity: CommentOrCommunity | undefined): str
|
|||||||
}
|
}
|
||||||
|
|
||||||
return stateString === '' ? undefined : stateString;
|
return stateString === '' ? undefined : stateString;
|
||||||
}, [debouncedStates, commentOrCommunity]);
|
}, [debouncedStates, commentOrCommunity, isBrowserPureP2P]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFeedStateString = (communityAddresses?: string[]): string | undefined => {
|
export const useFeedStateString = (communityAddresses?: string[]): string | undefined => {
|
||||||
|
const isBrowserPureP2P = isBrowserPureP2PEnabled();
|
||||||
const communities = useCommunityIdentifiers(communityAddresses);
|
const communities = useCommunityIdentifiers(communityAddresses);
|
||||||
|
|
||||||
// single community feed state string
|
// single community feed state string
|
||||||
@@ -136,11 +146,13 @@ export const useFeedStateString = (communityAddresses?: string[]): string | unde
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pagesStatesCommunityAddresses = new Set<string>();
|
const pagesStatesCommunityAddresses = new Set<string>();
|
||||||
|
const downloadingClientUrls: string[] = [];
|
||||||
for (const state in states) {
|
for (const state in states) {
|
||||||
if (state.match('page')) {
|
if (state.match('page')) {
|
||||||
const communityState = states[state];
|
const communityState = states[state];
|
||||||
if (isCommunityLoadingState(communityState)) {
|
if (isCommunityLoadingState(communityState)) {
|
||||||
communityState.communityAddresses.forEach((address: string) => pagesStatesCommunityAddresses.add(address));
|
communityState.communityAddresses.forEach((address: string) => pagesStatesCommunityAddresses.add(address));
|
||||||
|
downloadingClientUrls.push(...communityState.clientUrls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +163,7 @@ export const useFeedStateString = (communityAddresses?: string[]): string | unde
|
|||||||
if (states['fetching-ipns']) {
|
if (states['fetching-ipns']) {
|
||||||
const fetchingIpnsState = states['fetching-ipns'];
|
const fetchingIpnsState = states['fetching-ipns'];
|
||||||
if (isCommunityLoadingState(fetchingIpnsState)) {
|
if (isCommunityLoadingState(fetchingIpnsState)) {
|
||||||
|
downloadingClientUrls.push(...fetchingIpnsState.clientUrls);
|
||||||
const count = fetchingIpnsState.communityAddresses.length;
|
const count = fetchingIpnsState.communityAddresses.length;
|
||||||
stateString += `${count} ${count === 1 ? 'board' : 'boards'}`;
|
stateString += `${count} ${count === 1 ? 'board' : 'boards'}`;
|
||||||
if (count <= 5) {
|
if (count <= 5) {
|
||||||
@@ -162,6 +175,7 @@ export const useFeedStateString = (communityAddresses?: string[]): string | unde
|
|||||||
if (states['fetching-ipfs']) {
|
if (states['fetching-ipfs']) {
|
||||||
const fetchingIpfsState = states['fetching-ipfs'];
|
const fetchingIpfsState = states['fetching-ipfs'];
|
||||||
if (isCommunityLoadingState(fetchingIpfsState)) {
|
if (isCommunityLoadingState(fetchingIpfsState)) {
|
||||||
|
downloadingClientUrls.push(...fetchingIpfsState.clientUrls);
|
||||||
if (stateString[stateString.length - 1] !== ' ') {
|
if (stateString[stateString.length - 1] !== ' ') {
|
||||||
stateString += ', ';
|
stateString += ', ';
|
||||||
}
|
}
|
||||||
@@ -176,7 +190,7 @@ export const useFeedStateString = (communityAddresses?: string[]): string | unde
|
|||||||
stateString += `${count} ${count === 1 ? 'page' : 'pages'}`;
|
stateString += `${count} ${count === 1 ? 'page' : 'pages'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
stateString += ' via IPFS';
|
stateString += getDownloadSourceSuffix(downloadingClientUrls, isBrowserPureP2P);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stateString && communityAddresses?.length) {
|
if (!stateString && communityAddresses?.length) {
|
||||||
@@ -192,7 +206,7 @@ export const useFeedStateString = (communityAddresses?: string[]): string | unde
|
|||||||
|
|
||||||
// if string is empty, return undefined instead
|
// if string is empty, return undefined instead
|
||||||
return stateString === '' ? undefined : stateString;
|
return stateString === '' ? undefined : stateString;
|
||||||
}, [states, communityAddress, communityAddresses]);
|
}, [states, communityAddress, communityAddresses, isBrowserPureP2P]);
|
||||||
|
|
||||||
if (singleCommunityFeedStateString) {
|
if (singleCommunityFeedStateString) {
|
||||||
return singleCommunityFeedStateString;
|
return singleCommunityFeedStateString;
|
||||||
|
|||||||
Reference in New Issue
Block a user