diff --git a/src/pages/search.jsx b/src/pages/search.jsx index 50c31f89..295a4efe 100644 --- a/src/pages/search.jsx +++ b/src/pages/search.jsx @@ -34,9 +34,10 @@ const scrollIntoViewOptions = { function Search({ columnMode, ...props }) { const { t } = useLingui(); const params = columnMode ? {} : useParams(); - const { masto, instance, authenticated } = api({ + const { masto, instance, authenticated, client } = api({ instance: params.instance, }); + const atproto = !!client?.atproto; const [uiState, setUIState] = useState('default'); const [searchParams] = columnMode ? [emptySearchParams] : useSearchParams(); const searchFormRef = useRef(); @@ -64,8 +65,10 @@ function Search({ columnMode, ...props }) { const [showMore, setShowMore] = useState(false); const offsetRef = useRef(0); + const cursorRef = useRef({}); useEffect(() => { offsetRef.current = 0; + cursorRef.current = {}; }, [q, type]); const scrollableRef = useRef(); @@ -109,7 +112,7 @@ function Search({ columnMode, ...props }) { offsetRef.current = 0; } - if (!firstLoad && !authenticated) { + if (!firstLoad && !authenticated && !atproto) { // Search results pagination is only available to authenticated users return; } @@ -130,18 +133,34 @@ function Search({ columnMode, ...props }) { if (type) { params.limit = LIMIT; params.type = type; - if (authenticated) params.offset = offsetRef.current; + if (atproto) { + const cursor = cursorRef.current[type]; + if (!firstLoad && !cursor) { + setShowMore(false); + setUIState('default'); + return; + } + if (cursor) params.cursor = cursor; + } else if (authenticated) { + params.offset = offsetRef.current; + } } try { const results = await masto.v2.search.list(params); console.log(results); if (type) { + const nextCursor = results._pagination?.[type]; if (firstLoad) { setTypeResultsFunc[type](results[type]); const length = results[type]?.length; offsetRef.current = LIMIT; - setShowMore(!!length); + cursorRef.current[type] = nextCursor; + setShowMore(atproto ? !!nextCursor : !!length); + } else if (atproto) { + setTypeResultsFunc[type]((prev) => [...prev, ...results[type]]); + cursorRef.current[type] = nextCursor; + setShowMore(!!nextCursor); } else { // If first item is the same, it means API doesn't support offset // I know this is a very basic check, but it works for now @@ -161,7 +180,7 @@ function Search({ columnMode, ...props }) { offsetRef.current = 0; setShowMore(false); } - loadRelationships(results.accounts); + if (authenticated) loadRelationships(results.accounts); setUIState('default'); } catch (err) { diff --git a/src/utils/api.js b/src/utils/api.js index e044eceb..66ce2973 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -7,6 +7,7 @@ import { atprotoInstanceInfo, BSKY_INSTANCE, createAtprotoClient, + createPublicAtprotoClient, } from './atproto-adapter'; import { getCachedAtprotoOAuthSession, @@ -78,12 +79,15 @@ export function initClient({ instance, accessToken }) { accountApis[instance][nextAccessToken] = client; } }; - const masto = createAtprotoClient({ - session: atprotoSession?.session, - oauthSession, - service: atprotoSession?.service, - persistSession, - }); + const masto = + atprotoSession || atprotoOAuthSession + ? createAtprotoClient({ + session: atprotoSession?.session, + oauthSession, + service: atprotoSession?.service, + persistSession, + }) + : createPublicAtprotoClient(); client = { masto, instance, diff --git a/src/utils/atproto-adapter.js b/src/utils/atproto-adapter.js index a4b6a64a..dd878552 100644 --- a/src/utils/atproto-adapter.js +++ b/src/utils/atproto-adapter.js @@ -1509,12 +1509,15 @@ export function createAtprotoClient({ }, }, search: { - async list({ q, limit = 10 } = {}) { + async list({ q, limit = 10, cursor } = {}) { const res = await agent.searchActors({ - term: q || '', + q: q || '', limit, + cursor, }); - return res.data.actors.map(actorToAccount); + const accounts = res.data.actors.map(actorToAccount); + accounts._pagination = { cursor: res.data.cursor }; + return accounts; }, }, }, @@ -2190,30 +2193,44 @@ export function createAtprotoClient({ async fetch(params = {}) { return this.list(params); }, - async list({ q = '', type, limit = 20 } = {}) { + async list({ q = '', type, limit = 20, cursor, sort } = {}) { const wanted = type ? [type] : ['accounts', 'statuses', 'hashtags']; const results = { accounts: [], statuses: [], hashtags: [], + _pagination: {}, }; if (wanted.includes('accounts')) { - const res = await agent.searchActors({ - term: q, - limit, - }); - results.accounts = res.data.actors.map(actorToAccount); + try { + const res = await agent.searchActors({ + q, + limit, + cursor, + }); + results.accounts = res.data.actors.map(actorToAccount); + results._pagination.accounts = res.data.cursor; + } catch (err) { + if (wanted.length === 1) throw err; + } } if (wanted.includes('statuses')) { - const res = await agent.app.bsky.feed.searchPosts({ - q, - limit, - }); - results.statuses = res.data.posts.map((post) => - postToStatus(post, agent), - ); + try { + const res = await agent.app.bsky.feed.searchPosts({ + q, + limit, + cursor, + sort, + }); + results.statuses = res.data.posts.map((post) => + postToStatus(post, agent), + ); + results._pagination.statuses = res.data.cursor; + } catch (err) { + if (wanted.length === 1) throw err; + } } if (wanted.includes('hashtags')) {