diff --git a/app/(general)/integration/lens-protocol/explore/page.tsx b/app/(general)/integration/lens-protocol/explore/page.tsx new file mode 100644 index 00000000..e8aa4451 --- /dev/null +++ b/app/(general)/integration/lens-protocol/explore/page.tsx @@ -0,0 +1,13 @@ +"use client" + +import { ExploreProfiles } from "@/integrations/lens-protocol/components/profile/explore-profiles" +import { ExplorePublications } from "@/integrations/lens-protocol/components/publications/explore-publications" + +export default function PageIntegration() { + return ( + <> + + + + ) +} diff --git a/app/(general)/integration/lens-protocol/layout.tsx b/app/(general)/integration/lens-protocol/layout.tsx new file mode 100644 index 00000000..1e851aab --- /dev/null +++ b/app/(general)/integration/lens-protocol/layout.tsx @@ -0,0 +1,75 @@ +"use client" + +import { ReactNode } from "react" +import Link from "next/link" +import { turboIntegrations } from "@/data/turbo-integrations" +import { LensProvider } from "@lens-protocol/react-web" +import { LuBook } from "react-icons/lu" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" +import { WalletConnect } from "@/components/blockchain/wallet-connect" +import { + PageHeader, + PageHeaderCTA, + PageHeaderDescription, + PageHeaderHeading, +} from "@/components/layout/page-header" +import { PageSection } from "@/components/layout/page-section" +import { IsWalletConnected } from "@/components/shared/is-wallet-connected" +import { IsWalletDisconnected } from "@/components/shared/is-wallet-disconnected" +import { LightDarkImage } from "@/components/shared/light-dark-image" +import { Navbar } from "@/integrations/lens-protocol/components/navbar" +import { lensProviderConfig } from "@/integrations/lens-protocol/lens-provider" + +export default function LayoutIntegration({ + children, +}: { + children: ReactNode +}) { + return ( + <> + + + + {turboIntegrations.lensProtocol.name} + + + {turboIntegrations.lensProtocol.description} + + + + + Documentation + + + + + + + + + +
+ +
+ {children} +
+
+
+
+
+ + ) +} diff --git a/app/(general)/integration/lens-protocol/opengraph-image.tsx b/app/(general)/integration/lens-protocol/opengraph-image.tsx new file mode 100644 index 00000000..657902a0 --- /dev/null +++ b/app/(general)/integration/lens-protocol/opengraph-image.tsx @@ -0,0 +1,9 @@ +import { IntegrationOgImage } from "@/components/ui/social/og-image-integrations" + +export const runtime = "edge" +export const size = { + width: 1200, + height: 630, +} + +export default IntegrationOgImage("lensProtocol") diff --git a/app/(general)/integration/lens-protocol/page.tsx b/app/(general)/integration/lens-protocol/page.tsx new file mode 100644 index 00000000..bbcc659c --- /dev/null +++ b/app/(general)/integration/lens-protocol/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation" + +export default function PageIntegration() { + redirect("/integration/lens-protocol/profiles") +} diff --git a/app/(general)/integration/lens-protocol/profiles/[handle]/page.tsx b/app/(general)/integration/lens-protocol/profiles/[handle]/page.tsx new file mode 100644 index 00000000..539ac4ea --- /dev/null +++ b/app/(general)/integration/lens-protocol/profiles/[handle]/page.tsx @@ -0,0 +1,11 @@ +"use client" + +import { Profile } from "@/integrations/lens-protocol/components/profile/profile" + +export default function PageIntegration({ + params, +}: { + params: { handle: string } +}) { + return +} diff --git a/app/(general)/integration/lens-protocol/profiles/address/[address]/page.tsx b/app/(general)/integration/lens-protocol/profiles/address/[address]/page.tsx new file mode 100644 index 00000000..8eccd9b1 --- /dev/null +++ b/app/(general)/integration/lens-protocol/profiles/address/[address]/page.tsx @@ -0,0 +1,11 @@ +"use client" + +import { AddressProfiles } from "@/integrations/lens-protocol/components/profile/address-profiles" + +export default function PageIntegration({ + params, +}: { + params: { address: string } +}) { + return +} diff --git a/app/(general)/integration/lens-protocol/profiles/page.tsx b/app/(general)/integration/lens-protocol/profiles/page.tsx new file mode 100644 index 00000000..f73d62c5 --- /dev/null +++ b/app/(general)/integration/lens-protocol/profiles/page.tsx @@ -0,0 +1,7 @@ +"use client" + +import { OwnedProfiles } from "@/integrations/lens-protocol/components/profile/owned-profiles" + +export default function PageIntegration() { + return +} diff --git a/app/(general)/integration/lens-protocol/publications/[id]/page.tsx b/app/(general)/integration/lens-protocol/publications/[id]/page.tsx new file mode 100644 index 00000000..a9c16c30 --- /dev/null +++ b/app/(general)/integration/lens-protocol/publications/[id]/page.tsx @@ -0,0 +1,13 @@ +"use client" + +import { PublicationId } from "@lens-protocol/react-web" + +import { Publication } from "@/integrations/lens-protocol/components/publications/publication" + +export default function PageIntegration({ + params, +}: { + params: { id: string } +}) { + return +} diff --git a/app/(general)/integration/lens-protocol/search/page.tsx b/app/(general)/integration/lens-protocol/search/page.tsx new file mode 100644 index 00000000..cf71d562 --- /dev/null +++ b/app/(general)/integration/lens-protocol/search/page.tsx @@ -0,0 +1,17 @@ +"use client" + +import { useSearchParams } from "next/navigation" + +import { SearchProfiles } from "@/integrations/lens-protocol/components/profile/search-profiles" +import { SearchPublications } from "@/integrations/lens-protocol/components/publications/search-publications" + +export default function PageIntegration() { + const searchParams = useSearchParams() + const query = searchParams.get("q") ?? "" + return ( + <> + + + + ) +} diff --git a/app/(general)/integration/lens-protocol/twitter-image.tsx b/app/(general)/integration/lens-protocol/twitter-image.tsx new file mode 100644 index 00000000..215a2a59 --- /dev/null +++ b/app/(general)/integration/lens-protocol/twitter-image.tsx @@ -0,0 +1,9 @@ +import Image from "./opengraph-image" + +export const runtime = "edge" +export const size = { + width: 1200, + height: 630, +} + +export default Image diff --git a/components/shared/example-demos.tsx b/components/shared/example-demos.tsx index 7991bf2f..8e4d2326 100644 --- a/components/shared/example-demos.tsx +++ b/components/shared/example-demos.tsx @@ -472,6 +472,22 @@ const demos = [ ), }, + { + title: turboIntegrations.lensProtocol.name, + description: turboIntegrations.lensProtocol.description, + href: turboIntegrations.lensProtocol.href, + demo: ( +
+ +
+ ), + }, { title: turboIntegrations.starter.name, description: turboIntegrations.starter.description, diff --git a/config/networks.ts b/config/networks.ts index 884eb8bc..2c1fa8a1 100644 --- a/config/networks.ts +++ b/config/networks.ts @@ -83,6 +83,7 @@ export const ETH_CHAINS_PROD = [ goerli, base, baseGoerli, + polygonMumbai, ] export const ETH_CHAINS_DEV = env.NEXT_PUBLIC_PROD_NETWORKS_DEV === "true" diff --git a/data/integrations.ts b/data/integrations.ts index cd343a3d..641f5232 100644 --- a/data/integrations.ts +++ b/data/integrations.ts @@ -107,6 +107,14 @@ export const integrations = { imgLight: "/integrations/arweave-light.png", imgDark: "/integrations/arweave-dark.png", }, + lensProtocol: { + name: "Lens Protocol", + href: "/integration/lens-protocol", + url: "https://www.lens.xyz/", + description: "Lens Protocol is the social layer for Web3", + imgLight: "/integrations/lensprotocol-light.svg", + imgDark: "/integrations/lensprotocol-dark.svg", + }, }, services: { disco: { diff --git a/data/turbo-integrations.ts b/data/turbo-integrations.ts index 999b11c3..d0a6df71 100644 --- a/data/turbo-integrations.ts +++ b/data/turbo-integrations.ts @@ -148,6 +148,14 @@ export const turboIntegrations = { imgLight: "/integrations/arweave-light.png", imgDark: "/integrations/arweave-dark.png", }, + lensProtocol: { + name: "Lens Protocol", + href: "/integration/lens-protocol", + url: "https://www.lens.xyz/", + description: "Lens Protocol is the social layer for Web3", + imgLight: "/integrations/lensprotocol-light.svg", + imgDark: "/integrations/lensprotocol-dark.svg", + }, starter: { name: "Starter Template", href: "/integration/starter", diff --git a/integrations/lens-protocol/components/auth/is-user-authenticated.tsx b/integrations/lens-protocol/components/auth/is-user-authenticated.tsx new file mode 100644 index 00000000..ca418607 --- /dev/null +++ b/integrations/lens-protocol/components/auth/is-user-authenticated.tsx @@ -0,0 +1,17 @@ +import { ReactNode } from "react" +import { useActiveWallet } from "@lens-protocol/react-web" + +import { Skeleton } from "@/components/ui/skeleton" + +export const IsUserAuthenticated = ({ + children, + showLoading, +}: { + children: ReactNode + showLoading?: boolean +}) => { + const { data: wallet, loading } = useActiveWallet() + if (loading && showLoading) return + if (wallet && children) return <>{children} + return null +} diff --git a/integrations/lens-protocol/components/auth/login-button.tsx b/integrations/lens-protocol/components/auth/login-button.tsx new file mode 100644 index 00000000..529ace10 --- /dev/null +++ b/integrations/lens-protocol/components/auth/login-button.tsx @@ -0,0 +1,44 @@ +import { useEffect } from "react" +import { useWalletLogin } from "@lens-protocol/react-web" +import { useAccount } from "wagmi" + +import { useToast } from "@/lib/hooks/use-toast" +import { Button } from "@/components/ui/button" + +export const LoginButton = () => { + const { execute: login, error: loginError, isPending } = useWalletLogin() + const { address } = useAccount() + const { toast, dismiss } = useToast() + + useEffect(() => { + if (loginError) showErrorToast(String(loginError)) + }, [loginError]) + + const showErrorToast = (loginError: string) => { + toast({ + title: "Login failed", + description: loginError, + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + + const onLoginClick = async () => { + if (!address) return null + await login({ + address, + }) + } + return ( + + ) +} diff --git a/integrations/lens-protocol/components/auth/logout-button.tsx b/integrations/lens-protocol/components/auth/logout-button.tsx new file mode 100644 index 00000000..f89f567a --- /dev/null +++ b/integrations/lens-protocol/components/auth/logout-button.tsx @@ -0,0 +1,18 @@ +import { useWalletLogout } from "@lens-protocol/react-web" + +import { Button } from "@/components/ui/button" + +export const LogoutButton = () => { + const { execute: logout, isPending } = useWalletLogout() + + return ( + + ) +} diff --git a/integrations/lens-protocol/components/auth/not-authenticated-yet.tsx b/integrations/lens-protocol/components/auth/not-authenticated-yet.tsx new file mode 100644 index 00000000..a4021b86 --- /dev/null +++ b/integrations/lens-protocol/components/auth/not-authenticated-yet.tsx @@ -0,0 +1,8 @@ +import { ReactNode } from "react" +import { useActiveWallet } from "@lens-protocol/react-web" + +export const NotAuthenticatedYet = ({ children }: { children: ReactNode }) => { + const { data: wallet } = useActiveWallet() + if (!wallet) return <>{children} + return null +} diff --git a/integrations/lens-protocol/components/feed.tsx b/integrations/lens-protocol/components/feed.tsx new file mode 100644 index 00000000..fbbb4079 --- /dev/null +++ b/integrations/lens-protocol/components/feed.tsx @@ -0,0 +1,41 @@ +import { ProfileId, useActiveProfile, useFeed } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "./load-more-button" +import { + PublicationCard, + PublicationCardMode, +} from "./publications/publication-card" + +export const Feed = ({ profileId }: { profileId: ProfileId }) => { + const activeProfile = useActiveProfile() + const { data, loading, hasMore, next } = useFeed({ + observerId: activeProfile?.data?.id ?? undefined, + profileId, + limit: 10, + }) + return ( +
+

Feed

+ {data?.map((feedItem) => ( + + ))} + {loading && + Array(5) + .fill(0) + .map((_, index) => ( + + ))} + + {!loading && !data?.length && User feed is empty.} +
+ ) +} diff --git a/integrations/lens-protocol/components/load-more-button.tsx b/integrations/lens-protocol/components/load-more-button.tsx new file mode 100644 index 00000000..917b1416 --- /dev/null +++ b/integrations/lens-protocol/components/load-more-button.tsx @@ -0,0 +1,18 @@ +import { Button } from "@/components/ui/button" + +export const LoadMoreButton = ({ + hasMore, + loading, + onClick, +}: { + hasMore: boolean + loading: boolean + onClick: () => void +}) => + hasMore ? ( +
+ +
+ ) : null diff --git a/integrations/lens-protocol/components/navbar.tsx b/integrations/lens-protocol/components/navbar.tsx new file mode 100644 index 00000000..c4df2563 --- /dev/null +++ b/integrations/lens-protocol/components/navbar.tsx @@ -0,0 +1,116 @@ +import { useCallback, useState } from "react" +import { useRouter } from "next/navigation" +import { useActiveProfile } from "@lens-protocol/react-web" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Button } from "@/components/ui/button" +import { LinkComponent } from "@/components/shared/link-component" + +import { getProfilePictureSrc } from "../utils" +import { IsUserAuthenticated } from "./auth/is-user-authenticated" +import { LoginButton } from "./auth/login-button" +import { LogoutButton } from "./auth/logout-button" +import { NotAuthenticatedYet } from "./auth/not-authenticated-yet" + +export const Navbar = () => { + const [searchQuery, setSearchQuery] = useState("") + const router = useRouter() + const search = useCallback(() => { + router.push(`/integration/lens-protocol/search?q=${searchQuery}`) + }, [searchQuery]) + const { data: activeProfile } = useActiveProfile() + return ( + + ) +} diff --git a/integrations/lens-protocol/components/profile/address-profiles.tsx b/integrations/lens-protocol/components/profile/address-profiles.tsx new file mode 100644 index 00000000..a31b9f23 --- /dev/null +++ b/integrations/lens-protocol/components/profile/address-profiles.tsx @@ -0,0 +1,35 @@ +import { useActiveProfile, useProfilesOwnedBy } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { ProfileCard } from "./profile-card" + +export const AddressProfiles = ({ address }: { address: string }) => { + const { data: activeProfile } = useActiveProfile() + const { + data: profiles, + loading, + hasMore, + next, + } = useProfilesOwnedBy({ + limit: 10, + address, + observerId: activeProfile?.id, + }) + return ( +
+

+ {address}Profiles +

+
+ {profiles?.map((profile) => ( + + ))} + {loading && + Array(4) + .fill(0) + .map((_, index) => )} +
+ +
+ ) +} diff --git a/integrations/lens-protocol/components/profile/explore-profiles.tsx b/integrations/lens-protocol/components/profile/explore-profiles.tsx new file mode 100644 index 00000000..fa053007 --- /dev/null +++ b/integrations/lens-protocol/components/profile/explore-profiles.tsx @@ -0,0 +1,44 @@ +import { useRouter } from "next/navigation" +import { useActiveProfile, useExploreProfiles } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { ProfileCard } from "./profile-card" + +export const ExploreProfiles = () => { + const profile = useActiveProfile() + const { + data: profiles, + loading, + hasMore, + next, + } = useExploreProfiles({ + limit: 10, + observerId: profile?.data?.id ?? undefined, + }) + const router = useRouter() + return ( + <> +
+

Profiles

+
+ {profiles?.map((profile) => ( + + router.push( + `/integration/lens-protocol/profiles/${profile.handle}` + ) + } + /> + ))} + {loading && + Array(4) + .fill(0) + .map((_, index) => )} +
+ +
+ + ) +} diff --git a/integrations/lens-protocol/components/profile/follow-unfollow-button.tsx b/integrations/lens-protocol/components/profile/follow-unfollow-button.tsx new file mode 100644 index 00000000..c1383f27 --- /dev/null +++ b/integrations/lens-protocol/components/profile/follow-unfollow-button.tsx @@ -0,0 +1,117 @@ +import { useEffect } from "react" +import { + FollowPolicyType, + Profile, + ProfileOwnedByMe, + useActiveProfile, + useFollow, + useProfile, + useUnfollow, +} from "@lens-protocol/react-web" + +import { useToast } from "@/lib/hooks/use-toast" +import { Button } from "@/components/ui/button" + +const UnauthorizedFollowButton = () => { + const { toast, dismiss } = useToast() + const showErrorToast = () => { + toast({ + title: "You need to login first.", + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + return ( + + ) +} + +const AuthorizedFollowUnfollowButton = ({ + profile, + activeProfile, +}: { + profile: Profile + activeProfile: ProfileOwnedByMe +}) => { + const { data: followeeProfile } = useProfile({ + observerId: activeProfile.id, + profileId: profile.id, + }) + const { + execute: follow, + error, + isPending: followLoading, + } = useFollow({ follower: activeProfile, followee: profile }) + const { execute: unfollow, isPending: unfollowLoading } = useUnfollow({ + follower: activeProfile, + followee: profile, + }) + const { toast, dismiss } = useToast() + + useEffect(() => { + if (error) showErrorToast(String(error)) + }, [error]) + + const showErrorToast = (error: string) => { + toast({ + title: "Operation failed", + description: error, + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + if (followeeProfile?.isFollowedByMe) + return ( + + ) + return ( + + ) +} + +export const FollowUnfollowButton = ({ profile }: { profile: Profile }) => { + const { data: activeProfile } = useActiveProfile() + if (!activeProfile) return + if (profile.ownedByMe) return null + return ( + + ) +} diff --git a/integrations/lens-protocol/components/profile/owned-profiles.tsx b/integrations/lens-protocol/components/profile/owned-profiles.tsx new file mode 100644 index 00000000..7edce831 --- /dev/null +++ b/integrations/lens-protocol/components/profile/owned-profiles.tsx @@ -0,0 +1,146 @@ +import { turboIntegrations } from "@/data/turbo-integrations" +import { + useActiveProfile, + useActiveProfileSwitch, + useProfilesOwnedByMe, +} from "@lens-protocol/react-web" +import { FaExternalLinkAlt } from "react-icons/fa" + +import { Avatar, AvatarFallback } from "@/components/ui/avatar" +import { Button } from "@/components/ui/button" +import { Card } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { LightDarkImage } from "@/components/shared/light-dark-image" +import { LinkComponent } from "@/components/shared/link-component" + +import { useCreateTestProfile } from "../../hooks/use-create-profile" +import { IsUserAuthenticated } from "../auth/is-user-authenticated" +import { LoginButton } from "../auth/login-button" +import { NotAuthenticatedYet } from "../auth/not-authenticated-yet" +import { LoadMoreButton } from "../load-more-button" +import { ProfileCard } from "./profile-card" + +export const OwnedProfiles = () => { + const { + data: profiles, + loading, + hasMore, + next, + } = useProfilesOwnedByMe({ + limit: 10, + }) + const { + onSubmit, + handle, + error, + setHandle, + isPending: isCreating, + } = useCreateTestProfile() + const { data: activeProfile } = useActiveProfile() + const { execute: switchActiveProfile, isPending } = useActiveProfileSwitch() + return ( +
+

Owned Profiles

+ +
+ {profiles + ?.filter( + (profile, index, arr) => + arr.findIndex((p) => p.handle === profile.handle) === index + ) + .map((profile) => { + const isProfileSelected = profile.id === activeProfile?.id + return ( + + + + View + +
+ } + /> + ) + })} + {loading && + Array(4) + .fill(0) + .map((_, index) => )} + +
+ + + + +
+

+ Create a new profile on testnet +

+
+ setHandle(e.target.value)} + /> + {error && ( +
+ {String(error)} +
+ )} + +
+
+
+
+ + + +
+ Claim a new handle on Lens + +
+ Join the waitlist +
+
+
+ + + +
+
+ You need to login first to be able to see your profiles. +
+ +
+
+ + ) +} diff --git a/integrations/lens-protocol/components/profile/profile-card.tsx b/integrations/lens-protocol/components/profile/profile-card.tsx new file mode 100644 index 00000000..0e4bdefd --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile-card.tsx @@ -0,0 +1,86 @@ +import { ReactNode } from "react" +import { useRouter } from "next/navigation" +import { Profile } from "@lens-protocol/react-web" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card" +import { Skeleton } from "@/components/ui/skeleton" + +import { getProfilePictureSrc } from "../../utils" +import { FollowUnfollowButton } from "./follow-unfollow-button" + +export const ProfileCard = ({ + profile, + onClick, + cta, +}: { + profile: Profile | null + onClick?: () => void + cta?: ReactNode +}) => { + const router = useRouter() + return ( + { + e.stopPropagation() + if (onClick) onClick() + else if (profile) + router.push(`/integration/lens-protocol/profiles/${profile.handle}`) + }} + > + + {profile ? ( + + + + {profile.handle.substring(0, 1)} + + + ) : ( + + )} + {profile ? ( + + @{profile.handle} + + ) : ( + + )} + {profile ? ( + + {profile.name ?? profile.handle} + + ) : ( + + )} +
+ {profile ? ( + + {profile.stats.totalFollowers} + Followers + + ) : ( + + )} + {profile ? ( + + {profile.stats.totalFollowing} + Followings + + ) : ( + + )} +
+
+ + {profile ? ( + + ) : ( + + )} + {cta && cta} + +
+ ) +} diff --git a/integrations/lens-protocol/components/profile/profile-list-modal.tsx b/integrations/lens-protocol/components/profile/profile-list-modal.tsx new file mode 100644 index 00000000..3888b0bd --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile-list-modal.tsx @@ -0,0 +1,105 @@ +import { ReactNode } from "react" +import { useRouter } from "next/navigation" +import { Profile } from "@lens-protocol/react-web" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTrigger, +} from "@/components/ui/dialog" +import { Skeleton } from "@/components/ui/skeleton" + +import { getProfilePictureSrc } from "../../utils" +import { LoadMoreButton } from "../load-more-button" +import { FollowUnfollowButton } from "./follow-unfollow-button" + +const ProfileRow = ({ profile }: { profile: Profile | null }) => { + const router = useRouter() + + return ( +
+
{ + e.stopPropagation() + if (profile) + router.push(`/integration/lens-protocol/profiles/${profile.handle}`) + }} + > + {profile ? ( + + + + {profile.handle.substring(0, 1)} + + + ) : ( + + )} +
+ {profile ? ( + + {profile.name ?? profile.handle} + + ) : ( + + )} + {profile ? ( + + @{profile.handle} + + ) : ( + + )} +
+
+ {profile ? ( + + ) : ( + + )} +
+ ) +} + +export const ProfileListModal = ({ + profiles, + trigger, + hasMore, + loading, + title, + next, +}: { + profiles: Profile[] + trigger: ReactNode + hasMore: boolean + loading: boolean + title: string + next: () => void +}) => { + return ( + + {trigger} + + +

{title}

+
+
+ {profiles?.map((profile) => ( + + ))} + {loading && + Array(4) + .fill(0) + .map((_, index) => )} + + {!loading && profiles?.length === 0 && ( + This list is empty. + )} +
+
+
+ ) +} diff --git a/integrations/lens-protocol/components/profile/profile-publications.tsx b/integrations/lens-protocol/components/profile/profile-publications.tsx new file mode 100644 index 00000000..1c70429c --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile-publications.tsx @@ -0,0 +1,69 @@ +import { + Post, + ProfileId, + PublicationTypes, + useActiveProfile, + usePublications, +} from "@lens-protocol/react-web" +import { FaRetweet } from "react-icons/fa" + +import { LoadMoreButton } from "../load-more-button" +import { PublicationCard } from "../publications/publication-card" + +export const ProfilePublications = ({ + profileId, + publicationTypes, + title, +}: { + profileId: ProfileId + publicationTypes: PublicationTypes[] + title: string +}) => { + const { data: activeProfile } = useActiveProfile() + const { + data: publications, + loading, + hasMore, + next, + } = usePublications({ + profileId, + observerId: activeProfile?.id, + publicationTypes, + limit: 10, + }) + return ( +
+

{title}

+ {publications?.map((publication) => + publication.__typename === "Mirror" ? ( + <> +
+ + + {publication.profile.name ?? publication.profile.handle} + + Mirrored +
+ + + ) : ( + + ) + )} + {loading && + Array(5) + .fill(0) + .map((_, index) => ( + + ))} + + {publications?.length === 0 && No {title} yet.} +
+ ) +} diff --git a/integrations/lens-protocol/components/profile/profile-revenue.tsx b/integrations/lens-protocol/components/profile/profile-revenue.tsx new file mode 100644 index 00000000..f621f6cc --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile-revenue.tsx @@ -0,0 +1,36 @@ +import { ProfileId, useProfileFollowRevenue } from "@lens-protocol/react-web" + +import { Skeleton } from "@/components/ui/skeleton" + +export const ProfileRevenue = ({ profileId }: { profileId: ProfileId }) => { + const { data, loading: revenueLoading } = useProfileFollowRevenue({ + profileId, + }) + return ( +
+

Profile Revenue

+ {data?.map((revenue) => { + const { asset } = revenue.totalAmount + return ( +
+ + {revenue.totalAmount.toNumber()} + + {asset.symbol} +
+ ) + })} + {revenueLoading && ( +
+ +
+ )} + {!data?.length && ( + None yet + )} +
+ ) +} diff --git a/integrations/lens-protocol/components/profile/profile-stats.tsx b/integrations/lens-protocol/components/profile/profile-stats.tsx new file mode 100644 index 00000000..d5381a99 --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile-stats.tsx @@ -0,0 +1,68 @@ +import { + Profile, + useActiveProfile, + useProfileFollowers, + useProfileFollowing, +} from "@lens-protocol/react-web" + +import { ProfileListModal } from "./profile-list-modal" + +export const ProfileStats = ({ profile }: { profile: Profile }) => { + const { data: activeProfile } = useActiveProfile() + const { + data: followers, + loading: followersLoading, + hasMore: followersHasMore, + next: followersNext, + } = useProfileFollowers({ + profileId: profile.id, + observerId: activeProfile?.id, + limit: 10, + }) + const { + data: followings, + loading: followingsLoading, + hasMore: followingsHasMore, + next: followingsNext, + } = useProfileFollowing({ + walletAddress: profile.ownedBy, + observerId: activeProfile?.id, + limit: 10, + }) + return ( +
+ + follower.wallet.defaultProfile + ? [follower.wallet.defaultProfile] + : [] + ) ?? [] + } + title="Followers" + trigger={ + + {profile.stats.totalFollowers} + Followers + + } + /> + following.profile) ?? []} + title="Followings" + trigger={ + + {profile.stats.totalFollowing} + Followings + + } + /> +
+ ) +} diff --git a/integrations/lens-protocol/components/profile/profile.tsx b/integrations/lens-protocol/components/profile/profile.tsx new file mode 100644 index 00000000..21b2ff7a --- /dev/null +++ b/integrations/lens-protocol/components/profile/profile.tsx @@ -0,0 +1,191 @@ +import Link from "next/link" +import { + PublicationTypes, + useActiveProfile, + useProfile, +} from "@lens-protocol/react-web" + +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Skeleton } from "@/components/ui/skeleton" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" + +import { getProfilePictureSrc } from "../../utils" +import { IsUserAuthenticated } from "../auth/is-user-authenticated" +import { LoginButton } from "../auth/login-button" +import { NotAuthenticatedYet } from "../auth/not-authenticated-yet" +import { Feed } from "../feed" +import { PublicationCard } from "../publications/publication-card" +import { FollowUnfollowButton } from "./follow-unfollow-button" +import { ProfilePublications } from "./profile-publications" +import { ProfileRevenue } from "./profile-revenue" +import { ProfileStats } from "./profile-stats" + +export const Profile = ({ handle }: { handle: string }) => { + const activeProfile = useActiveProfile() + const { data: profile, loading } = useProfile({ + handle, + observerId: activeProfile?.data?.id ?? undefined, + }) + + if (!loading && !profile) + return
Profile not found!
+ return ( +
+
+ {profile ? ( + + + + {profile.handle.substring(0, 1)} + + + ) : ( + + )} + {!profile ? ( + + ) : ( + profile.name && ( + + {profile.name} + + ) + )} + {profile ? ( + @{profile.handle} + ) : ( + + )} + {profile ? ( + + ) : ( +
+ + +
+ )} + {profile && !profile.ownedByMe && ( + + )} + {!profile ? ( +
+ + + +
+ ) : ( + profile.bio && ( + <> +
Bio
+
+ {profile.bio} +
+ + ) + )} +
+ {profile ? ( +
Owned by
+ ) : ( + + )} + {profile ? ( + + {profile.ownedBy} + + ) : ( + + )} + {profile ? ( + + See all profiles + + ) : ( + + )} +
+ {profile && Object.keys(profile.attributes).length > 0 && ( +
+ {Object.entries(profile.attributes).map(([key, attribute]) => ( +
+ {key}: + + {attribute.toString()} + +
+ ))} +
+ )} + {profile ? ( + + ) : ( + + )} +
+
+ +
+ {profile ? ( + + Feed + Posts + Replies + + ) : ( + + + Feed + Posts + Replies + + + )} +
+ {profile ? ( + <> + + + + + +
+ You need to login to see the profile feed. +
+ +
+
+ + + + + + + + ) : ( +
+ {Array(5) + .fill(0) + .map((_, index) => ( + + ))} +
+ )} +
+
+
+ ) +} diff --git a/integrations/lens-protocol/components/profile/search-profiles.tsx b/integrations/lens-protocol/components/profile/search-profiles.tsx new file mode 100644 index 00000000..3656e3fc --- /dev/null +++ b/integrations/lens-protocol/components/profile/search-profiles.tsx @@ -0,0 +1,44 @@ +import { useRouter } from "next/navigation" +import { useActiveProfile, useSearchProfiles } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { ProfileCard } from "./profile-card" + +export const SearchProfiles = ({ query }: { query: string }) => { + const profile = useActiveProfile() + const { + data: profiles, + loading, + hasMore, + next, + } = useSearchProfiles({ + query, + limit: 10, + observerId: profile?.data?.id ?? undefined, + }) + const router = useRouter() + return ( +
+

Profiles

+
+ {profiles?.map((profile) => ( + + router.push( + `/integration/lens-protocol/profiles/${profile.handle}` + ) + } + /> + ))} + {loading && + Array(4) + .fill(0) + .map((_, index) => )} +
+ + {!loading && !profiles?.length && No profiles found.} +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/actions/button.tsx b/integrations/lens-protocol/components/publications/actions/button.tsx new file mode 100644 index 00000000..03c80e6b --- /dev/null +++ b/integrations/lens-protocol/components/publications/actions/button.tsx @@ -0,0 +1,50 @@ +import { ReactNode } from "react" + +import { Button } from "@/components/ui/button" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" + +export const ActionButton = ({ + color, + execute, + name, + hideCount, + count, + disabled, + icon, +}: { + color: string + execute: () => void + name: string + hideCount: boolean + count: number + disabled: boolean + icon: ReactNode +}) => { + const btnColor = `text-${color}-500 dark:text-${color}-300 hover:text-${color}-600 hover:dark:text-${color}-200` + return ( + + ) +} diff --git a/integrations/lens-protocol/components/publications/actions/comment.tsx b/integrations/lens-protocol/components/publications/actions/comment.tsx new file mode 100644 index 00000000..8c2e16cb --- /dev/null +++ b/integrations/lens-protocol/components/publications/actions/comment.tsx @@ -0,0 +1,30 @@ +import { FaRegCommentAlt } from "react-icons/fa" + +import { useToast } from "@/lib/hooks/use-toast" + +import { IActionButton } from "." +import { ActionButton } from "./button" + +export const CommentButton = ({ publication, hideCount }: IActionButton) => { + const { toast, dismiss } = useToast() + const showErrorToast = () => { + toast({ + title: "Commenting on a publication is not supported currently.", + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + return ( + showErrorToast()} + hideCount={hideCount} + icon={} + name="comment" + /> + ) +} diff --git a/integrations/lens-protocol/components/publications/actions/index.tsx b/integrations/lens-protocol/components/publications/actions/index.tsx new file mode 100644 index 00000000..b4f1aa12 --- /dev/null +++ b/integrations/lens-protocol/components/publications/actions/index.tsx @@ -0,0 +1,6 @@ +import { Comment, Post } from "@lens-protocol/react-web" + +export interface IActionButton { + publication: Post | Comment + hideCount: boolean +} diff --git a/integrations/lens-protocol/components/publications/actions/like.tsx b/integrations/lens-protocol/components/publications/actions/like.tsx new file mode 100644 index 00000000..e864a94e --- /dev/null +++ b/integrations/lens-protocol/components/publications/actions/like.tsx @@ -0,0 +1,96 @@ +import { + Profile, + ReactionTypes, + useActiveProfile, + useReaction, +} from "@lens-protocol/react-web" +import { FaHeart, FaRegHeart } from "react-icons/fa" + +import { useToast } from "@/lib/hooks/use-toast" + +import { IActionButton } from "." +import { IsUserAuthenticated } from "../../auth/is-user-authenticated" +import { NotAuthenticatedYet } from "../../auth/not-authenticated-yet" +import { ActionButton } from "./button" + +const UnAuthorizedLikeButton = ({ publication, hideCount }: IActionButton) => { + const { toast, dismiss } = useToast() + const showErrorToast = () => { + toast({ + title: "You need to login first.", + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + return ( + showErrorToast()} + hideCount={hideCount} + icon={} + name="like" + /> + ) +} + +const AuthorizedLikeButton = ({ + publication, + hideCount, + profile, +}: IActionButton & { profile: Profile }) => { + const { addReaction, removeReaction, hasReaction, isPending } = useReaction({ + profileId: profile.id, + }) + + const reactionType = ReactionTypes.Upvote + + const hasReactionType = hasReaction({ + reactionType, + publication, + }) + + const execute = async () => { + if (isPending) return + if (!hasReactionType) { + await addReaction({ + reactionType, + publication, + }) + } else if (hasReactionType) { + await removeReaction({ + reactionType, + publication, + }) + } + } + + return ( + : } + name="like" + /> + ) +} + +export const LikeButton = (props: IActionButton) => { + const { data: profile } = useActiveProfile() + return ( + <> + + + + + {profile && } + + + ) +} diff --git a/integrations/lens-protocol/components/publications/actions/mirror.tsx b/integrations/lens-protocol/components/publications/actions/mirror.tsx new file mode 100644 index 00000000..bbd9aa9e --- /dev/null +++ b/integrations/lens-protocol/components/publications/actions/mirror.tsx @@ -0,0 +1,98 @@ +import { useEffect } from "react" +import { + ProfileOwnedByMe, + useActiveProfile, + useCreateMirror, +} from "@lens-protocol/react-web" +import { FaRetweet } from "react-icons/fa" + +import { useToast } from "@/lib/hooks/use-toast" + +import { IActionButton } from "." +import { IsUserAuthenticated } from "../../auth/is-user-authenticated" +import { NotAuthenticatedYet } from "../../auth/not-authenticated-yet" +import { ActionButton } from "./button" + +const UnAuthorizedMirrorButton = ({ + publication, + hideCount, +}: IActionButton) => { + const { toast, dismiss } = useToast() + const showErrorToast = () => { + toast({ + title: "You need to login first.", + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + return ( + showErrorToast()} + hideCount={hideCount} + icon={} + name="mirror" + /> + ) +} + +const AuthorizedMirrorButton = ({ + publication, + hideCount, + profile, +}: IActionButton & { profile: ProfileOwnedByMe }) => { + const { + execute: create, + isPending, + error, + } = useCreateMirror({ publisher: profile }) + const { toast, dismiss } = useToast() + const showErrorToast = (error: string) => { + toast({ + title: "Mirror failed.", + description: error, + }) + + setTimeout(() => { + dismiss() + }, 10000) + } + useEffect(() => { + if (error) showErrorToast(String(error)) + }, [error]) + return ( + } + name="mirror" + execute={() => + create({ + publication, + }) + } + /> + ) +} + +export const MirrorButton = (props: IActionButton) => { + const { data: profile } = useActiveProfile() + return ( + <> + + + + + {profile && } + + + ) +} diff --git a/integrations/lens-protocol/components/publications/commnets.tsx b/integrations/lens-protocol/components/publications/commnets.tsx new file mode 100644 index 00000000..47cea9d1 --- /dev/null +++ b/integrations/lens-protocol/components/publications/commnets.tsx @@ -0,0 +1,46 @@ +import { PublicationId, useComments } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { PublicationCard } from "./publication-card" + +export const Comments = ({ + publicationId, +}: { + publicationId: PublicationId +}) => { + const { + data: comments, + loading, + hasMore, + next, + } = useComments({ + commentsOf: publicationId, + limit: 10, + }) + return ( +
+ {comments?.map((comment, index) => ( + + ))} + {loading && + Array(5) + .fill(0) + .map((_, index) => ( + + ))} + +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/explore-publications.tsx b/integrations/lens-protocol/components/publications/explore-publications.tsx new file mode 100644 index 00000000..ec35cc79 --- /dev/null +++ b/integrations/lens-protocol/components/publications/explore-publications.tsx @@ -0,0 +1,40 @@ +import { + Post, + PublicationTypes, + useExplorePublications, +} from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { PublicationCard } from "./publication-card" + +export const ExplorePublications = () => { + const { + data: publications, + loading, + hasMore, + next, + } = useExplorePublications({ + limit: 10, + publicationTypes: [PublicationTypes.Post], + }) + return ( + <> +
+

Publications

+ {publications?.map((publication) => ( + + ))} + {loading && + Array(5) + .fill(0) + .map((_, index) => ( + + ))} + +
+ + ) +} diff --git a/integrations/lens-protocol/components/publications/publication-actions-and-stats.tsx b/integrations/lens-protocol/components/publications/publication-actions-and-stats.tsx new file mode 100644 index 00000000..6450d31f --- /dev/null +++ b/integrations/lens-protocol/components/publications/publication-actions-and-stats.tsx @@ -0,0 +1,25 @@ +import { Comment, Post } from "@lens-protocol/react-web" + +import { CommentButton } from "./actions/comment" +import { LikeButton } from "./actions/like" +import { MirrorButton } from "./actions/mirror" +import { PublicationStats } from "./stats" + +export const PublicationActionsAndStats = ({ + publication, + showCounts = false, +}: { + publication: Post | Comment + showCounts: boolean +}) => { + return ( +
+ {showCounts && } +
+ + + +
+
+ ) +} diff --git a/integrations/lens-protocol/components/publications/publication-card.tsx b/integrations/lens-protocol/components/publications/publication-card.tsx new file mode 100644 index 00000000..3625b298 --- /dev/null +++ b/integrations/lens-protocol/components/publications/publication-card.tsx @@ -0,0 +1,243 @@ +import { ReactNode } from "react" +import { useRouter } from "next/navigation" +import { Comment, FeedItem, Post } from "@lens-protocol/react-web" +import moment from "moment" +import { FaRetweet } from "react-icons/fa" + +import { cn } from "@/lib/utils" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Card, CardContent } from "@/components/ui/card" +import { Skeleton } from "@/components/ui/skeleton" + +import { getProfilePictureSrc } from "../../utils" +import { Comments } from "./commnets" +import { PublicationActionsAndStats } from "./publication-actions-and-stats" +import { PublicationRevenue } from "./publication-revenue" + +export enum PublicationCardMode { + Normal = "normal", + Compact = "compact", + FeedComment = "FeedComment", + Full = "full", +} + +const Wrapper = ({ + shouldLinkToFullMode, + children, + id, + classNames, + chainedStyle, + last, +}: { + shouldLinkToFullMode: boolean + children: ReactNode + id?: string + classNames: string + chainedStyle: boolean + last: boolean +}) => { + const router = useRouter() + const defaultClassName = "p-6 w-full block" + const bottomLine = + "before:absolute before:left-[32px] before:top-[40px] before:h-full before:bg-slate-200 before:dark:bg-neutral-600 before:self-start before:w-[1px]" + const topLine = + "after:absolute after:left-[32px] after:top-[0px] after:h-[24px] after:bg-slate-200 after:dark:bg-neutral-600 after:self-start after:w-[1px]" + const chainedClassName = "relative ml-[-12px]" + const baseClassName = cn( + defaultClassName, + chainedStyle && chainedClassName, + chainedStyle && !last && bottomLine, + chainedStyle && topLine + ) + if (shouldLinkToFullMode) + return ( + { + e.stopPropagation() + if (id) router.push(`/integration/lens-protocol/publications/${id}`) + }} + > + {children} + + ) + return ( + + {children} + + ) +} + +export const PublicationCard = ({ + publication, + feedItem, + mode = PublicationCardMode.Normal, + wrapperClassNames = "", + last = false, + chainedStyle = false, +}: { + publication: Post | Comment | null + feedItem?: FeedItem + mode?: PublicationCardMode + wrapperClassNames?: string + last?: boolean + chainedStyle?: boolean +}) => { + const router = useRouter() + const compactMode = mode === PublicationCardMode.Compact + const fullMode = mode === PublicationCardMode.Full + const feedCommentMode = mode === PublicationCardMode.FeedComment + const { profile } = publication ?? { profile: null } + const mirrored = feedItem?.electedMirror ?? false + const bottomLine = + "relative before:absolute before:left-[-20px] before:top-[-16px] before:h-[calc(100%_+_32px)] before:bg-slate-200 before:dark:bg-neutral-600 before:self-start before:w-[1px]" + return ( + + {mirrored && ( +
+ + + {feedItem?.electedMirror?.profile.name ?? + feedItem?.electedMirror?.profile.handle} + + Mirrored +
+ )} +
+
+
{ + e.stopPropagation() + if (profile) + router.push( + `/integration/lens-protocol/profiles/${profile.handle}` + ) + }} + > + {profile ? ( + + + + {profile.handle.substring(0, 1)} + + + ) : ( + + )} +
+ + {profile ? ( + profile.name ?? profile.handle + ) : ( + + )} + + {!compactMode && ( + + {profile ? ( + <>@{profile.handle} + ) : ( + + )} + + )} +
+
+
+
+
+
+ {publication ? ( + publication.metadata?.content + ) : ( +
+ + + +
+ )} +
+
+ {publication ? ( + moment(publication.createdAt).format("HH:mm YY MMM DD") + ) : ( + + )} +
+ {!compactMode && publication && ( + + )} + {fullMode && publication && ( + + )} +
+
+ {fullMode ? ( + publication ? ( + + ) : ( +
+ {Array(3) + .fill(0) + .map((_, index) => ( + + ))} +
+ ) + ) : null} + {feedCommentMode && feedItem?.comments?.[0] && ( + + )} +
+
+ ) +} diff --git a/integrations/lens-protocol/components/publications/publication-revenue.tsx b/integrations/lens-protocol/components/publications/publication-revenue.tsx new file mode 100644 index 00000000..720bad6b --- /dev/null +++ b/integrations/lens-protocol/components/publications/publication-revenue.tsx @@ -0,0 +1,29 @@ +import { PublicationId, usePublicationRevenue } from "@lens-protocol/react-web" + +export const PublicationRevenue = ({ + publicationId, +}: { + publicationId: PublicationId +}) => { + const { data, loading: revenueLoading } = usePublicationRevenue({ + publicationId, + }) + if (revenueLoading) return null + return ( +
+

Publication Revenue

+ {data ? ( +
+ + {data.revenue.totalAmount.toNumber()} + + + {data.revenue.totalAmount.asset.symbol} + +
+ ) : ( + None yet + )} +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/publication.tsx b/integrations/lens-protocol/components/publications/publication.tsx new file mode 100644 index 00000000..6d88c545 --- /dev/null +++ b/integrations/lens-protocol/components/publications/publication.tsx @@ -0,0 +1,115 @@ +import { + Comment, + Mirror, + Post, + PublicationId, + usePublication, +} from "@lens-protocol/react-web" +import { FaRegCommentAlt, FaRetweet } from "react-icons/fa" + +import { LinkComponent } from "@/components/shared/link-component" + +import { PublicationCard, PublicationCardMode } from "./publication-card" + +export const Publication = ({ + publicationId, +}: { + publicationId: PublicationId +}) => { + return ( +
+ +
+ ) +} + +export const PublicationDetails = ({ + publicationId, +}: { + publicationId: PublicationId +}) => { + const { data: publication, loading } = usePublication({ + publicationId, + }) + if (loading) + return ( + + ) + if (!publication) + return
Publication not found!
+ if (publication.__typename === "Mirror") + return + if (publication.__typename === "Comment") + return + if (publication.__typename === "Post") + return + return ( +
Unknown type of publication!
+ ) +} + +const RenderPost = ({ publication }: { publication: Post }) => { + return ( + + ) +} + +const RenderMirror = ({ publication }: { publication: Mirror }) => { + const { profile } = publication + return ( +
+
+ + + + {profile.name ?? profile.handle} + + + Mirrored +
+ {publication.mirrorOf.__typename === "Post" && ( + + )} + {publication.mirrorOf.__typename === "Comment" && ( + + )} +
+ ) +} + +const RenderComment = ({ publication }: { publication: Comment }) => { + const { profile } = publication + return ( +
+
+ Original publication +
+ {publication.commentOn?.__typename === "Post" && ( + + )} +
+ + + + {profile.name ?? profile.handle} + + + Commented +
+ +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/search-publications.tsx b/integrations/lens-protocol/components/publications/search-publications.tsx new file mode 100644 index 00000000..d999f8be --- /dev/null +++ b/integrations/lens-protocol/components/publications/search-publications.tsx @@ -0,0 +1,32 @@ +import { Post, useSearchPublications } from "@lens-protocol/react-web" + +import { LoadMoreButton } from "../load-more-button" +import { PublicationCard } from "./publication-card" + +export const SearchPublications = ({ query }: { query: string }) => { + const { + data: publications, + loading, + hasMore, + next, + } = useSearchPublications({ query, limit: 10 }) + return ( +
+

Publications

+ {publications?.map((publication) => ( + + ))} + {loading && + Array(5) + .fill(0) + .map((_, index) => ( + + ))} + + {!loading && !publications?.length && No publications found.} +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/stats/index.tsx b/integrations/lens-protocol/components/publications/stats/index.tsx new file mode 100644 index 00000000..c3e0d25e --- /dev/null +++ b/integrations/lens-protocol/components/publications/stats/index.tsx @@ -0,0 +1,69 @@ +import { + Comment, + Post, + useActiveProfile, + useWhoCollectedPublication, + useWhoMirroredPublication, + useWhoReacted, +} from "@lens-protocol/react-web" + +import { Stat } from "./stat" + +export const PublicationStats = ({ + publication, +}: { + publication: Post | Comment +}) => { + const { data: profile } = useActiveProfile() + const likes = useWhoReacted({ + publicationId: publication.id, + observerId: profile?.id, + limit: 10, + }) + const mirrors = useWhoMirroredPublication({ + publicationId: publication.id, + observerId: profile?.id, + limit: 10, + }) + const collects = useWhoCollectedPublication({ + publicationId: publication.id, + observerId: profile?.id, + limit: 10, + }) + return ( +
+ + + {publication.stats.commentsCount} + + comments + + reaction.profile)} + hasMore={likes.hasMore} + loading={likes.loading} + name="likes" + next={likes.next} + value={publication.stats.totalUpvotes} + /> + + + wallet.defaultProfile ? [wallet.defaultProfile] : [] + )} + hasMore={collects.hasMore} + loading={collects.loading} + name="collects" + next={collects.next} + value={publication.stats.totalAmountOfCollects} + /> +
+ ) +} diff --git a/integrations/lens-protocol/components/publications/stats/stat.tsx b/integrations/lens-protocol/components/publications/stats/stat.tsx new file mode 100644 index 00000000..6b3b0a56 --- /dev/null +++ b/integrations/lens-protocol/components/publications/stats/stat.tsx @@ -0,0 +1,33 @@ +import { Profile } from "@lens-protocol/react-web" + +import { ProfileListModal } from "../../profile/profile-list-modal" + +export const Stat = ({ + name, + value, + data, + hasMore, + loading, + next, +}: { + name: string + value: number + data?: Profile[] + hasMore: boolean + loading: boolean + next: () => void +}) => ( + + {value} + {name} + + } + /> +) diff --git a/integrations/lens-protocol/hooks/use-create-profile.ts b/integrations/lens-protocol/hooks/use-create-profile.ts new file mode 100644 index 00000000..e05cc0f8 --- /dev/null +++ b/integrations/lens-protocol/hooks/use-create-profile.ts @@ -0,0 +1,23 @@ +import { FormEvent, useState } from "react" +import { isValidHandle, useCreateProfile } from "@lens-protocol/react-web" + +export const useCreateTestProfile = () => { + const [handle, setHandle] = useState(null) + const [error, setError] = useState(null) + + const { execute: create, error: createError, isPending } = useCreateProfile() + + const onSubmit = async (e: FormEvent) => { + e.preventDefault() + setError(null) + if (!handle) return + if (!isValidHandle(handle)) { + setError("Handle is taken.") + return + } + setHandle(null) + await create({ handle }) + } + + return { error: error ?? createError, isPending, onSubmit, handle, setHandle } +} diff --git a/integrations/lens-protocol/lens-provider.ts b/integrations/lens-protocol/lens-provider.ts new file mode 100644 index 00000000..37c9a3b6 --- /dev/null +++ b/integrations/lens-protocol/lens-provider.ts @@ -0,0 +1,7 @@ +import { development, LensConfig } from "@lens-protocol/react-web" +import { bindings as wagmiBindings } from "@lens-protocol/wagmi" + +export const lensProviderConfig: LensConfig = { + bindings: wagmiBindings(), + environment: development, +} diff --git a/integrations/lens-protocol/utils/index.ts b/integrations/lens-protocol/utils/index.ts new file mode 100644 index 00000000..405a475b --- /dev/null +++ b/integrations/lens-protocol/utils/index.ts @@ -0,0 +1,14 @@ +import { Profile } from "@lens-protocol/react-web" + +export const getProfilePictureSrc = (profile: Profile): string | undefined => { + if (!profile || !profile.picture) return undefined + if (profile.picture.__typename === "MediaSet") { + const splittedUrl = profile.picture.original.url.split("ipfs://") + if (splittedUrl.length === 2) + return `https://gateway.ipfs.io/ipfs/${splittedUrl[1]}` + return profile.picture.original.url + } + if (profile.picture.__typename === "NftImage") + return `https://cdn.stamp.fyi/avatar/eth:${profile.picture.uri}` + return undefined +} diff --git a/package.json b/package.json index 5c3a0b86..ede8ea69 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "@gelatonetwork/automate-sdk": "^2.14.0", "@graphql-typed-document-node/core": "^3.2.0", "@hookform/resolvers": "^3.1.1", + "@lens-protocol/react-web": "^1.3.0", + "@lens-protocol/wagmi": "^2.1.0", "@lit-protocol/lit-node-client": "2.1.161", "@livepeer/react": "^2.6.0", "@moralisweb3/common-evm-utils": "^2.22.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34002e8a..bc9b7c76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@bgd-labs/aave-address-book': specifier: ^1.30.0 @@ -19,6 +23,12 @@ dependencies: '@hookform/resolvers': specifier: ^3.1.1 version: 3.1.1(react-hook-form@7.43.9) + '@lens-protocol/react-web': + specifier: ^1.3.0 + version: 1.3.0(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4) + '@lens-protocol/wagmi': + specifier: ^2.1.0 + version: 2.1.0(@lens-protocol/react-web@1.3.0)(ethers@5.7.2)(viem@1.0.0)(wagmi@1.3.10) '@lit-protocol/lit-node-client': specifier: 2.1.161 version: 2.1.161(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(react@18.2.0) @@ -384,6 +394,11 @@ devDependencies: packages: + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + /@adraffy/ens-normalize@1.9.0: resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==} @@ -410,6 +425,42 @@ packages: leven: 3.1.0 dev: false + /@apollo/client@3.8.1(graphql@16.8.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-JGGj/9bdoLEqzatRikDeN8etseY5qeFAY0vSAx/Pd0ePNsaflKzHx6V2NZ0NsGkInq+9IXXX3RLVDf0EotizMA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.0) + '@wry/context': 0.7.3 + '@wry/equality': 0.5.6 + '@wry/trie': 0.4.3 + graphql: 16.8.0 + graphql-tag: 2.12.6(graphql@16.8.0) + hoist-non-react-statics: 3.3.2 + optimism: 0.17.5 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + response-iterator: 0.2.6 + symbol-observable: 4.0.0 + ts-invariant: 0.10.3 + tslib: 2.5.0 + zen-observable-ts: 1.2.5 + dev: false + /@ardatan/fast-json-stringify@0.0.6(ajv-formats@2.1.1)(ajv@8.12.0): resolution: {integrity: sha512-//BefMIP6U1ptNeBf44Le4vqThejTwZndtYLtAuFBwA/DmbVbbYTCLNIMhZ96WZnhI92EvTXneT5tKJrgINE9A==} peerDependencies: @@ -6441,6 +6492,251 @@ packages: /@ledgerhq/connect-kit-loader@1.1.2: resolution: {integrity: sha512-mscwGroSJQrCTjtNGBu+18FQbZYA4+q6Tyx6K7CXHl6AwgZKbWfZYdgP2F+fyZcRUdGRsMX8QtvU61VcGGtO1A==} + /@lens-protocol/api-bindings@0.10.0(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CoILWFbVQWg7rfJUv1EINimrOG1MJpn413PlWFOILsXEQT4RiSp0JRPs752piog6DKQ4QJCaDld1fi/Haix/qg==} + peerDependencies: + '@faker-js/faker': ^7.6.0 + react: ^18.2.0 + peerDependenciesMeta: + '@faker-js/faker': + optional: true + dependencies: + '@apollo/client': 3.8.1(graphql@16.8.0)(react-dom@18.2.0)(react@18.2.0) + '@lens-protocol/domain': 0.10.0(ethers@5.7.2) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + graphql: 16.8.0 + graphql-tag: 2.12.6(graphql@16.8.0) + react: 18.2.0 + tslib: 2.5.0 + transitivePeerDependencies: + - '@jest/globals' + - ethers + - graphql-ws + - jest-mock-extended + - jest-when + - react-dom + - subscriptions-transport-ws + - wait-for-expect + dev: false + + /@lens-protocol/blockchain-bindings@0.9.1(ethers@5.7.2): + resolution: {integrity: sha512-dc06vo4nlbmL4lwwoSLGi6upo3sdON0wz3KUi6oknPhgoMKt6qIiAbyBfyyDgLIaLPOKEcxkoCg4rKtc7j+3Eg==} + peerDependencies: + ethers: ^5.7.2 + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@lens-protocol/domain': 0.10.0(ethers@5.7.2) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + ethers: 5.7.2 + tslib: 2.5.0 + transitivePeerDependencies: + - '@faker-js/faker' + - '@jest/globals' + - bufferutil + - jest-mock-extended + - jest-when + - utf-8-validate + - wait-for-expect + dev: false + + /@lens-protocol/domain@0.10.0(ethers@5.7.2): + resolution: {integrity: sha512-Au5a5BfOZDisRBxZ0w4pGqFp9ATf1iLo+haEcNwH11L8ssGVzZ3FLKK7jF1az4pliCt+gfwcAo83BQk8Hso2aQ==} + peerDependencies: + '@faker-js/faker': ^7.6.0 + '@jest/globals': ^29.3.1 + jest-mock-extended: ^3.0.1 + jest-when: ^3.5.2 + wait-for-expect: ^3.0.2 + peerDependenciesMeta: + '@faker-js/faker': + optional: true + '@jest/globals': + optional: true + jest-mock-extended: + optional: true + jest-when: + optional: true + wait-for-expect: + optional: true + dependencies: + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + tslib: 2.5.0 + transitivePeerDependencies: + - ethers + dev: false + + /@lens-protocol/gated-content@0.3.2(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4): + resolution: {integrity: sha512-e6JnKMQZ+TfEMT0J9hhIhMFqPd2b3gHZbchQOsdempgt0sSCXYLXLp0FGoRlwtaAtDK+FqYVQXyYJEQZgCJv5w==} + peerDependencies: + '@ethersproject/contracts': ^5.7.0 + '@ethersproject/hash': ^5.7.0 + '@ethersproject/providers': ^5.7.2 + '@ethersproject/wallet': ^5.7.0 + ethers: ^5.7.2 + zod: ^3.20.6 + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@ethersproject/wallet': 5.7.0 + '@lens-protocol/api-bindings': 0.10.0(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0) + '@lens-protocol/domain': 0.10.0(ethers@5.7.2) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + '@lens-protocol/storage': 0.7.4(ethers@5.7.2) + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/crypto': 2.1.62 + '@lit-protocol/encryption': 2.1.62 + '@lit-protocol/node-client': 2.1.62(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0) + '@lit-protocol/types': 2.1.62 + ethers: 5.7.2 + siwe: 1.1.6(ethers@5.7.2) + tslib: 2.5.0 + zod: 3.21.4 + transitivePeerDependencies: + - '@faker-js/faker' + - '@jest/globals' + - '@react-native-async-storage/async-storage' + - '@walletconnect/modal' + - bufferutil + - encoding + - graphql-ws + - jest-mock-extended + - jest-when + - lokijs + - react + - react-dom + - subscriptions-transport-ws + - utf-8-validate + - wait-for-expect + dev: false + + /@lens-protocol/react-web@1.3.0(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4): + resolution: {integrity: sha512-0ztmgGi6URjyX2hYujaJ+HH30gRsI4/Rgk3VviLTtNi1xmduECywEqFljUqZx8zIEndZs+bDpXPojCHhHmjdAQ==} + peerDependencies: + '@xmtp/react-sdk': 1.0.0-preview.40 + ethers: ^5.7.2 + react: ^18.2.0 + peerDependenciesMeta: + '@xmtp/react-sdk': + optional: true + dependencies: + '@lens-protocol/api-bindings': 0.10.0(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0) + '@lens-protocol/domain': 0.10.0(ethers@5.7.2) + '@lens-protocol/gated-content': 0.3.2(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4) + '@lens-protocol/react': 1.3.0(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + '@lens-protocol/storage': 0.7.4(ethers@5.7.2) + ethers: 5.7.2 + react: 18.2.0 + tslib: 2.5.0 + transitivePeerDependencies: + - '@ethersproject/contracts' + - '@ethersproject/hash' + - '@ethersproject/providers' + - '@ethersproject/wallet' + - '@faker-js/faker' + - '@jest/globals' + - '@react-native-async-storage/async-storage' + - '@walletconnect/modal' + - bufferutil + - encoding + - graphql-ws + - jest-mock-extended + - jest-when + - lokijs + - react-dom + - subscriptions-transport-ws + - utf-8-validate + - wait-for-expect + - zod + dev: false + + /@lens-protocol/react@1.3.0(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yrF79WMqmCe5yKpBXBmhhxuDrMszQljZT2gjWy3+zOg0EbktMCg69Z7wmEczOrk6CjSEL2GxW510fZ+8yaJNIQ==} + peerDependencies: + ethers: ^5.7.2 + react: ^18.2.0 + dependencies: + '@apollo/client': 3.8.1(graphql@16.8.0)(react-dom@18.2.0)(react@18.2.0) + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@lens-protocol/api-bindings': 0.10.0(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0) + '@lens-protocol/blockchain-bindings': 0.9.1(ethers@5.7.2) + '@lens-protocol/domain': 0.10.0(ethers@5.7.2) + '@lens-protocol/gated-content': 0.3.2(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + '@lens-protocol/storage': 0.7.4(ethers@5.7.2) + ethers: 5.7.2 + graphql: 16.8.0 + jwt-decode: 3.1.2 + lodash: 4.17.21 + react: 18.2.0 + tslib: 2.5.0 + uuid: 9.0.0 + zod: 3.21.4 + transitivePeerDependencies: + - '@ethersproject/contracts' + - '@ethersproject/hash' + - '@ethersproject/wallet' + - '@faker-js/faker' + - '@jest/globals' + - '@react-native-async-storage/async-storage' + - '@walletconnect/modal' + - bufferutil + - encoding + - graphql-ws + - jest-mock-extended + - jest-when + - lokijs + - react-dom + - subscriptions-transport-ws + - utf-8-validate + - wait-for-expect + dev: false + + /@lens-protocol/shared-kernel@0.10.0(ethers@5.7.2): + resolution: {integrity: sha512-I8WSYI39ALB9dMZOYKb6pmnaKPkEaoXI73G8dZ0ghQRoV7AkWu8a2RrPYuoCq1VIFwQElCL9AmCjuLHTQkOeuQ==} + peerDependencies: + ethers: ^5.7.2 + peerDependenciesMeta: + ethers: + optional: true + dependencies: + decimal.js: 10.4.3 + ethers: 5.7.2 + lodash: 4.17.21 + tslib: 2.5.0 + uuid: 9.0.0 + dev: false + + /@lens-protocol/storage@0.7.4(ethers@5.7.2): + resolution: {integrity: sha512-ezr44IhhcF3OEgJrqCmeOUX1p+UMYhU1sv6ua8AqbpTJZT33nPhXbyiXOYjtPTXi2VZJZPBpKU8rpkixrEgXTA==} + dependencies: + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + tslib: 2.5.0 + zod: 3.21.4 + transitivePeerDependencies: + - ethers + dev: false + + /@lens-protocol/wagmi@2.1.0(@lens-protocol/react-web@1.3.0)(ethers@5.7.2)(viem@1.0.0)(wagmi@1.3.10): + resolution: {integrity: sha512-TpwZjaY8xegyA/Waj8ABxRCmkhz7rcyctSK8NO5+akQ67AVfzn8yhBqNep5Wx4HJokiCRKA8XmSi9md7dNDkLA==} + peerDependencies: + '@lens-protocol/react-web': 1.3.0 + ethers: ^5.7.2 + viem: ^1.0.0 + wagmi: ^1.1.0 + dependencies: + '@lens-protocol/react-web': 1.3.0(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(ethers@5.7.2)(react-dom@18.2.0)(react@18.2.0)(zod@3.21.4) + '@lens-protocol/shared-kernel': 0.10.0(ethers@5.7.2) + ethers: 5.7.2 + viem: 1.0.0(typescript@5.0.4)(zod@3.21.4) + wagmi: 1.3.10(react-dom@18.2.0)(react-native@0.72.4)(react@18.2.0)(typescript@5.0.4)(viem@1.0.0)(zod@3.21.4) + dev: false + /@lit-labs/ssr-dom-shim@1.1.1: resolution: {integrity: sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==} @@ -6458,6 +6754,20 @@ packages: - utf-8-validate dev: false + /@lit-protocol/access-control-conditions@2.1.62: + resolution: {integrity: sha512-nP+iqiLUzQa6bfZL9hM9a+s+YVW21HoHkHP7s2E11VFQmucdnJmUUr7Aw46SK/4yClTjLb6RuHyfIPvCdmIKhQ==} + dependencies: + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + ethers: 5.7.2 + tslib: 2.5.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /@lit-protocol/auth-browser@2.1.161(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0)(react@18.2.0): resolution: {integrity: sha512-QdUzRLjziMJtjLtBQkaRhmhn8T1EiqKTyPWyoyzx9/5WFpaRYc0TmMbK/EidcG68YGObTDEuy2r/XZ3lGr/fcg==} dependencies: @@ -6490,10 +6800,44 @@ packages: - utf-8-validate dev: false + /@lit-protocol/auth-browser@2.1.62(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0): + resolution: {integrity: sha512-/4BTl0omR+JUCyJJc93FCiygSn/4ldrbeBuzWYQzuOFh2f6fcY1GJe3ttEoSJUfwu7OblW86YpWAT65b56rACA==} + dependencies: + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/misc-browser': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + '@walletconnect/ethereum-provider': 2.9.2(@walletconnect/modal@2.6.1) + ethers: 5.7.2 + lit-connect-modal: 0.1.11 + lit-siwe: 1.1.8(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0) + tslib: 2.5.0 + tweetnacl: 1.0.3 + tweetnacl-util: 0.13.5 + util: 0.12.5 + web-vitals: 3.3.1 + transitivePeerDependencies: + - '@ethersproject/contracts' + - '@ethersproject/hash' + - '@ethersproject/providers' + - '@ethersproject/wallet' + - '@react-native-async-storage/async-storage' + - '@walletconnect/modal' + - bufferutil + - encoding + - lokijs + - utf-8-validate + dev: false + /@lit-protocol/bls-sdk@2.1.161: resolution: {integrity: sha512-+ubypaILDWjZejtsyHCeuEGbiMIX6PXKEw9uTMM7wV4PCOTtwUixsaGZr8/+xM6ivGnowO3e80hKKCR3gpXhaA==} dev: false + /@lit-protocol/bls-sdk@2.1.62: + resolution: {integrity: sha512-UjNjycoNXOEoLH/foIJx1L9PLL5OxmHcCD/mFXr4KSeQV/v4srvGNpY/4ng7+k9sJEbvwRwv+FB07ng3/Ihacg==} + dev: false + /@lit-protocol/constants@2.1.161: resolution: {integrity: sha512-91Rs01/BmRdi7YYmwqkaqI7TYOxhldpLIf5nLBaAQNZloijdF073KbXa9dFZtVbTo3c8fG5O6ts6DOQLK7mSKA==} dependencies: @@ -6501,6 +6845,13 @@ packages: tslib: 2.5.0 dev: false + /@lit-protocol/constants@2.1.62: + resolution: {integrity: sha512-4CigP3GS7Cxpa9RXT1twCCvYI5wvfo1UAMbdrjoDgM9VMDtpvSrmlG8AwC9yMoqPM6409BYcgGI9LDGzUjNUjg==} + dependencies: + '@lit-protocol/types': 2.1.62 + tslib: 2.5.0 + dev: false + /@lit-protocol/crypto@2.1.161: resolution: {integrity: sha512-0ntmjz1jdFqGgng7jomHMHt/aW2Ov9tOrKCQ/8phy1x3hkTL2o336vkMU7H+3UbpOt2wryHrobcFyP82ZLm/qQ==} dependencies: @@ -6518,10 +6869,31 @@ packages: - utf-8-validate dev: false + /@lit-protocol/crypto@2.1.62: + resolution: {integrity: sha512-pWte+VQOPmSFvfoMxvobmj5JjkGSD44XMkkTXGubpGTBr27hK9CuDxpVHTsI9NsGFSJRdPBpRou+YD5I22yDiA==} + dependencies: + '@lit-protocol/bls-sdk': 2.1.62 + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/ecdsa-sdk': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/nacl': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + ethers: 5.7.2 + tslib: 2.5.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /@lit-protocol/ecdsa-sdk@2.1.161: resolution: {integrity: sha512-UuZvzCEYnoXflJItwmDATDc60H1dH+z8J8iW3rAkPbxYcwCBSbVSo4aUel2Y9Vf9/uaBtm1xSjeWnBv6PBVFWw==} dev: false + /@lit-protocol/ecdsa-sdk@2.1.62: + resolution: {integrity: sha512-VWYAQh31e5Vu6YXvw7iDQja/f2Je6Obj8VoXLweWWfSpUnKqe1JJKGDLxOAuQUT3ZSaX7bYrq7hLIJdwdWmJQw==} + dev: false + /@lit-protocol/encryption@2.1.161(node-fetch@2.6.9): resolution: {integrity: sha512-O8ZiZtjzrXvBY8OuRyOviAQ1VK3Lsmh1G5qAovGwB1Za8fshGB2mTR4PBlQ7Zf2rb+qlxC7220sZ8Y20UtCd5A==} dependencies: @@ -6545,6 +6917,25 @@ packages: - utf-8-validate dev: false + /@lit-protocol/encryption@2.1.62: + resolution: {integrity: sha512-Nmte/UINgc+YVlA3RewhW+1SFnKcSikd94HlBxS+TX9yb2KBUO6oKNjTQSGX4P/KD3zBxaFlbY8+jrWeYR1aQQ==} + dependencies: + '@lit-protocol/bls-sdk': 2.1.62 + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/crypto': 2.1.62 + '@lit-protocol/ecdsa-sdk': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/nacl': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + ethers: 5.7.2 + jszip: 3.10.1 + tslib: 2.5.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /@lit-protocol/lit-node-client-nodejs@2.1.161(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0): resolution: {integrity: sha512-mQR56sh0rZF7XxmuQphPrx4hBB+rHY7sneDhdp6ZCxbUcM7NWRbz5h+idn8Ln49E6qXHplFCyp2dWqrwF0vr9w==} dependencies: @@ -6633,6 +7024,10 @@ packages: resolution: {integrity: sha512-TwO+nKCL2CC8L4O7ypJ06RvBmSXntL+Yrfr+Bqj/d1xhVOB+RwohMoF8lyP6zvQokcKZqar+uPgu/hiaQhIlJw==} dev: false + /@lit-protocol/lit-third-party-libs@2.1.62: + resolution: {integrity: sha512-js8Z3uG4v30Dw9HNqnjxkzMcB3cp3UcF6tfsWGo99+g5OqqKnkCDbb4IXeqnGbslVPn6ll6XouRQPmCcuzeGaw==} + dev: false + /@lit-protocol/misc-browser@2.1.161: resolution: {integrity: sha512-FpN5N+jSff9r3WoH/H7Es/MX0nwnYqmOL1Zfg1HWHlTXbUV8ROg7QX3UPQWLFFF1nGLw649+OgN+bkXdDTG7fg==} dependencies: @@ -6647,6 +7042,20 @@ packages: - utf-8-validate dev: false + /@lit-protocol/misc-browser@2.1.62: + resolution: {integrity: sha512-2NX//tUe5ChrWCN4Msi4RE8DlYjTMGqyPYJHS86r7nKHG7sHSPCucn84LiTmVGA3DVKzspeGJdMbEF/W8Ogn6w==} + dependencies: + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + ethers: 5.7.2 + tslib: 2.5.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /@lit-protocol/misc@2.1.161: resolution: {integrity: sha512-wA927QjBfTEtpXglsduB1QTadIdzl/ELytRO+UzilQwQOGwSr7I/jp9giJU8WKoeFHQZuZYREQvBRxyocu1quw==} dependencies: @@ -6655,18 +7064,76 @@ packages: tslib: 2.5.0 dev: false + /@lit-protocol/misc@2.1.62: + resolution: {integrity: sha512-i6A/kxiJQgy8BZJGH7H8V2kxqOA2xboAjH2BzAbE/pMezfHG7wybkXT9cnXnXOZsAnuGnOKd93u+j7bskuDd2w==} + dependencies: + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/types': 2.1.62 + tslib: 2.5.0 + dev: false + /@lit-protocol/nacl@2.1.161: resolution: {integrity: sha512-FrbhM1J/SkMGZqOenW+Fm3lSHLiOUFvorU+rwG6PzOokgRWk+PSp2ZVsFT2xfM6dg527eq6L5IL5asRVZX9zZg==} dev: false + /@lit-protocol/nacl@2.1.62: + resolution: {integrity: sha512-0v9fa6Sd4xphjlYMZ9L8TTyR7G4YLvp323E8OJ76giuaPla4HXuwSiGMzUOaC6NKraArSrd54CKkHJ/bxEqVDA==} + dev: false + + /@lit-protocol/node-client@2.1.62(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0): + resolution: {integrity: sha512-rLEUleDoJ+AATZfWNWXvy7UdSrUXMyCjpyB5bevVfk9YjIa5rd9BBXdFENCIA+9kLgVOgtND/R1PpEI/vZkMmw==} + dependencies: + '@lit-protocol/access-control-conditions': 2.1.62 + '@lit-protocol/auth-browser': 2.1.62(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0) + '@lit-protocol/bls-sdk': 2.1.62 + '@lit-protocol/constants': 2.1.62 + '@lit-protocol/crypto': 2.1.62 + '@lit-protocol/ecdsa-sdk': 2.1.62 + '@lit-protocol/encryption': 2.1.62 + '@lit-protocol/lit-third-party-libs': 2.1.62 + '@lit-protocol/misc': 2.1.62 + '@lit-protocol/misc-browser': 2.1.62 + '@lit-protocol/nacl': 2.1.62 + '@lit-protocol/types': 2.1.62 + '@lit-protocol/uint8arrays': 2.1.62 + '@walletconnect/ethereum-provider': 2.9.2(@walletconnect/modal@2.6.1) + ethers: 5.7.2 + jszip: 3.10.1 + lit-connect-modal: 0.1.11 + lit-siwe: 1.1.8(@ethersproject/contracts@5.7.0)(@ethersproject/hash@5.7.0)(@ethersproject/providers@5.7.2)(@ethersproject/wallet@5.7.0) + node-fetch: 2.6.12 + tslib: 2.5.0 + tweetnacl: 1.0.3 + tweetnacl-util: 0.15.1 + transitivePeerDependencies: + - '@ethersproject/contracts' + - '@ethersproject/hash' + - '@ethersproject/providers' + - '@ethersproject/wallet' + - '@react-native-async-storage/async-storage' + - '@walletconnect/modal' + - bufferutil + - encoding + - lokijs + - utf-8-validate + dev: false + /@lit-protocol/types@2.1.161: resolution: {integrity: sha512-d/PGjmuREERHghcxGOnq4SA0ivX8e3tta7eXEKKV7FfsUXW6fNsSfwaDRKUK3FtlhaRvKtdlatrrZP4emG1WSA==} dev: false + /@lit-protocol/types@2.1.62: + resolution: {integrity: sha512-DoIOmbI+Bg3zLWzqx4fLv1vW3k1sbDof/fxslHsLt5aX/MXHSZVKTJb+jWgNVcQ4ba+YLqgoKaPb1i58DMvCPw==} + dev: false + /@lit-protocol/uint8arrays@2.1.161: resolution: {integrity: sha512-EjW6mn9+DzI27lUV/1vyUoZV59hOJwKQ7UkjUdPDouW7EOcMhvpScYhgsYEx67Zv0hiiP8XWLBiDGZD4U2qVvg==} dev: false + /@lit-protocol/uint8arrays@2.1.62: + resolution: {integrity: sha512-Q9Leppzyb9Y2jwe+i8btAUkTXqgnu21PFql83v6N70dkELSC+fKBzRSRqUpFqruW7dcrG8mNWsOCQbQ0kL/w/w==} + dev: false + /@lit/reactive-element@1.6.1: resolution: {integrity: sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==} dependencies: @@ -6773,7 +7240,7 @@ packages: '@ethereumjs/block': 3.6.3 ethereumjs-util: 7.1.5 merkle-patricia-tree: 4.2.4 - node-fetch: 2.6.9 + node-fetch: 2.6.12 transitivePeerDependencies: - encoding dev: false @@ -6785,7 +7252,7 @@ packages: '@ethereumjs/block': 3.6.3 ethereumjs-util: 7.1.5 merkle-patricia-tree: 4.2.4 - node-fetch: 2.6.9 + node-fetch: 2.6.12 transitivePeerDependencies: - encoding dev: false @@ -10844,8 +11311,8 @@ packages: resolution: {integrity: sha512-xUeFPpElybgn1a+lknqtHleei4VyuV/4qWgB1nP8qQUAO6a5pNsioODrnB2VAPdUHJYBdx2dCt2maRk6g53IPQ==} dependencies: '@walletconnect/heartbeat': 1.2.1 - '@walletconnect/jsonrpc-provider': 1.0.12 - '@walletconnect/jsonrpc-utils': 1.0.7 + '@walletconnect/jsonrpc-provider': 1.0.13 + '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.11 '@walletconnect/keyvaluestorage': 1.0.2 '@walletconnect/logger': 2.0.1 @@ -11045,6 +11512,7 @@ packages: dependencies: keyvaluestorage-interface: 1.0.0 tslib: 1.14.1 + dev: false /@walletconnect/jsonrpc-types@1.0.3: resolution: {integrity: sha512-iIQ8hboBl3o5ufmJ8cuduGad0CQm3ZlsHtujv9Eu16xq89q+BG7Nh5VLxxUgmtpnrePgFkTwXirCTkwJH1v+Yw==} @@ -11070,7 +11538,7 @@ packages: /@walletconnect/jsonrpc-ws-connection@1.0.11: resolution: {integrity: sha512-TiFJ6saasKXD+PwGkm5ZGSw0837nc6EeFmurSPgIT/NofnOV4Tv7CVJqGQN0rQYoJUSYu21cwHNYaFkzNpUN+w==} dependencies: - '@walletconnect/jsonrpc-utils': 1.0.7 + '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/safe-json': 1.0.2 events: 3.3.0 tslib: 1.14.1 @@ -11200,7 +11668,7 @@ packages: /@walletconnect/relay-api@1.0.9: resolution: {integrity: sha512-Q3+rylJOqRkO1D9Su0DPE3mmznbAalYapJ9qmzDgK28mYF9alcP3UwG/og5V7l7CFOqzCLi7B8BvcBUrpDj0Rg==} dependencies: - '@walletconnect/jsonrpc-types': 1.0.2 + '@walletconnect/jsonrpc-types': 1.0.3 tslib: 1.14.1 /@walletconnect/relay-auth@1.0.4: @@ -11617,6 +12085,27 @@ packages: tslib: 2.5.0 dev: true + /@wry/context@0.7.3: + resolution: {integrity: sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA==} + engines: {node: '>=8'} + dependencies: + tslib: 2.5.0 + dev: false + + /@wry/equality@0.5.6: + resolution: {integrity: sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA==} + engines: {node: '>=8'} + dependencies: + tslib: 2.5.0 + dev: false + + /@wry/trie@0.4.3: + resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==} + engines: {node: '>=8'} + dependencies: + tslib: 2.5.0 + dev: false + /@xtuc/ieee754@1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} dev: false @@ -13856,6 +14345,10 @@ packages: engines: {node: '>=10'} dev: false + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: false + /decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} dependencies: @@ -16308,7 +16801,6 @@ packages: dependencies: graphql: 16.8.0 tslib: 2.5.0 - dev: true /graphql-ws@5.12.1(graphql@16.8.0): resolution: {integrity: sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg==} @@ -17027,8 +17519,8 @@ packages: it-to-stream: 1.0.0 merge-options: 3.0.4 nanoid: 3.3.6 - native-fetch: 3.0.0(node-fetch@2.6.9) - node-fetch: 2.6.9 + native-fetch: 3.0.0(node-fetch@2.6.12) + node-fetch: 2.6.12 react-native-fetch-api: 3.0.0 stream-to-it: 0.2.4 transitivePeerDependencies: @@ -17986,6 +18478,10 @@ packages: resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==} dev: false + /jwt-decode@3.1.2: + resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} + dev: false + /keccak@3.0.3: resolution: {integrity: sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==} engines: {node: '>=10.0.0'} @@ -19786,6 +20282,14 @@ packages: resolution: {integrity: sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==} dev: false + /native-fetch@3.0.0(node-fetch@2.6.12): + resolution: {integrity: sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==} + peerDependencies: + node-fetch: '*' + dependencies: + node-fetch: 2.6.12 + dev: false + /native-fetch@3.0.0(node-fetch@2.6.9): resolution: {integrity: sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==} peerDependencies: @@ -20228,10 +20732,19 @@ packages: asn1.js: 5.4.1 dev: false + /optimism@0.17.5: + resolution: {integrity: sha512-TEcp8ZwK1RczmvMnvktxHSF2tKgMWjJ71xEFGX5ApLh67VsMSTy1ZUlipJw8W+KaqgOmQ+4pqwkeivY89j+4Vw==} + dependencies: + '@wry/context': 0.7.3 + '@wry/trie': 0.4.3 + tslib: 2.5.0 + dev: false + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 @@ -21153,7 +21666,7 @@ packages: bl: 5.1.0 debug: 4.3.4(supports-color@5.5.0) minimist: 1.2.8 - node-fetch: 2.6.9 + node-fetch: 2.6.12 readable-stream: 3.6.2 transitivePeerDependencies: - encoding @@ -21789,6 +22302,11 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /response-iterator@0.2.6: + resolution: {integrity: sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==} + engines: {node: '>=0.8'} + dev: false + /responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: @@ -22837,6 +23355,11 @@ packages: engines: {node: '>=0.10'} dev: false + /symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + dev: false + /synckit@0.8.5: resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} engines: {node: ^14.18.0 || >=16.0.0} @@ -23119,6 +23642,13 @@ packages: /ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + /ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + dependencies: + tslib: 2.5.0 + dev: false + /ts-log@2.2.5: resolution: {integrity: sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==} dev: true @@ -24758,6 +25288,16 @@ packages: engines: {node: '>=12.20'} dev: true + /zen-observable-ts@1.2.5: + resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} + dependencies: + zen-observable: 0.8.15 + dev: false + + /zen-observable@0.8.15: + resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + dev: false + /zod-to-json-schema@3.21.4(zod@3.21.4): resolution: {integrity: sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw==} peerDependencies: diff --git a/public/integrations/lensprotocol-dark.svg b/public/integrations/lensprotocol-dark.svg new file mode 100644 index 00000000..e8733fae --- /dev/null +++ b/public/integrations/lensprotocol-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/integrations/lensprotocol-light.svg b/public/integrations/lensprotocol-light.svg new file mode 100644 index 00000000..bca241f5 --- /dev/null +++ b/public/integrations/lensprotocol-light.svg @@ -0,0 +1,3 @@ + + +