From 24af1e00ade2d8845fa424c83312775a8582dbf7 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 9 May 2023 21:20:30 +0200 Subject: [PATCH] add loading state to subscriptions view --- src/components/views/Board.jsx | 20 ++++---- src/components/views/Catalog.jsx | 2 +- src/components/views/Subscriptions.jsx | 34 ++++++++++++-- src/hooks/useFeedStateString.js | 65 ++++++++++++++++++++++++++ src/hooks/useStateString.js | 22 ++++++--- 5 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 src/hooks/useFeedStateString.js diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx index 69cbbd6c..c2562d50 100644 --- a/src/components/views/Board.jsx +++ b/src/components/views/Board.jsx @@ -68,6 +68,15 @@ const Board = () => { const stateString = useStateString(subplebbit?.clients); + const { subscribed, subscribe, unsubscribe } = useSubscribe({subplebbitAddress: selectedAddress}); + + const [errorMessage, setErrorMessage] = useState(null); + const [successMessage] = useState(null); + useError(errorMessage, [errorMessage]); + useSuccess(successMessage, [successMessage]); + + const [triggerPublishComment, setTriggerPublishComment] = useState(false); + const errorString = useMemo(() => { if (subplebbit?.state === 'failed') { let errorString = 'Failed fetching board "' + selectedAddress + '".'; @@ -84,15 +93,6 @@ const Board = () => { } }, [errorString]); - const { subscribed, subscribe, unsubscribe } = useSubscribe({subplebbitAddress: selectedAddress}); - - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage] = useState(null); - useError(errorMessage, [errorMessage]); - useSuccess(successMessage, [successMessage]); - - const [triggerPublishComment, setTriggerPublishComment] = useState(false); - const flattenedRepliesByThread = useMemo(() => { return selectedFeed.reduce((acc, thread) => { @@ -541,7 +541,7 @@ const Board = () => { {subplebbit?.state === "failed" ? ( null ) : ( - feed.length > 1 ? ( + feed.length > 0 ? ( { {subplebbit?.state === "failed" ? ( null ) : ( - feed.length > 1 ? ( + feed.length > 0 ? ( { const navigate = useNavigate(); const [prevScrollPos, setPrevScrollPos] = useState(0); const [visible, setVisible] = useState(true); + + const [errorMessage, setErrorMessage] = useState(null); + useError(errorMessage, [errorMessage]); + const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: account?.subscriptions, sortType: 'new'}); const [selectedFeed, setSelectedFeed] = useState(feed.sort((a, b) => b.timestamp - a.timestamp)); + const {subplebbits} = useSubplebbits({subplebbitAddresses: account?.subscriptions, sortType: 'new'}); + + const stateString = useFeedStateString(subplebbits); + + const errorString = useMemo(() => { + for (const subplebbit of subplebbits) { + if (subplebbit?.updatingState !== 'failed') { + return + } + } + for (const subplebbit of subplebbits) { + if (subplebbit?.error) { + setErrorMessage(`Failed fetching subplebbit: ${subplebbit?.error.toString().slice(0, 300)}`); + } + } + }, [subplebbits]) + useEffect(() => { setSelectedFeed(feed.sort((a, b) => b.timestamp - a.timestamp)); }, [feed]); - - const [errorMessage] = useState(null); - useError(errorMessage, [errorMessage]); const flattenedRepliesByThread = useMemo(() => { @@ -258,6 +277,13 @@ const Subscriptions = () => { Catalog ] + {feed.length > 0 ? ( + null + ) : ( +
+ {stateString} +
+ )}
Catalog diff --git a/src/hooks/useFeedStateString.js b/src/hooks/useFeedStateString.js new file mode 100644 index 00000000..bc3da548 --- /dev/null +++ b/src/hooks/useFeedStateString.js @@ -0,0 +1,65 @@ +import { useMemo } from "react" + +const useFeedStateString = (subplebbits) => { + const getClientUrls = (regex) => { + const clientUrls = new Set() + for (const clientType in subplebbits.clients) { + for (const clientUrl in subplebbits.clients[clientType]) { + const client = subplebbits.clients[clientType][clientUrl] + if (client.state.match(regex)) { + clientUrls.add(clientUrl) + } + } + } + return [...clientUrls] + } + + return useMemo(() => { + if (!subplebbits) { + return undefined; + } + + const states = {} + const clientUrls = {} + for (const subplebbit of subplebbits) { + states[subplebbit?.updatingState] = (states[subplebbit?.updatingState] || 0) + 1 + } + + // 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']) { + stateString += `resolving ${states['resolving-address']} addresses` + const clientUrls = getClientUrls(/address/) + if (clientUrls.length) { + stateString += ` from ${clientUrls.join(', ')}` + } + } + if (states['fetching-ipns'] || states['fetching-ipfs']) { + if (stateString) { + stateString += ', ' + } + stateString += `fetching ` + if (states['fetching-ipns']) { + stateString += `${states['fetching-ipns']} IPNS` + } + if (states['fetching-ipfs']) { + if (states['fetching-ipns']) { + stateString += ', ' + } + stateString += `${states['fetching-ipfs']} IPNS` + } + const clientUrls = getClientUrls(/ipfs|ipns/) + if (clientUrls.length) { + stateString += ` from ${clientUrls.join(', ')}` + } + } + + // capitalize first letter + stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1) + + // if string is empty, return undefined instead + return stateString || undefined + }, [subplebbits]) +} + +export default useFeedStateString \ No newline at end of file diff --git a/src/hooks/useStateString.js b/src/hooks/useStateString.js index b7f21e15..9c92685f 100644 --- a/src/hooks/useStateString.js +++ b/src/hooks/useStateString.js @@ -1,10 +1,12 @@ import { useMemo } from 'react' +import isValidUrl from '../utils/isValidUrl' const useStateString = (clients) => { - const stateString = useMemo(() => { + return useMemo(() => { if (!clients) { return } + const states = {} for (const clientType in clients) { for (const clientUrl in clients[clientType]) { @@ -22,7 +24,12 @@ const useStateString = (clients) => { let stateString = '' for (const state in states) { const clientUrls = states[state] - const clientHosts = clientUrls.map(clientUrl => new URL(clientUrl).hostname) + const clientHosts = clientUrls.filter(isValidUrl).map(clientUrl => new URL(clientUrl).hostname) + + // if there are no valid hosts, skip this state + if (clientHosts.length === 0) { + continue + } // separate 2 different states using ', ' if (stateString) { @@ -35,13 +42,16 @@ const useStateString = (clients) => { } // capitalize first letter - stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1) + if (stateString) { + stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1) + + console.log("first log:", stateString); + } // if string is empty, return undefined instead - return stateString || undefined + console.log("second log:", stateString); + return stateString === '' ? undefined : stateString }, [clients]) - - return stateString } export default useStateString \ No newline at end of file