mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
refactor(state-string): make loading state strings user-friendly, replace gateway hostnames with via IPFS
This commit is contained in:
@@ -75,7 +75,7 @@ describe('use-state-string', () => {
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it('formats client state strings with normalized hostnames', () => {
|
||||
it('formats client state strings with friendly names and via IPFS', () => {
|
||||
testState.clientsStates = {
|
||||
'fetching-ipns': ['https://rpc.example.com/path', 'https://ipfs.io/api'],
|
||||
'resolving-address': ['https://ens.example.com'],
|
||||
@@ -85,19 +85,19 @@ describe('use-state-string', () => {
|
||||
root.render(createElement(StateStringHarness, { value: { state: 'updating' } }));
|
||||
});
|
||||
|
||||
expect(latestValue).toBe('Fetching IPNS from rpc.example.com, ipfs.io, resolving address from ens.example.com');
|
||||
expect(latestValue).toBe('Resolving address, downloading board via IPFS');
|
||||
});
|
||||
|
||||
it('falls back to publishing and updating states when no client states are available', () => {
|
||||
act(() => {
|
||||
root.render(createElement(StateStringHarness, { value: { publishingState: 'fetching-ipfs', state: 'publishing' } }));
|
||||
});
|
||||
expect(latestValue).toBe('Downloading thread');
|
||||
expect(latestValue).toBe('Downloading thread via IPFS');
|
||||
|
||||
act(() => {
|
||||
root.render(createElement(StateStringHarness, { value: { state: 'updating', updatingState: 'fetching-ipns' } }));
|
||||
});
|
||||
expect(latestValue).toBe('Downloading board');
|
||||
expect(latestValue).toBe('Downloading board via IPFS');
|
||||
});
|
||||
|
||||
it('sanitizes single-board feed state strings to board wording', () => {
|
||||
@@ -110,7 +110,7 @@ describe('use-state-string', () => {
|
||||
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth'] }));
|
||||
});
|
||||
|
||||
expect(latestValue).toBe('Downloading board');
|
||||
expect(latestValue).toBe('Downloading board via IPFS');
|
||||
});
|
||||
|
||||
it('aggregates multi-board feed states across address resolution, threads, and pages', () => {
|
||||
@@ -137,7 +137,7 @@ describe('use-state-string', () => {
|
||||
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth', 'tech-posting.eth'] }));
|
||||
});
|
||||
|
||||
expect(latestValue).toBe('Resolving 2 addresses from ens.example.com, downloading 2 boards, 1 threads, 1 page from gateway.example.com, ipfs.io');
|
||||
expect(latestValue).toBe('Resolving 2 board addresses, downloading 2 boards (music-posting.eth, tech-posting.eth), 1 thread, 1 page via IPFS');
|
||||
});
|
||||
|
||||
it('shows an immediate board-specific loading string before detailed multi-board states arrive', () => {
|
||||
@@ -145,6 +145,17 @@ describe('use-state-string', () => {
|
||||
root.render(createElement(FeedStateStringHarness, { addresses: ['music-posting.eth', 'tech-posting.eth'] }));
|
||||
});
|
||||
|
||||
expect(latestValue).toBe('Downloading 2 boards');
|
||||
expect(latestValue).toBe('Downloading 2 boards (music-posting.eth, tech-posting.eth)');
|
||||
});
|
||||
|
||||
it('shortens long hash addresses in board list using getShortAddress', () => {
|
||||
const longAddr1 = 'AdnytMQQMvAkG3XbzoVyAE6YLmHuG3UDigUC';
|
||||
const longAddr2 = 'NFgjQWX2EUEsZbzoVyAE6YLmHuG3UDigUCxx';
|
||||
|
||||
act(() => {
|
||||
root.render(createElement(FeedStateStringHarness, { addresses: [longAddr1, longAddr2] }));
|
||||
});
|
||||
|
||||
expect(latestValue).toBe('Downloading 2 boards (MvAkG3XbzoVy, EUEsZbzoVyAE)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useClientsStates, useSubplebbit, useSubplebbitsStates } from '@bitsocialnet/bitsocial-react-hooks';
|
||||
import debounce from 'lodash/debounce';
|
||||
import getShortAddress from '../lib/get-short-address';
|
||||
|
||||
interface CommentOrSubplebbit {
|
||||
state?: string;
|
||||
@@ -12,19 +13,19 @@ interface States {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
const clientHosts: { [key: string]: string } = {};
|
||||
|
||||
const getClientHost = (clientUrl: string): string => {
|
||||
if (!clientHosts[clientUrl]) {
|
||||
try {
|
||||
clientHosts[clientUrl] = new URL(clientUrl).hostname || clientUrl;
|
||||
} catch {
|
||||
clientHosts[clientUrl] = clientUrl;
|
||||
}
|
||||
}
|
||||
return clientHosts[clientUrl];
|
||||
const friendlyStateNames: Record<string, string> = {
|
||||
'fetching-ipns': 'downloading board',
|
||||
'fetching-ipfs': 'downloading thread',
|
||||
'fetching-subplebbit-ipns': 'downloading board',
|
||||
'fetching-subplebbit-ipfs': 'downloading board',
|
||||
'fetching-update-ipfs': 'downloading update',
|
||||
'resolving-address': 'resolving address',
|
||||
'resolving-subplebbit-address': 'resolving board address',
|
||||
'resolving-author-address': 'resolving author address',
|
||||
};
|
||||
|
||||
const getFriendlyStateName = (state: string): string => friendlyStateNames[state] || state.replaceAll('-', ' ');
|
||||
|
||||
const sanitizeSingleFeedLoadingState = (stateString?: string): string | undefined => {
|
||||
if (!stateString) {
|
||||
return stateString;
|
||||
@@ -47,21 +48,25 @@ const useStateString = (commentOrSubplebbit: CommentOrSubplebbit): string | unde
|
||||
|
||||
return useMemo(() => {
|
||||
let stateString: string | undefined = '';
|
||||
const resolvingParts: string[] = [];
|
||||
const downloadingParts: string[] = [];
|
||||
|
||||
for (const state in debouncedStates) {
|
||||
const clientUrls = debouncedStates[state];
|
||||
const clientHosts = clientUrls.map((clientUrl: string) => getClientHost(clientUrl));
|
||||
|
||||
if (clientHosts.length === 0) {
|
||||
continue;
|
||||
if (debouncedStates[state].length === 0) continue;
|
||||
const friendlyName = getFriendlyStateName(state);
|
||||
if (state.includes('resolving')) {
|
||||
resolvingParts.push(friendlyName);
|
||||
} else {
|
||||
downloadingParts.push(friendlyName);
|
||||
}
|
||||
}
|
||||
|
||||
if (stateString) {
|
||||
stateString += ', ';
|
||||
}
|
||||
|
||||
const formattedState = state.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS');
|
||||
stateString += `${formattedState} from ${clientHosts.join(', ')}`;
|
||||
if (resolvingParts.length) {
|
||||
stateString = resolvingParts.join(', ');
|
||||
}
|
||||
if (downloadingParts.length) {
|
||||
if (stateString) stateString += ', ';
|
||||
stateString += downloadingParts.join(', ') + ' via IPFS';
|
||||
}
|
||||
|
||||
if (!stateString && commentOrSubplebbit?.state !== 'succeeded') {
|
||||
@@ -71,6 +76,7 @@ const useStateString = (commentOrSubplebbit: CommentOrSubplebbit): string | unde
|
||||
stateString = commentOrSubplebbit?.updatingState;
|
||||
}
|
||||
if (stateString) {
|
||||
const isIpfsRelated = stateString.includes('ipfs') || stateString.includes('ipns');
|
||||
stateString = stateString
|
||||
.replaceAll('-', ' ')
|
||||
.replace('ipfs', 'thread')
|
||||
@@ -78,6 +84,9 @@ const useStateString = (commentOrSubplebbit: CommentOrSubplebbit): string | unde
|
||||
.replace('fetching', 'downloading')
|
||||
.replace('subplebbit subplebbit', 'board')
|
||||
.replace('downloading subplebbit', 'downloading board');
|
||||
if (isIpfsRelated) {
|
||||
stateString += ' via IPFS';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,62 +112,52 @@ export const useFeedStateString = (subplebbitAddresses?: string[]): string | und
|
||||
return;
|
||||
}
|
||||
|
||||
// e.g. Resolving 2 addresses from infura.io, fetching 2 IPNS, 1 IPFS from cloudflare-ipfs.com, ipfs.io
|
||||
let stateString = '';
|
||||
|
||||
if (states['resolving-address']) {
|
||||
const { subplebbitAddresses, clientUrls } = states['resolving-address'];
|
||||
if (subplebbitAddresses.length && clientUrls.length) {
|
||||
stateString += `resolving ${subplebbitAddresses.length} ${subplebbitAddresses.length === 1 ? 'address' : 'addresses'} from ${clientUrls
|
||||
.map(getClientHost)
|
||||
.join(', ')}`;
|
||||
const count = subplebbitAddresses.length;
|
||||
stateString += `resolving ${count} board ${count === 1 ? 'address' : 'addresses'}`;
|
||||
}
|
||||
}
|
||||
|
||||
// find all page client and sub addresses
|
||||
const pagesStatesClientHosts = new Set();
|
||||
const pagesStatesSubplebbitAddresses = new Set();
|
||||
const pagesStatesSubplebbitAddresses = new Set<string>();
|
||||
for (const state in states) {
|
||||
if (state.match('page')) {
|
||||
states[state].clientUrls.forEach((clientUrl) => pagesStatesClientHosts.add(getClientHost(clientUrl)));
|
||||
states[state].subplebbitAddresses.forEach((subplebbitAddress) => pagesStatesSubplebbitAddresses.add(subplebbitAddress));
|
||||
states[state].subplebbitAddresses.forEach((subplebbitAddress: string) => pagesStatesSubplebbitAddresses.add(subplebbitAddress));
|
||||
}
|
||||
}
|
||||
|
||||
if (states['fetching-ipns'] || states['fetching-ipfs'] || pagesStatesSubplebbitAddresses.size) {
|
||||
// separate 2 different states using ', '
|
||||
if (stateString) {
|
||||
stateString += ', ';
|
||||
if (stateString) stateString += ', ';
|
||||
stateString += 'downloading ';
|
||||
if (states['fetching-ipns']) {
|
||||
const count = states['fetching-ipns'].subplebbitAddresses.length;
|
||||
stateString += `${count} ${count === 1 ? 'board' : 'boards'}`;
|
||||
if (count <= 5) {
|
||||
stateString += ` (${states['fetching-ipns'].subplebbitAddresses.map((a: string) => getShortAddress(a) || a).join(', ')})`;
|
||||
}
|
||||
}
|
||||
|
||||
// find all client urls
|
||||
const clientHosts = new Set(pagesStatesClientHosts);
|
||||
states['fetching-ipns']?.clientUrls.forEach((clientUrl) => clientHosts.add(getClientHost(clientUrl)));
|
||||
states['fetching-ipfs']?.clientUrls.forEach((clientUrl) => clientHosts.add(getClientHost(clientUrl)));
|
||||
|
||||
if (clientHosts.size) {
|
||||
stateString += 'downloading ';
|
||||
if (states['fetching-ipns']) {
|
||||
stateString += `${states['fetching-ipns'].subplebbitAddresses.length} boards`;
|
||||
}
|
||||
if (states['fetching-ipfs']) {
|
||||
if (states['fetching-ipns']) {
|
||||
stateString += ', ';
|
||||
}
|
||||
stateString += `${states['fetching-ipfs'].subplebbitAddresses.length} threads`;
|
||||
}
|
||||
if (pagesStatesSubplebbitAddresses.size) {
|
||||
if (states['fetching-ipns'] || states['fetching-ipfs']) {
|
||||
stateString += ', ';
|
||||
}
|
||||
stateString += `${pagesStatesSubplebbitAddresses.size} ${pagesStatesSubplebbitAddresses.size === 1 ? 'page' : 'pages'}`;
|
||||
}
|
||||
stateString += ` from ${[...clientHosts].join(', ')}`;
|
||||
if (states['fetching-ipfs']) {
|
||||
if (states['fetching-ipns']) stateString += ', ';
|
||||
const count = states['fetching-ipfs'].subplebbitAddresses.length;
|
||||
stateString += `${count} ${count === 1 ? 'thread' : 'threads'}`;
|
||||
}
|
||||
if (pagesStatesSubplebbitAddresses.size) {
|
||||
if (states['fetching-ipns'] || states['fetching-ipfs']) stateString += ', ';
|
||||
const count = pagesStatesSubplebbitAddresses.size;
|
||||
stateString += `${count} ${count === 1 ? 'page' : 'pages'}`;
|
||||
}
|
||||
stateString += ' via IPFS';
|
||||
}
|
||||
|
||||
if (!stateString && subplebbitAddresses?.length) {
|
||||
stateString = `downloading ${subplebbitAddresses.length} boards`;
|
||||
const count = subplebbitAddresses.length;
|
||||
stateString = `downloading ${count} ${count === 1 ? 'board' : 'boards'}`;
|
||||
if (count <= 5) {
|
||||
stateString += ` (${subplebbitAddresses.map((a) => getShortAddress(a) || a).join(', ')})`;
|
||||
}
|
||||
}
|
||||
|
||||
// capitalize first letter
|
||||
|
||||
Reference in New Issue
Block a user