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: custom fragments support #1042

Open
wants to merge 5 commits into
base: next
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions examples/custom-fragments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Custom Fragments

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/custom-fragments)
18 changes: 18 additions & 0 deletions examples/custom-fragments/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
</head>
<body>
<h1>Custom Fragments</h1>
<div id="app">Loading...</div>
<script type="module">
import out from './index.ts';
document.querySelector('#app').innerHTML = Array.isArray(out)
? out.map((x) => `<div style="margin-bottom: 16px;">${x}</div>`).join('')
: out;
</script>
</body>
</html>
41 changes: 41 additions & 0 deletions examples/custom-fragments/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'viem/window';

import {
type FragmentOf,
PublicClient,
evmAddress,
graphql,
testnet as protocolTestnet,
} from '@lens-protocol/client';
import { fetchAccount } from '@lens-protocol/client/actions';

const MyAccountFragment = graphql(
`fragment Account on Account {
__typename
address
username {
value
}
metadata {
__typename
name
picture
}
}`,
);

type MyAccount = FragmentOf<typeof MyAccountFragment>;

const client = PublicClient.create({
environment: protocolTestnet,
accountFragment: MyAccountFragment,
});

const account: MyAccount | null = await fetchAccount(client, {
address: evmAddress('0x57b62a1571F4F09CDB4C3d93dA542bfe142D9F81'),
}).unwrapOr(null);

export default [
`<h2>${account?.username?.value}</h2>`,
`<pre>${JSON.stringify(account, null, 2)}</pre>`,
];
19 changes: 19 additions & 0 deletions examples/custom-fragments/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "example-custom-fragments",
"private": true,
"type": "module",
"scripts": {
"dev": "vite"
},
"dependencies": {
"@lens-network/sdk": "canary",
"@lens-protocol/client": "file:../../packages/client",
"@lens-protocol/metadata": "next",
"@lens-protocol/storage-node-client": "next",
"viem": "^2.21.55"
},
"devDependencies": {
"typescript": "^5.6.3",
"vite": "^5.4.11"
}
}
19 changes: 19 additions & 0 deletions examples/custom-fragments/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["ESNext", "DOM"],
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true
},
"include": ["./"]
}
29 changes: 18 additions & 11 deletions packages/client/src/actions/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ import type {
import {
AccountFeedsStatsQuery,
AccountGraphsStatsQuery,
AccountQuery,
AccountStatsQuery,
AccountsAvailableQuery,
AccountsBlockedQuery,
AccountsBulkQuery,
AccountsQuery,
BlockMutation,
CreateAccountWithUsernameMutation,
EnableSignlessMutation,
Expand All @@ -50,10 +48,13 @@ import {
UnblockMutation,
UndoRecommendAccountMutation,
UnmuteAccountMutation,
accountQuery,
accountsQuery,
} from '@lens-protocol/graphql';
import type { ResultAsync } from '@lens-protocol/types';

import type { AnyClient, SessionClient } from '../clients';
import type { Context } from '../context';
import type { UnauthenticatedError, UnexpectedError } from '../errors';

/**
Expand All @@ -71,11 +72,11 @@ import type { UnauthenticatedError, UnexpectedError } from '../errors';
* @param request - The Account query request.
* @returns The Account or `null` if it does not exist.
*/
export function fetchAccount(
client: AnyClient,
export function fetchAccount<TAccount extends Account>(
client: AnyClient<Context<TAccount>>,
request: AccountRequest,
): ResultAsync<Account | null, UnexpectedError> {
return client.query(AccountQuery, { request });
): ResultAsync<TAccount | null, UnexpectedError> {
return client.query(accountQuery([client.context.accountFragment]), { request });
}

/**
Expand All @@ -84,18 +85,24 @@ export function fetchAccount(
* Using a {@link SessionClient} will yield {@link Account#operations} specific to the authenticated Account.
*
* ```ts
* const result = await fetchAccounts(anyClient);
* const result = await fetchAccounts(anyClient, {
* filter: {
* searchBy: {
* localNameQuery: 'stani',
* }
* }
* });
* ```
*
* @param client - Any Lens client.
* @param request - The query request.
* @returns The list of accounts.
*/
export function fetchAccounts(
client: AnyClient,
export function fetchAccounts<TAccount extends Account>(
client: AnyClient<Context<TAccount>>,
request: AccountsRequest = {},
): ResultAsync<Paginated<Account> | null, UnexpectedError> {
return client.query(AccountsQuery, { request });
): ResultAsync<Paginated<TAccount> | null, UnexpectedError> {
return client.query(accountsQuery([client.context.accountFragment]), { request });
}

/**
Expand Down
12 changes: 7 additions & 5 deletions packages/client/src/actions/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import {
AuthenticatedSessionsQuery,
CurrentSessionQuery,
LegacyRolloverRefreshMutation,
MeQuery,
RefreshMutation,
RevokeAuthenticationMutation,
SwitchAccountMutation,
meQuery,
} from '@lens-protocol/graphql';
import type { ResultAsync } from '@lens-protocol/types';

import type { Account } from '@lens-protocol/graphql';
import type { AnyClient, SessionClient } from '../clients';
import type { Context } from '../context';
import type { UnauthenticatedError, UnexpectedError } from '../errors';

/**
Expand Down Expand Up @@ -151,8 +153,8 @@ export function switchAccount(
* @param client - The session client for the authenticated Account.
* @returns The details of the authenticated Account.
*/
export function fetchMeDetails(
client: SessionClient,
): ResultAsync<MeResult, UnauthenticatedError | UnexpectedError> {
return client.query(MeQuery, {});
export function fetchMeDetails<TAccount extends Account>(
client: SessionClient<Context<TAccount>>,
): ResultAsync<MeResult<TAccount>, UnauthenticatedError | UnexpectedError> {
return client.query(meQuery([client.context.accountFragment]), {});
}
43 changes: 24 additions & 19 deletions packages/client/src/actions/posts.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
import type {
Account,
AccountPostReaction,
ActionInfo,
AnyPost,
Paginated,
Post,
PostActionsRequest,
PostBookmarksRequest,
PostEdit,
PostEditsRequest,
PostFields,
PostReactionStatus,
PostReactionStatusRequest,
PostReactionsRequest,
PostReferencesRequest,
PostRequest,
PostTagsRequest,
PostsRequest,
WhoActedOnPostQueryRequest,
WhoReferencedPostRequest,
} from '@lens-protocol/graphql';
import {
PostActionsQuery,
PostBookmarksQuery,
PostEditsQuery,
PostQuery,
PostReactionStatusQuery,
PostReactionsQuery,
PostReferencesQuery,
PostTagsQuery,
PostsQuery,
WhoActedOnPostQuery,
WhoReferencedPostQuery,
postBookmarksQuery,
postQuery,
postReferencesQuery,
postsQuery,
} from '@lens-protocol/graphql';
import type { ResultAsync } from '@lens-protocol/types';

import type { PostTagsRequest } from '@lens-protocol/graphql';
import type { PostReactionStatusRequest } from '@lens-protocol/graphql';
import type { PostReactionStatus } from '@lens-protocol/graphql';
import type { WhoReferencedPostRequest } from '@lens-protocol/graphql';
import type { Account } from '@lens-protocol/graphql';
import type { WhoActedOnPostQueryRequest } from '@lens-protocol/graphql';
import type { PostEditsRequest } from '@lens-protocol/graphql';
import type { PostEdit } from '@lens-protocol/graphql';
import type { AnyClient, SessionClient } from '../clients';
import type { Context } from '../context';
import type { UnauthenticatedError, UnexpectedError } from '../errors';

/**
Expand All @@ -53,11 +55,14 @@ import type { UnauthenticatedError, UnexpectedError } from '../errors';
* @param request - The query request.
* @returns The Post or `null` if it does not exist.
*/
export function fetchPost(
client: AnyClient,
export function fetchPost<TAccount extends Account, TPostFields extends PostFields>(
client: AnyClient<Context<TAccount, TPostFields>>,
request: PostRequest,
): ResultAsync<AnyPost | null, UnexpectedError> {
return client.query(PostQuery, { request });
): ResultAsync<AnyPost<TPostFields, TAccount> | null, UnexpectedError> {
return client.query(
postQuery([client.context.postFieldsFragment, client.context.accountFragment]),
{ request },
);
}

/**
Expand All @@ -82,7 +87,7 @@ export function fetchPosts(
client: AnyClient,
request: PostsRequest,
): ResultAsync<Paginated<AnyPost>, UnexpectedError> {
return client.query(PostsQuery, { request });
return client.query(postsQuery, { request });
}

/**
Expand Down Expand Up @@ -138,7 +143,7 @@ export function fetchPostBookmarks(
client: SessionClient,
request: PostBookmarksRequest = {},
): ResultAsync<Paginated<AnyPost>, UnexpectedError | UnauthenticatedError> {
return client.query(PostBookmarksQuery, { request });
return client.query(postBookmarksQuery, { request });
}

/**
Expand All @@ -158,7 +163,7 @@ export function fetchPostReferences(
client: AnyClient,
request: PostReferencesRequest,
): ResultAsync<Paginated<AnyPost>, UnexpectedError | UnauthenticatedError> {
return client.query(PostReferencesQuery, { request });
return client.query(postReferencesQuery, { request });
}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/actions/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
TimelineItem,
TimelineRequest,
} from '@lens-protocol/graphql';
import { TimelineHighlightsQuery, TimelineQuery } from '@lens-protocol/graphql';
import { timelineHighlightsQuery, timelineQuery } from '@lens-protocol/graphql';
import type { ResultAsync } from '@lens-protocol/types';

import type { AnyClient } from '../clients';
Expand All @@ -28,7 +28,7 @@ export function fetchTimeline(
client: AnyClient,
request: TimelineRequest,
): ResultAsync<Paginated<TimelineItem> | null, UnexpectedError> {
return client.query(TimelineQuery, { request });
return client.query(timelineQuery, { request });
}

/**
Expand All @@ -48,5 +48,5 @@ export function fetchTimelineHighlights(
client: AnyClient,
request: TimelineHighlightsRequest,
): ResultAsync<Paginated<AnyPost>, UnexpectedError> {
return client.query(TimelineHighlightsQuery, { request });
return client.query(timelineHighlightsQuery, { request });
}
6 changes: 4 additions & 2 deletions packages/client/src/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { type AuthConfig, authExchange } from '@urql/exchange-auth';
import { type AuthenticatedUser, authenticatedUser } from './AuthenticatedUser';
import { switchAccount, transactionStatus } from './actions';
import type { ClientConfig } from './config';
import { type Context, configureContext } from './context';
import { type Context, type ContextFrom, configureContext } from './context';
import {
AuthenticationError,
GraphQLErrorCode,
Expand Down Expand Up @@ -151,7 +151,9 @@ export class PublicClient<TContext extends Context = Context> extends AbstractCl
* @param options - The options to configure the client.
* @returns The new instance of the client.
*/
static create(options: ClientConfig): PublicClient<Context> {
static create<TConfig extends ClientConfig>(
options: TConfig,
): PublicClient<ContextFrom<TConfig>> {
return new PublicClient(configureContext(options));
}

Expand Down
20 changes: 18 additions & 2 deletions packages/client/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { EnvironmentConfig } from '@lens-protocol/env';
import type { Account, PostFields } from '@lens-protocol/graphql';
import type { FragmentDocumentFor } from '@lens-protocol/graphql';
import type { IStorageProvider } from '@lens-protocol/storage';

/**
* The client configuration.
*/
export type ClientConfig = {
export type ClientConfig<
TAccount extends Account = Account,
TPostFields extends PostFields = PostFields,
> = {
/**
* The environment configuration to use (e.g. `mainnet`, `testnet`).
*/
Expand All @@ -27,11 +32,22 @@ export type ClientConfig = {
* Use this to set the `Origin` header for requests from non-browser environments.
*/
origin?: string;

/**
* The storage provider to use.
*
* @defaultValue {@link InMemoryStorageProvider}
*/
storage?: IStorageProvider;
/**
* The Account Fragment to use.
*
* @defaultValue {@link AccountFragment}
*/
accountFragment?: FragmentDocumentFor<TAccount, 'Account'>;
/**
* The Post Fragment to use.
*
* @defaultValue {@link PostFragment}
*/
postFieldsFragment?: FragmentDocumentFor<TPostFields, 'Post', 'PostFields'>;
};
Loading
Loading