Skip to content

Commit

Permalink
feat: React Suspense support in usePublications hook
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarenaldi committed May 7, 2024
1 parent aa460c7 commit 5a2b062
Show file tree
Hide file tree
Showing 10 changed files with 466 additions and 402 deletions.
7 changes: 7 additions & 0 deletions .changeset/brown-guests-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@lens-protocol/react": minor
"@lens-protocol/react-native": minor
"@lens-protocol/react-web": minor
---

**feat:** adds React Suspense support to `usePublications` hook
232 changes: 121 additions & 111 deletions examples/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,134 +94,144 @@ import {
UseRateLimits,
WalletPage,
} from './wallet';
import { Suspense } from 'react';

Check failure on line 97 in examples/web/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint

There should be at least one empty line between import groups

Check failure on line 97 in examples/web/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint

`react` import should occur before import of `react-hot-toast`
import { Loading } from './components/loading/Loading';

Check failure on line 98 in examples/web/src/App.tsx

View workflow job for this annotation

GitHub Actions / Lint

`./components/loading/Loading` import should occur before import of `./discovery`

export function App() {
return (
<Providers>
<Router>
<Header />
<main>
<GenericErrorBoundary fallback={ErrorMessage}>
<Routes>
<Route index element={<HomePage />} />
<Route path="/login" element={<LogInPage />} />
<Suspense fallback={<Loading />}>
<GenericErrorBoundary fallback={ErrorMessage}>
<Routes>
<Route index element={<HomePage />} />
<Route path="/login" element={<LogInPage />} />

<Route element={<Layout />}>
<Route path="/publications">
<Route index element={<PublicationsPage />} />
<Route path="useCreatePost" element={<UseCreatePost />} />
<Route path="useOptimisticCreatePost" element={<UseOptimisticCreatePost />} />
<Route path="useCreateComment" element={<UseCreateComment />} />
<Route path="useCreateMirror" element={<UseCreateMirror />} />
<Route path="useCreateQuote" element={<UseCreateQuote />} />
<Route path="usePublication" element={<UsePublication />} />
<Route path="usePublications" element={<UsePublications />} />
<Route path="useLazyPublications" element={<UseLazyPublications />} />
<Route
path="useWhoReactedToPublication"
element={<UseWhoReactedToPublication />}
/>
<Route path="useHidePublication" element={<UseHidePublication />} />
<Route path="useReportPublication" element={<UseReportPublication />} />
<Route path="useReactionToggle" element={<UseReactionToggle />} />
<Route path="useBookmarkToggle" element={<UseBookmarkToggle />} />
<Route path="useBookmarks" element={<UseBookmarks />} />
<Route path="useOpenAction" element={<UseOpenAction />} />
<Route path="useNotInterestedToggle" element={<UseNotInterestedToggle />} />
<Route path="useHideCommentToggle" element={<UseHideCommentToggle />} />
</Route>
<Route element={<Layout />}>
<Route path="/publications">
<Route index element={<PublicationsPage />} />
<Route path="useCreatePost" element={<UseCreatePost />} />
<Route path="useOptimisticCreatePost" element={<UseOptimisticCreatePost />} />
<Route path="useCreateComment" element={<UseCreateComment />} />
<Route path="useCreateMirror" element={<UseCreateMirror />} />
<Route path="useCreateQuote" element={<UseCreateQuote />} />
<Route path="usePublication" element={<UsePublication />} />
<Route path="usePublications" element={<UsePublications />} />
<Route path="useLazyPublications" element={<UseLazyPublications />} />
<Route
path="useWhoReactedToPublication"
element={<UseWhoReactedToPublication />}
/>
<Route path="useHidePublication" element={<UseHidePublication />} />
<Route path="useReportPublication" element={<UseReportPublication />} />
<Route path="useReactionToggle" element={<UseReactionToggle />} />
<Route path="useBookmarkToggle" element={<UseBookmarkToggle />} />
<Route path="useBookmarks" element={<UseBookmarks />} />
<Route path="useOpenAction" element={<UseOpenAction />} />
<Route path="useNotInterestedToggle" element={<UseNotInterestedToggle />} />
<Route path="useHideCommentToggle" element={<UseHideCommentToggle />} />
</Route>

<Route path="/profiles">
<Route index element={<ProfilesPage />} />
<Route path="useCreateProfile" element={<UseCreateProfile />} />
<Route path="useProfile" element={<UseProfile />} />
<Route path="useLazyProfile" element={<UseLazyProfile />} />
<Route path="useProfiles" element={<UseProfiles />} />
<Route path="useLazyProfiles" element={<UseLazyProfiles />} />
<Route path="useProfileFollowers" element={<UseProfileFollowers />} />
<Route path="useProfileFollowing" element={<UseProfileFollowing />} />
<Route path="useMutualFollowers" element={<UseMutualFollowers />} />
<Route path="useFollow" element={<UseFollowAndUnfollow />} />
<Route path="useProfileManagers" element={<UseProfileManagers />} />
<Route path="useUpdateProfileManagers" element={<UseUpdateProfileManagers />} />
<Route path="useWhoActedOnPublication" element={<UseWhoActedOnPublication />} />
<Route path="useProfileActionHistory" element={<UseProfileActionHistory />} />
<Route path="useSetProfileMetadata" element={<UseSetProfileMetadata />} />
<Route path="useUpdateFollowPolicy" element={<UseUpdateFollowPolicy />} />
<Route path="useBlockProfiles" element={<UseBlockAndUnblockProfiles />} />
<Route path="useBlockedProfiles" element={<UseBlockedProfiles />} />
<Route path="useReportProfile" element={<UseReportProfile />} />
<Route path="useRecommendProfileToggle" element={<UseRecommendProfileToggle />} />
<Route path="useProfileInterests" element={<UseProfileInterests />} />
</Route>
<Route path="/profiles">
<Route index element={<ProfilesPage />} />
<Route path="useCreateProfile" element={<UseCreateProfile />} />
<Route path="useProfile" element={<UseProfile />} />
<Route path="useLazyProfile" element={<UseLazyProfile />} />
<Route path="useProfiles" element={<UseProfiles />} />
<Route path="useLazyProfiles" element={<UseLazyProfiles />} />
<Route path="useProfileFollowers" element={<UseProfileFollowers />} />
<Route path="useProfileFollowing" element={<UseProfileFollowing />} />
<Route path="useMutualFollowers" element={<UseMutualFollowers />} />
<Route path="useFollow" element={<UseFollowAndUnfollow />} />
<Route path="useProfileManagers" element={<UseProfileManagers />} />
<Route path="useUpdateProfileManagers" element={<UseUpdateProfileManagers />} />
<Route path="useWhoActedOnPublication" element={<UseWhoActedOnPublication />} />
<Route path="useProfileActionHistory" element={<UseProfileActionHistory />} />
<Route path="useSetProfileMetadata" element={<UseSetProfileMetadata />} />
<Route path="useUpdateFollowPolicy" element={<UseUpdateFollowPolicy />} />
<Route path="useBlockProfiles" element={<UseBlockAndUnblockProfiles />} />
<Route path="useBlockedProfiles" element={<UseBlockedProfiles />} />
<Route path="useReportProfile" element={<UseReportProfile />} />
<Route
path="useRecommendProfileToggle"
element={<UseRecommendProfileToggle />}
/>
<Route path="useProfileInterests" element={<UseProfileInterests />} />
</Route>

<Route path="/discovery">
<Route index element={<DiscoveryPage />} />
<Route path="useFeed" element={<UseFeed />} />
<Route path="useFeedHighlights" element={<UseFeedHighlights />} />
<Route path="useSearchPublications" element={<UseSearchPublications />} />
<Route path="useSearchProfiles" element={<UseSearchProfiles />} />
<Route path="useExploreProfiles" element={<UseExploreProfiles />} />
<Route path="useExplorePublications" element={<UseExplorePublications />} />
<Route path="useRecommendedProfiles" element={<UseRecommendedProfiles />} />
</Route>
<Route path="/discovery">
<Route index element={<DiscoveryPage />} />
<Route path="useFeed" element={<UseFeed />} />
<Route path="useFeedHighlights" element={<UseFeedHighlights />} />
<Route path="useSearchPublications" element={<UseSearchPublications />} />
<Route path="useSearchProfiles" element={<UseSearchProfiles />} />
<Route path="useExploreProfiles" element={<UseExploreProfiles />} />
<Route path="useExplorePublications" element={<UseExplorePublications />} />
<Route path="useRecommendedProfiles" element={<UseRecommendedProfiles />} />
</Route>

<Route path="/wallet">
<Route index element={<WalletPage />} />
<Route path="useProfilesManaged" element={<UseProfilesManaged />} />
<Route path="useOwnedHandles" element={<UseOwnedHandles />} />
<Route path="useLastLoggedInProfile" element={<UseLastLoggedInProfile />} />
<Route path="useRateLimits" element={<UseRateLimits />} />
</Route>
<Route path="/wallet">
<Route index element={<WalletPage />} />
<Route path="useProfilesManaged" element={<UseProfilesManaged />} />
<Route path="useOwnedHandles" element={<UseOwnedHandles />} />
<Route path="useLastLoggedInProfile" element={<UseLastLoggedInProfile />} />
<Route path="useRateLimits" element={<UseRateLimits />} />
</Route>

<Route path="/revenue">
<Route index element={<RevenuePage />} />
<Route path="useRevenueFromFollow" element={<UseRevenueFromFollow />} />
<Route path="useRevenueFromPublication" element={<UseRevenueFromPublication />} />
<Route
path="useRevenueFromPublications"
element={<UseRevenueFromPublications />}
/>
</Route>
<Route path="/revenue">
<Route index element={<RevenuePage />} />
<Route path="useRevenueFromFollow" element={<UseRevenueFromFollow />} />
<Route
path="useRevenueFromPublication"
element={<UseRevenueFromPublication />}
/>
<Route
path="useRevenueFromPublications"
element={<UseRevenueFromPublications />}
/>
</Route>

<Route path="/misc">
<Route index element={<MiscPage />} />
<Route path="useNotifications" element={<UseNotifications />} />
<Route path="useCurrencies" element={<UseCurrencies />} />
<Route path="useApproveModule" element={<UseApproveModule />} />
<Route path="useLatestPaidActions" element={<UseLatestPaidActions />} />
<Route path="useClaimHandle" element={<UseClaimHandle />} />
<Route path="useInviteWallets" element={<UseInviteWallets />} />
<Route path="useResolveAddress" element={<UseResolveAddress />} />
<Route path="useValidateHandle" element={<UseValidateHandle />} />
<Route
path="lensClientInteroperability"
element={<LensClientInteroperability />}
/>
<Route path="useSignFrameAction" element={<UseSignFrameAction />} />
</Route>
<Route path="/misc">
<Route index element={<MiscPage />} />
<Route path="useNotifications" element={<UseNotifications />} />
<Route path="useCurrencies" element={<UseCurrencies />} />
<Route path="useApproveModule" element={<UseApproveModule />} />
<Route path="useLatestPaidActions" element={<UseLatestPaidActions />} />
<Route path="useClaimHandle" element={<UseClaimHandle />} />
<Route path="useInviteWallets" element={<UseInviteWallets />} />
<Route path="useResolveAddress" element={<UseResolveAddress />} />
<Route path="useValidateHandle" element={<UseValidateHandle />} />
<Route
path="lensClientInteroperability"
element={<LensClientInteroperability />}
/>
<Route path="useSignFrameAction" element={<UseSignFrameAction />} />
</Route>

<Route
path="/inbox"
element={
<XMTPProvider>
<Outlet />
</XMTPProvider>
}
>
<Route index element={<InboxPage />} />
<Route path="useEnhanceConversations" element={<UseEnhanceConversations />} />
<Route
path="useEnhanceConversation/:conversationId"
element={<UseEnhanceConversation />}
/>
<Route path="useStartLensConversation" element={<UseStartLensConversation />} />
path="/inbox"
element={
<XMTPProvider>
<Outlet />
</XMTPProvider>
}
>
<Route index element={<InboxPage />} />
<Route path="useEnhanceConversations" element={<UseEnhanceConversations />} />
<Route
path="useEnhanceConversation/:conversationId"
element={<UseEnhanceConversation />}
/>
<Route path="useStartLensConversation" element={<UseStartLensConversation />} />
</Route>
</Route>
</Route>

<Route path="*" element={<p>Not found</p>} />
</Routes>
</GenericErrorBoundary>
<Route path="*" element={<p>Not found</p>} />
</Routes>
</GenericErrorBoundary>
</Suspense>
<Toaster />
</main>
</Router>
Expand Down
10 changes: 6 additions & 4 deletions examples/web/src/hooks/useInfiniteScroll.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { PaginatedReadResult } from '@lens-protocol/react-web';
import { RefCallback } from 'react';
import { SuspendablePaginatedResult } from '@lens-protocol/react-web';
import { RefCallback, startTransition } from 'react';
import { useInView } from 'react-cool-inview';

export function useInfiniteScroll<T, Q extends PaginatedReadResult<T> = PaginatedReadResult<T>>(
export function useInfiniteScroll<T, Q extends SuspendablePaginatedResult<T>>(
queryResult: Q,
): Q & { observeRef: RefCallback<unknown> } {
const { observe: observeRef } = useInView({
// Grow the root margin so the data will be loaded earlier
rootMargin: '20% 0px',
onEnter: async ({ unobserve, observe }) => {
unobserve();
await queryResult.next();
startTransition(() => {
queryResult.next();

Check failure on line 14 in examples/web/src/hooks/useInfiniteScroll.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
});
observe();
},
});
Expand Down
24 changes: 10 additions & 14 deletions examples/web/src/publications/UsePublications.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { PublicationType, profileId, usePublications } from '@lens-protocol/react-web';
import { useState } from 'react';
import { LimitType, PublicationType, profileId, usePublications } from '@lens-protocol/react-web';
import { startTransition, useState } from 'react';

import { PublicationCard } from '../components/cards';
import { ErrorMessage } from '../components/error/ErrorMessage';
import { Loading } from '../components/loading/Loading';
import { useInfiniteScroll } from '../hooks/useInfiniteScroll';

const allPublicationType = [PublicationType.Comment, PublicationType.Post, PublicationType.Mirror];
Expand All @@ -13,24 +11,24 @@ export function UsePublications() {

const {
data: publications,
beforeCount,
error,
loading,
hasMore,
prev,
observeRef,
} = useInfiniteScroll(
usePublications({
where: {
from: [profileId('0x06')],
from: [profileId('0x05')],
publicationTypes: publicationType,
},
limit: LimitType.Ten,
suspense: true,
}),
);

if (loading) return <Loading />;

if (error) return <ErrorMessage error={error} />;
const refresh = () =>
startTransition(() => {
prev();

Check failure on line 30 in examples/web/src/publications/UsePublications.tsx

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
});

return (
<div>
Expand Down Expand Up @@ -60,9 +58,7 @@ export function UsePublications() {
))}
</fieldset>

<button disabled={loading || beforeCount === 0} onClick={prev}>
Fetch newer
</button>
<button onClick={refresh}>Refresh</button>

{publications.map((publication) => (
<PublicationCard key={publication.id} publication={publication} />
Expand Down
24 changes: 12 additions & 12 deletions packages/react/src/authentication/useSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { EvmAddress, invariant, never } from '@lens-protocol/shared-kernel';
import { useEffect, useRef } from 'react';

import { useLensApolloClient } from '../helpers/arguments';
import { ReadResult, SuspenseEnabled, SuspenseResult } from '../helpers/reads';
import { ReadResult } from '../helpers/reads';
import { SuspenseEnabled, SuspenseResult } from '../helpers/suspense';
import { useLazyFragmentVariables } from '../helpers/variables';

export function usePreviousValue<T>(value: T) {
Expand Down Expand Up @@ -101,14 +102,7 @@ export type Session = AnonymousSession | ProfileSession | WalletOnlySession;
export type UseSessionArgs<TSuspense extends boolean> = SuspenseEnabled<TSuspense>;

/**
* `useSession` is a hook that lets you access the current {@link Session}
*
* @example
* ```ts
* const { data, error, loading } = useSession();
* ```
*
* ## Basic Usage
* Returns current {@link Session} data.
*
* Use this hook to determine if the user is authenticated or not.
* ```tsx
Expand Down Expand Up @@ -138,9 +132,15 @@ export type UseSessionArgs<TSuspense extends boolean> = SuspenseEnabled<TSuspens
* }
* ```
*
* ## Suspense Enabled
* @category Authentication
* @group Hooks
*/
export function useSession(args: UseSessionArgs<true>): SuspenseResult<Session>;

/**
* Returns current {@link Session} data.
*
* You can enable suspense mode to suspend the component until the session data is available.
* This signature supports [React Suspense](https://react.dev/reference/react/Suspense).
*
* ```tsx
* function Page() {
Expand Down Expand Up @@ -170,8 +170,8 @@ export type UseSessionArgs<TSuspense extends boolean> = SuspenseEnabled<TSuspens
* @category Authentication
* @group Hooks
*/
export function useSession(args: UseSessionArgs<true>): SuspenseResult<Session>;
export function useSession(args?: UseSessionArgs<never>): ReadResult<Session, UnspecifiedError>;

export function useSession(
args?: UseSessionArgs<boolean>,
): ReadResult<Session, UnspecifiedError> | SuspenseResult<Session> {
Expand Down
Loading

0 comments on commit 5a2b062

Please sign in to comment.