Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: authentication domain changes #563

Merged
merged 6 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/hot-hornets-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@lens-protocol/shared-kernel": patch
"@lens-protocol/api-bindings": patch
"@lens-protocol/domain": patch
"@lens-protocol/react": patch
---

**feat:** new `useLogin` and `useSession` hooks
66 changes: 37 additions & 29 deletions examples/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LensConfig, LensProvider, development } from '@lens-protocol/react';
import { bindings as wagmiBindings } from '@lens-protocol/wagmi';
import { XMTPProvider } from '@xmtp/react-sdk';
import { Toaster } from 'react-hot-toast';
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';
Expand All @@ -7,8 +8,9 @@ import { polygonMumbai } from 'wagmi/chains';
import { InjectedConnector } from 'wagmi/connectors/injected';
import { publicProvider } from 'wagmi/providers/public';

import { Home } from './HomePage';
import { Breadcrumbs } from './components/Breadcrumbs';
import { HomePage } from './HomePage';
import { Layout } from './Layout';
import { LogInPage } from './LogInPage';
import { GenericErrorBoundary } from './components/GenericErrorBoundary';
import { ErrorMessage } from './components/error/ErrorMessage';
import { Header } from './components/header/Header';
Expand Down Expand Up @@ -56,6 +58,7 @@ const config = createConfig({
const lensConfig: LensConfig = {
environment: development,
storage: localStorage(),
bindings: wagmiBindings(),
};

export function App() {
Expand All @@ -66,38 +69,43 @@ export function App() {
<Router>
<Header />
<main>
<Breadcrumbs />
<GenericErrorBoundary fallback={ErrorMessage}>
<Routes>
<Route index element={<Home />} />
<Route index element={<HomePage />} />
<Route path="/login" element={<LogInPage />} />

<Route path="/publications">
<Route index element={<PublicationsPage />} />
<Route path="usePublication" element={<UsePublication />} />
<Route path="usePublications" element={<UsePublications />} />
<Route
path="useWhoReactedToPublication"
element={<UseWhoReactedToPublication />}
/>
</Route>
<Route element={<Layout />}>
<Route path="/publications">
<Route index element={<PublicationsPage />} />
<Route path="usePublication" element={<UsePublication />} />
<Route path="usePublications" element={<UsePublications />} />
<Route
path="useWhoReactedToPublication"
element={<UseWhoReactedToPublication />}
/>
</Route>

<Route path="/profiles">
<Route index element={<ProfilesPage />} />
<Route path="useProfile" element={<UseProfile />} />
<Route path="useProfiles" element={<UseProfiles />} />
<Route path="useProfileFollowers" element={<UseProfileFollowers />} />
<Route path="useProfileFollowing" element={<UseProfileFollowing />} />
<Route path="useMutualFollowers" element={<UseMutualFollowers />} />
<Route path="useRecommendedProfiles" element={<UseRecommendedProfiles />} />
<Route path="useWhoActedOnPublication" element={<UseWhoActedOnPublication />} />
<Route path="useProfileActionHistory" element={<UseProfileActionHistory />} />
</Route>
<Route path="/profiles">
<Route index element={<ProfilesPage />} />
<Route path="useProfile" element={<UseProfile />} />
<Route path="useProfiles" element={<UseProfiles />} />
<Route path="useProfileFollowers" element={<UseProfileFollowers />} />
<Route path="useProfileFollowing" element={<UseProfileFollowing />} />
<Route path="useMutualFollowers" element={<UseMutualFollowers />} />
<Route path="useRecommendedProfiles" element={<UseRecommendedProfiles />} />
<Route
path="useWhoActedOnPublication"
element={<UseWhoActedOnPublication />}
/>
<Route path="useProfileActionHistory" element={<UseProfileActionHistory />} />
</Route>

<Route path="/discovery">
<Route index element={<DiscoveryPage />} />
<Route path="useFeed" element={<UseFeed />} />
<Route path="useSearchPublications" element={<UseSearchPublications />} />
<Route path="useSearchProfiles" element={<UseSearchProfiles />} />
<Route path="/discovery">
<Route index element={<DiscoveryPage />} />
<Route path="useFeed" element={<UseFeed />} />
<Route path="useSearchPublications" element={<UseSearchPublications />} />
<Route path="useSearchProfiles" element={<UseSearchProfiles />} />
</Route>
</Route>

<Route path="*" element={<p>Not found</p>} />
Expand Down
22 changes: 9 additions & 13 deletions examples/web/src/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@ import { Link } from 'react-router-dom';

import { CATEGORIES } from './config';

export function Home() {
export function HomePage() {
return (
<>
<h1>Home</h1>

<div>
{CATEGORIES.map(({ path, label }) => (
<article key={path}>
<h2>{label}</h2>
<Link to={path}>View</Link>
</article>
))}
</div>
</>
<div>
{CATEGORIES.map(({ path, label }) => (
<article key={path}>
<h2>{label}</h2>
<Link to={path}>View</Link>
</article>
))}
</div>
);
}
12 changes: 12 additions & 0 deletions examples/web/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Outlet } from 'react-router-dom';

import { Breadcrumbs } from './components/Breadcrumbs';

export function Layout() {
return (
<>
<Breadcrumbs />
<Outlet />
</>
);
}
95 changes: 95 additions & 0 deletions examples/web/src/LogInPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { ProfileId, useLogin, useProfiles } from '@lens-protocol/react';
import toast from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
import { useAccount, useConnect } from 'wagmi';
import { InjectedConnector } from 'wagmi/connectors/injected';

import { ErrorMessage } from './components/error/ErrorMessage';
import { Loading } from './components/loading/Loading';
import { never } from './utils';

function ProfilesList({ owner }: { owner: string }) {
const navigate = useNavigate();
const { execute: login, isPending: isLoginPending } = useLogin();
const { data: profiles, error, loading } = useProfiles({ where: { ownedBy: [owner] } });

const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

const form = event.currentTarget;
const formData = new FormData(form);

const profileId = (formData.get('id') as string) ?? never();

const result = await login({
address: owner,
profileId: profileId as ProfileId,
});

if (result.isSuccess()) {
toast.success(`Welcome ${String(result.value.handle)}`);
return navigate('/');
}

toast.error(result.error.message);
};

if (loading) {
return <Loading />;
}

if (error) {
return <ErrorMessage error={error} />;
}

if (profiles.length === 0) {
return <p>No profiles on this wallet.</p>;
}

return (
<form onSubmit={onSubmit}>
<fieldset>
<legend>Which Profile you want to log-in with?</legend>

{profiles.map((profile, idx) => (
<label key={profile.id}>
<input
disabled={isLoginPending}
type="radio"
defaultChecked={idx === 0}
name="id"
value={profile.id}
/>
{profile.handle}
</label>
))}

<div>
<button disabled={isLoginPending} type="submit">
Continue
</button>
</div>
</fieldset>
</form>
);
}

export function LogInPage() {
const { address, isConnected, isConnecting } = useAccount();

const { connect } = useConnect({
connector: new InjectedConnector(),
});

return (
<div>
{!isConnected && (
<button disabled={isConnecting} onClick={() => connect()}>
Connect first
</button>
)}

{address && <ProfilesList owner={address} />}
</div>
);
}
6 changes: 6 additions & 0 deletions examples/web/src/components/auth/LoginButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Link } from 'react-router-dom';

// TODO render log out button once useSession is implemented
export function LoginButton() {
return <Link to="/login">Log in</Link>;
}
1 change: 1 addition & 0 deletions examples/web/src/components/auth/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './LoginButton';
15 changes: 14 additions & 1 deletion examples/web/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { SessionType, useSession } from '@lens-protocol/react';
import { NavLink } from 'react-router-dom';

import { CATEGORIES } from '../../config';
import { LoginButton } from '../auth';

export function Header() {
const { data: session } = useSession();
return (
<header>
<div
Expand All @@ -25,7 +28,17 @@ export function Header() {
justifyContent: 'space-between',
gap: '1rem',
}}
></div>
>
{session?.type === SessionType.WithProfile && (
<strong>
{session.profile.metadata?.displayName ??
session.profile.handle ??
session.profile.id}
</strong>
)}

<LoginButton />
</div>
</div>

<nav>
Expand Down
21 changes: 2 additions & 19 deletions packages/api-bindings/src/apollo/cache/__helpers__/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { ApolloCache, NormalizedCacheObject, ReactiveVar } from '@apollo/client';
import { ApolloCache, NormalizedCacheObject } from '@apollo/client';
import { faker } from '@faker-js/faker';
import { mockCreatePostRequest, mockWalletData } from '@lens-protocol/domain/mocks';
import { ProfileIdentifier, WalletData } from '@lens-protocol/domain/use-cases/lifecycle';
import { mockCreatePostRequest } from '@lens-protocol/domain/mocks';
import { AnyTransactionRequest } from '@lens-protocol/domain/use-cases/transactions';

import { createLensCache } from '../createLensCache';
import { authenticatedProfile, authenticatedWallet, updateSession } from '../session';
import { TransactionState, TxStatus } from '../transactions';

export type MockCacheConfiguration = {
activeWalletVar?: ReactiveVar<WalletData | null>;
};

export function mockLensCache(): ApolloCache<NormalizedCacheObject> {
return createLensCache();
}
Expand All @@ -26,14 +20,3 @@ export function mockTransactionState<T extends AnyTransactionRequest>(
...partial,
};
}

export function simulateAuthenticatedWallet(wallet = mockWalletData()) {
updateSession(authenticatedWallet(wallet));
}

export function simulateAuthenticatedProfile(
profile: ProfileIdentifier,
wallet = mockWalletData(),
) {
updateSession(authenticatedProfile(wallet, profile));
}
Loading