-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
react: useAddProfileInterests and useRemoveProfileInterests
- Loading branch information
Showing
12 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { | ||
useAddProfileInterests, | ||
useRemoveProfileInterests, | ||
ProfileInterestTypes, | ||
Profile, | ||
} from '@lens-protocol/react-web'; | ||
import { useMemo } from 'react'; | ||
|
||
import { RequireProfileSession } from '../components/auth'; | ||
|
||
type Interest = { | ||
parent: string; | ||
value: ProfileInterestTypes; | ||
label: string; | ||
isSubcategory?: boolean; | ||
}; | ||
|
||
function capitalize(label: string): string { | ||
return label | ||
.toLowerCase() | ||
.split(' ') | ||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) | ||
.join(' '); | ||
} | ||
|
||
function createInterests(categories: ProfileInterestTypes[]): Interest[] { | ||
const interests: Interest[] = []; | ||
|
||
categories.forEach((item) => { | ||
const parts = item.split('__'); | ||
const category = parts[0]; | ||
const subcategory = parts[1]; | ||
|
||
const label = capitalize( | ||
subcategory ? subcategory.replace(/_/g, ' ') : category.replace(/_/g, ' '), | ||
); | ||
|
||
if (subcategory) { | ||
interests.push({ | ||
parent: category, | ||
value: item, | ||
label: label, | ||
}); | ||
} else { | ||
if (!interests.some((interest) => interest.parent === category && !interest.isSubcategory)) { | ||
interests.push({ | ||
parent: category, | ||
value: item, | ||
label: label, | ||
isSubcategory: true, | ||
}); | ||
} | ||
} | ||
}); | ||
|
||
return interests; | ||
} | ||
|
||
function UseProfileInterestsInner({ profile }: { profile: Profile }) { | ||
const { execute: addInterests } = useAddProfileInterests(); | ||
const { execute: removeInterests } = useRemoveProfileInterests(); | ||
|
||
const groupedInterests = useMemo(() => { | ||
// transform ProfileInterestTypes to Interest[] | ||
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], idx) => ( | ||
<div key={idx}> | ||
<h3>{capitalize(category.replace(/_/g, ' '))}</h3> | ||
<div> | ||
{items.map((item, index) => ( | ||
<button | ||
key={index} | ||
onClick={() => handleClick(item.value)} | ||
style={profile.interests.includes(item.value) ? { fontWeight: 'bold' } : {}} | ||
> | ||
{item.label} | ||
</button> | ||
))} | ||
</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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
packages/domain/src/use-cases/profile/ManageProfileInterests.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { ProfileId } from '../../entities'; | ||
|
||
export type ProfileInterestsRequest = { | ||
profileId: ProfileId; | ||
}; | ||
|
||
export interface IProfileInterestsGateway { | ||
add(request: ProfileInterestsRequest): Promise<void>; | ||
remove(request: ProfileInterestsRequest): Promise<void>; | ||
} | ||
|
||
export interface IProfileInterestsPresenter { | ||
add(request: ProfileInterestsRequest): Promise<void>; | ||
remove(request: ProfileInterestsRequest): Promise<void>; | ||
} | ||
|
||
export class ManageProfileInterests { | ||
constructor( | ||
private readonly gateway: IProfileInterestsGateway, | ||
private readonly presenter: IProfileInterestsPresenter, | ||
) {} | ||
|
||
async add(request: ProfileInterestsRequest) { | ||
void this.gateway.add(request); | ||
await this.presenter.add(request); | ||
} | ||
|
||
async remove(request: ProfileInterestsRequest) { | ||
void this.gateway.remove(request); | ||
await this.presenter.remove(request); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
packages/react/src/profile/adapters/ProfileInterestsGateway.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { | ||
AddProfileInterestsData, | ||
AddProfileInterestsDocument, | ||
AddProfileInterestsVariables, | ||
ProfileInterestTypes, | ||
RemoveProfileInterestsData, | ||
RemoveProfileInterestsDocument, | ||
RemoveProfileInterestsVariables, | ||
SafeApolloClient, | ||
} from '@lens-protocol/api-bindings'; | ||
import { ProfileId } from '@lens-protocol/domain/entities'; | ||
import { IProfileInterestsGateway } from '@lens-protocol/domain/use-cases/profile'; | ||
|
||
export type ProfileInterestsRequest = { | ||
profileId: ProfileId; | ||
interests: ProfileInterestTypes[]; | ||
}; | ||
|
||
export class ProfileInterestsGateway implements IProfileInterestsGateway { | ||
constructor(private apolloClient: SafeApolloClient) {} | ||
|
||
async add(request: ProfileInterestsRequest) { | ||
await this.apolloClient.mutate<AddProfileInterestsData, AddProfileInterestsVariables>({ | ||
mutation: AddProfileInterestsDocument, | ||
variables: { | ||
request: { | ||
interests: request.interests, | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
async remove(request: ProfileInterestsRequest) { | ||
await this.apolloClient.mutate<RemoveProfileInterestsData, RemoveProfileInterestsVariables>({ | ||
mutation: RemoveProfileInterestsDocument, | ||
variables: { | ||
request: { | ||
interests: request.interests, | ||
}, | ||
}, | ||
}); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
packages/react/src/profile/adapters/ProfileInterestsPresenter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { ProfileInterestTypes } from '@lens-protocol/api-bindings'; | ||
import { IProfileInterestsPresenter } from '@lens-protocol/domain/use-cases/profile'; | ||
|
||
import { IProfileCacheManager } from './IProfileCacheManager'; | ||
import { ProfileInterestsRequest } from './ProfileInterestsGateway'; | ||
|
||
export class ProfileInterestsPresenter implements IProfileInterestsPresenter { | ||
constructor(private readonly profileCacheManager: IProfileCacheManager) {} | ||
|
||
async add(request: ProfileInterestsRequest) { | ||
this.profileCacheManager.update(request.profileId, (current) => { | ||
return { | ||
...current, | ||
interests: [...current.interests, ...request.interests], | ||
}; | ||
}); | ||
} | ||
|
||
async remove(request: ProfileInterestsRequest) { | ||
this.profileCacheManager.update(request.profileId, (current) => { | ||
return { | ||
...current, | ||
interests: current.interests.filter( | ||
(interest) => !request.interests.includes(interest as ProfileInterestTypes), | ||
), | ||
}; | ||
}); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
packages/react/src/profile/adapters/useProfileInterestsController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { ManageProfileInterests } from '@lens-protocol/domain/use-cases/profile'; | ||
|
||
import { useSharedDependencies } from '../../shared'; | ||
import { ProfileInterestsGateway, ProfileInterestsRequest } from './ProfileInterestsGateway'; | ||
import { ProfileInterestsPresenter } from './ProfileInterestsPresenter'; | ||
|
||
export function useProfileInterestsController() { | ||
const { apolloClient, profileCacheManager } = useSharedDependencies(); | ||
|
||
const add = async (request: ProfileInterestsRequest) => { | ||
const presenter = new ProfileInterestsPresenter(profileCacheManager); | ||
const gateway = new ProfileInterestsGateway(apolloClient); | ||
const manageInterests = new ManageProfileInterests(gateway, presenter); | ||
|
||
await manageInterests.add(request); | ||
}; | ||
|
||
const remove = async (request: ProfileInterestsRequest) => { | ||
const presenter = new ProfileInterestsPresenter(profileCacheManager); | ||
const gateway = new ProfileInterestsGateway(apolloClient); | ||
const manageInterests = new ManageProfileInterests(gateway, presenter); | ||
|
||
await manageInterests.remove(request); | ||
}; | ||
|
||
return { | ||
add, | ||
remove, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { ProfileInterestTypes } from '@lens-protocol/api-bindings'; | ||
import { invariant, success } from '@lens-protocol/shared-kernel'; | ||
|
||
import { SessionType, useSession } from '../authentication'; | ||
import { UseDeferredTask, useDeferredTask } from '../helpers/tasks'; | ||
import { useProfileInterestsController } from './adapters/useProfileInterestsController'; | ||
|
||
export { ProfileInterestTypes }; | ||
|
||
export type AddProfileInterestsArgs = { | ||
interests: ProfileInterestTypes[]; | ||
}; | ||
|
||
/** | ||
* Add profile interests. | ||
* | ||
* You MUST be authenticated via {@link useLogin} to use this hook. | ||
* | ||
* @category Profiles | ||
* @group Hooks | ||
*/ | ||
export function useAddProfileInterests(): UseDeferredTask<void, never, AddProfileInterestsArgs> { | ||
const { data: session } = useSession(); | ||
const { add } = useProfileInterestsController(); | ||
|
||
return useDeferredTask(async (request) => { | ||
invariant( | ||
session?.type === SessionType.WithProfile, | ||
'You must be authenticated with a profile to use this hook. Use `useLogin` hook to authenticate.', | ||
); | ||
|
||
await add({ | ||
profileId: session.profile.id, | ||
interests: request.interests, | ||
}); | ||
|
||
return success(); | ||
}); | ||
} |
Oops, something went wrong.