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

chore: prepare release react 2.2.0 #933

Merged
merged 16 commits into from
May 3, 2024
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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"eslint.workingDirectories": [
"examples/nextjs",
"examples/lens-next-app",
"examples/node",
"examples/react-native",
"examples/shared",
"examples/web",
"packages/api-bindings",
"packages/blockchain-bindings",
"packages/cli",
"packages/client",
"packages/domain",
"packages/eslint-config",
Expand Down
66 changes: 66 additions & 0 deletions examples/node/scripts/wallet/hideManagedProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { LensClient, ManagedProfileVisibility, development } from '@lens-protocol/client';

import { setupWallet } from '../shared/setupWallet';

/**
* Notice!
* Hide managed profile feature works only for managed profiles that are not owned by the wallet.
*/

async function fetchManagedNotOwnedProfiles(client: LensClient, address: string) {
const result = await client.wallet.profilesManaged({
for: address,
includeOwned: false, // important!
hiddenFilter: ManagedProfileVisibility.NoneHidden,
});

console.log(
`Profiles managed by ${address}: `,
result.items.map((item) => ({
id: item.id,
handle: item.handle,
})),
);

return result.items;
}

async function main() {
const client = new LensClient({
environment: development,
});

const wallet = setupWallet();
const address = await wallet.getAddress();

const profiles = await fetchManagedNotOwnedProfiles(client, address);

if (profiles.length === 0) {
console.log('No managed profiles found');
process.exit(0);
}

const profileIdToHide = profiles[0].id;

// Hide the first managed profile
console.log(`Hiding profile ${profileIdToHide} from managed profiles list`);

await client.wallet.hideManagedProfile({
profileId: profileIdToHide,
});

// Fetch managed profiles again
await fetchManagedNotOwnedProfiles(client, address);

// Unhide the profile
console.log(`Unhiding profile ${profileIdToHide}`);

await client.wallet.unhideManagedProfile({
profileId: profileIdToHide,
});

// Fetch managed profiles again
await fetchManagedNotOwnedProfiles(client, address);
}

main();
2 changes: 1 addition & 1 deletion examples/node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es6"
"target": "ESNext"
},
"include": ["scripts"],
"ts-node": {
Expand Down
2 changes: 2 additions & 0 deletions examples/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
UseProfileActionHistory,
UseProfileFollowers,
UseProfileFollowing,
UseProfileInterests,
UseProfileManagers,
UseProfiles,
UseRecommendProfileToggle,
Expand Down Expand Up @@ -151,6 +152,7 @@ export function App() {
<Route path="useBlockedProfiles" element={<UseBlockedProfiles />} />
<Route path="useReportProfile" element={<UseReportProfile />} />
<Route path="useRecommendProfileToggle" element={<UseRecommendProfileToggle />} />
<Route path="useProfileInterests" element={<UseProfileInterests />} />
</Route>

<Route path="/discovery">
Expand Down
5 changes: 5 additions & 0 deletions examples/web/src/profiles/ProfilesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ const profileHooks = [
description: 'Recommend a profile.',
path: '/profiles/useRecommendProfileToggle',
},
{
label: 'useProfileInterests',
description: 'Add and remove profile interests.',
path: '/profiles/useProfileInterests',
},
];

export function ProfilesPage() {
Expand Down
40 changes: 34 additions & 6 deletions examples/web/src/profiles/UseProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,43 @@
import { profileId, useProfile } from '@lens-protocol/react-web';
import { useProfile } from '@lens-protocol/react-web';
import { Suspense, startTransition, useState } from 'react';

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

export function UseProfileInner({ localName }: { localName: string }) {
const { data, error } = useProfile({ forHandle: `lens/${localName}`, suspense: true });

if (error) {
return <p>Profile not found.</p>;
}

return <ProfileCard profile={data} />;
}

export function UseProfile() {
const { data: profile, error, loading } = useProfile({ forProfileId: profileId('0x01') });
const [localName, setLocalName] = useState('brainjammer');

const update = (event: React.ChangeEvent<HTMLInputElement>) =>
startTransition(() => {
if (event.target.value.length > 0) {
setLocalName(event.target.value);
}
});

if (loading) return <Loading />;
return (
<div>
<h1>
<code>useProfile</code>
</h1>

if (error) return <ErrorMessage error={error} />;
<label>
lens/
<input type="text" name="localName" defaultValue={localName} onChange={update} />
</label>

return <ProfileCard profile={profile} />;
<Suspense fallback={<Loading />}>
<UseProfileInner localName={localName} />
</Suspense>
</div>
);
}
122 changes: 122 additions & 0 deletions examples/web/src/profiles/UseProfileInterests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
useAddProfileInterests,
useRemoveProfileInterests,
ProfileInterestTypes,
Profile,
} from '@lens-protocol/react-web';
import { Fragment, useMemo } from 'react';

import { RequireProfileSession } from '../components/auth';

// Capitalizes each word in a string
function capitalize(label: string): string {
return label.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase());
}

type Interest = {
parent: string;
value: ProfileInterestTypes;
label: string;
};

// Processes raw interest types into structured interests array
function createInterests(categories: ProfileInterestTypes[]): Interest[] {
return categories.map((item) => {
const [parent, subcategory] = item.split('__');
const label = capitalize(
subcategory ? subcategory.replace(/_/g, ' ') : parent.replace(/_/g, ' '),
);
return { parent, value: item, label };
});
}

type ButtonProps = {
isActive: boolean;
onClick: () => void;
children: React.ReactNode;
};

function ToggleButton({ isActive, onClick, children }: ButtonProps) {
const normalStyle = {
backgroundColor: 'transparent',
border: '1px solid grey',
color: '#111',
outline: 'none',
};

const activeStyle = {
...normalStyle,
backgroundColor: '#333',
color: '#eee',
};

return (
<button style={isActive ? activeStyle : normalStyle} onClick={onClick}>
{children}
</button>
);
}

function UseProfileInterestsInner({ profile }: { profile: Profile }) {
const { execute: addInterests } = useAddProfileInterests();
const { execute: removeInterests } = useRemoveProfileInterests();

const groupedInterests = useMemo(() => {
const interests = createInterests(Object.values(ProfileInterestTypes));

// Group interests by category
return interests.reduce((acc, interest) => {
acc[interest.parent] = acc[interest.parent] || [];
acc[interest.parent].push(interest);
return acc;
}, {} as Record<string, Interest[]>);
}, []);

const handleClick = async (interest: ProfileInterestTypes) => {
const request = {
interests: [interest],
};

if (profile.interests.includes(interest)) {
await removeInterests(request);
} else {
await addInterests(request);
}
};

return (
<div>
{Object.entries(groupedInterests).map(([category, items]) => (
<div key={category}>
<h4>{capitalize(category.replace(/_/g, ' '))}</h4>
<div>
{items.map((item) => (
<Fragment key={item.value}>
<ToggleButton
onClick={() => handleClick(item.value)}
isActive={profile.interests.includes(item.value)}
>
{item.label}
</ToggleButton>{' '}
</Fragment>
))}
</div>
</div>
))}
</div>
);
}

export function UseProfileInterests() {
return (
<div>
<h2>
<code>useAddProfileInterests & useRemoveProfileInterests</code>
</h2>

<RequireProfileSession message="Log in to view this example.">
{({ profile }) => <UseProfileInterestsInner profile={profile} />}
</RequireProfileSession>
</div>
);
}
1 change: 1 addition & 0 deletions examples/web/src/profiles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './UseProfile';
export * from './UseProfileActionHistory';
export * from './UseProfileFollowers';
export * from './UseProfileFollowing';
export * from './UseProfileInterests';
export * from './UseProfileManagers';
export * from './UseProfiles';
export * from './UseRecommendProfileToggle';
Expand Down
9 changes: 9 additions & 0 deletions packages/api-bindings/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# @lens-protocol/api-bindings

## 0.12.1

### Patch Changes

- 2edd76361: **feat:** added globalStats alias to publication and profile stats
- Updated dependencies [b1e474862]
- Updated dependencies [1e6b96c67]
- @lens-protocol/[email protected]

## 0.12.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/api-bindings/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lens-protocol/api-bindings",
"version": "0.12.0",
"version": "0.12.1",
"description": "Graphql fragments, react hooks, typescript types of lens API.",
"repository": {
"directory": "packages/api-bindings",
Expand Down
11 changes: 9 additions & 2 deletions packages/api-bindings/src/lens/__helpers__/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export function mockProfileFragment(overrides?: Partial<gql.Profile>): gql.Profi
metadata: null,
invitedBy: null,
stats: mockProfileStatsFragment(),
globalStats: mockProfileStatsFragment(),
peerToPeerRecommendedByMe: false,

...overrides,
Expand All @@ -138,8 +139,11 @@ export function mockProfileFragment(overrides?: Partial<gql.Profile>): gql.Profi
}

export function mockPostFragment(overrides?: Partial<Omit<gql.Post, '__typename'>>): gql.Post {
const publicationId = mockPublicationId();
const stats = mockPublicationStatsFragment({ id: publicationId });

return {
id: mockPublicationId(),
id: publicationId,
isHidden: false,
txHash: mockTransactionHash(),
by: mockProfileFragment(),
Expand All @@ -150,7 +154,8 @@ export function mockPostFragment(overrides?: Partial<Omit<gql.Post, '__typename'
metadata: mockPublicationTextOnlyMetadata(),
openActionModules: [],
referenceModule: null,
stats: mockPublicationStatsFragment(),
stats,
globalStats: stats,
isEncrypted: false,
hashtagsMentioned: [],
profilesMentioned: [],
Expand Down Expand Up @@ -182,6 +187,7 @@ export function mockCommentFragment(
commentOn: mainPost,
firstComment: null,
stats: mockPublicationStatsFragment(),
globalStats: mockPublicationStatsFragment(),
isEncrypted: false,
hashtagsMentioned: [],
profilesMentioned: [],
Expand All @@ -206,6 +212,7 @@ export function mockQuoteFragment(overrides?: Partial<Omit<gql.Quote, '__typenam
referenceModule: null,
quoteOn: mockPostFragment(),
stats: mockPublicationStatsFragment(),
globalStats: mockPublicationStatsFragment(),
isEncrypted: false,
hashtagsMentioned: [],
profilesMentioned: [],
Expand Down
Loading
Loading