From 7257e9b9ce65e929d4c15ed03a267366e2f78da3 Mon Sep 17 00:00:00 2001 From: Geolffrey Mena Date: Thu, 23 Jan 2025 15:06:25 -0600 Subject: [PATCH 01/12] chore: remove logs on production --- vite.config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index 5f9f2982..f5d671fb 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,8 +8,10 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills' export default defineConfig(({ mode }) => { // Load environment variables based on the current mode const env = loadEnv(mode, process.cwd(), ''); - + const pure = mode === 'production' ? ['console.log', 'console.info'] : [] + return { + esbuild: { pure }, plugins: [ react(), preserveDirectives(), From 3beeb4e71c91c61c50e2ae8fdbca268d95ed2e71 Mon Sep 17 00:00:00 2001 From: jadapema Date: Thu, 23 Jan 2025 15:49:14 -0600 Subject: [PATCH 02/12] fix: bundler client null --- src/components/video-player/video-player.tsx | 4 ++-- src/hooks/use-web3-session.ts | 21 ++++--------------- .../view/publication-details-view.tsx | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/components/video-player/video-player.tsx b/src/components/video-player/video-player.tsx index 2bdca479..b32acc9c 100644 --- a/src/components/video-player/video-player.tsx +++ b/src/components/video-player/video-player.tsx @@ -35,10 +35,10 @@ export const VideoPlayer: FC = ({ src, titleMovie, onBack, sho if (event.key === 'Escape' || event.key === 'Esc') onBack?.(); }; - document.addEventListener('keydown', handleKeyDown); + document?.addEventListener('keydown', handleKeyDown); return () => { - document.removeEventListener('keydown', handleKeyDown); + document?.removeEventListener('keydown', handleKeyDown); }; }, [onBack]); diff --git a/src/hooks/use-web3-session.ts b/src/hooks/use-web3-session.ts index 68f6b633..f9ec0ce2 100644 --- a/src/hooks/use-web3-session.ts +++ b/src/hooks/use-web3-session.ts @@ -1,4 +1,3 @@ -import { useMemo } from 'react'; import { useWeb3Auth } from '@src/hooks/use-web3-auth'; /** @@ -9,22 +8,10 @@ import { useWeb3Auth } from '@src/hooks/use-web3-auth'; */ export function useWeb3Session() { const { web3Auth } = useWeb3Auth(); - const accountAbstractionProvider = web3Auth?.options?.accountAbstractionProvider; - - const bundlerClient = useMemo(() => { - // @ts-ignore - return accountAbstractionProvider?.bundlerClient || null; - }, [accountAbstractionProvider]); - - const smartAccount = useMemo(() => { - // @ts-ignore - return accountAbstractionProvider?.smartAccount || null; - }, [accountAbstractionProvider]); - - const provider = useMemo(() => { - // @ts-ignore - return accountAbstractionProvider?.provider || null; - }, [accountAbstractionProvider]); + const accountAbstractionProvider: any = web3Auth?.options?.accountAbstractionProvider; + const bundlerClient = accountAbstractionProvider?.bundlerClient; + const smartAccount = accountAbstractionProvider?.smartAccount; + const provider = accountAbstractionProvider?.provider; return { bundlerClient, diff --git a/src/sections/publication/view/publication-details-view.tsx b/src/sections/publication/view/publication-details-view.tsx index b9c90a50..ce6340a8 100644 --- a/src/sections/publication/view/publication-details-view.tsx +++ b/src/sections/publication/view/publication-details-view.tsx @@ -169,7 +169,7 @@ export default function PublicationDetailsView({ id }: Props) { justifyContent: 'center', }} > - {hasAccess ? ( + {hasAccess && sessionData?.authenticated ? ( ) : ( Date: Thu, 23 Jan 2025 16:16:35 -0600 Subject: [PATCH 03/12] fix: session data anonymous --- src/components/publication-detail-main.tsx | 4 ++-- src/components/subscribe-to-unlock-card.tsx | 3 +-- src/hooks/use-account-session.ts | 4 ++-- src/sections/publication/view/publication-details-view.tsx | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/publication-detail-main.tsx b/src/components/publication-detail-main.tsx index 6466a10a..115ede6f 100644 --- a/src/components/publication-detail-main.tsx +++ b/src/components/publication-detail-main.tsx @@ -224,7 +224,7 @@ export default function PublicationDetailMain({ sx={{ width: 26, height: 26, - border: (theme) => `solid 2px ${theme.palette.background.default}`, + border: (theme: any) => `solid 2px ${theme.palette.background.default}`, }} /> @@ -338,7 +338,7 @@ export default function PublicationDetailMain({ pr: 1, }} > - {hasAccess ? ( + {hasAccess && sessionData?.authenticated ? ( // @ts-ignore ) : ( diff --git a/src/components/subscribe-to-unlock-card.tsx b/src/components/subscribe-to-unlock-card.tsx index 56120bb3..d0d6d0a5 100644 --- a/src/components/subscribe-to-unlock-card.tsx +++ b/src/components/subscribe-to-unlock-card.tsx @@ -16,7 +16,6 @@ interface Props { export const SubscribeToUnlockCard = ({ onSubscribe, loadingSubscribe, - subscribeDisabled, post, }: Props) => { const { terms } = useGetPolicyTerms( @@ -58,7 +57,7 @@ export const SubscribeToUnlockCard = ({ sx={{ width: '100%', py: 1.5 }} onClick={onSubscribe} loading={loadingSubscribe} - disabled={subscribeDisabled} + // disabled={subscribeDisabled} > Join diff --git a/src/hooks/use-account-session.ts b/src/hooks/use-account-session.ts index d64fcde2..067407e4 100644 --- a/src/hooks/use-account-session.ts +++ b/src/hooks/use-account-session.ts @@ -65,11 +65,11 @@ export const useAccountSession = (): UseAccountSessionHook => { // wait for web3auth ready state and allow bypass if if ((isPending() || loading) && !data?.authenticated) return; // is authenticated avoid re-run code below - if (sessionData?.authenticated) return; + if (sessionData?.authenticated || data?.type === 'ANONYMOUS') return; // dispatch the session data and turn off the loading dispatch(setSession({ session: data })) dispatch(setAuthLoading({ isSessionLoading: false })); - }, [isSessionLoading]); + }, [isSessionLoading, data]); return { logout: handleSessionExpired, diff --git a/src/sections/publication/view/publication-details-view.tsx b/src/sections/publication/view/publication-details-view.tsx index ce6340a8..0bc51448 100644 --- a/src/sections/publication/view/publication-details-view.tsx +++ b/src/sections/publication/view/publication-details-view.tsx @@ -236,7 +236,7 @@ export default function PublicationDetailsView({ id }: Props) { zIndex: 2, }} onClick={handleSubscribe} - disabled={accessLoading || hasAccess || accessFetchingLoading} + // disabled={accessLoading || hasAccess || accessFetchingLoading} loading={accessLoading || accessFetchingLoading} > From 4e98039c4af438cc1f04fdef3c8244888367d811 Mon Sep 17 00:00:00 2001 From: Geolffrey Mena Date: Thu, 23 Jan 2025 16:32:56 -0600 Subject: [PATCH 04/12] chore: remove consoles --- vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index f5d671fb..66161996 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,7 +8,7 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills' export default defineConfig(({ mode }) => { // Load environment variables based on the current mode const env = loadEnv(mode, process.cwd(), ''); - const pure = mode === 'production' ? ['console.log', 'console.info'] : [] + const pure = mode === 'production' ? ['console.log', 'console.info', 'console.warn'] : [] return { esbuild: { pure }, From 7c85c89c383540bf7f97ab496f08eea56061d157 Mon Sep 17 00:00:00 2001 From: Geolffrey Mena Date: Thu, 23 Jan 2025 18:30:07 -0600 Subject: [PATCH 05/12] fix: infinite loop in session --- src/hooks/use-account-session.ts | 2 +- vite.config.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/use-account-session.ts b/src/hooks/use-account-session.ts index 067407e4..bf073540 100644 --- a/src/hooks/use-account-session.ts +++ b/src/hooks/use-account-session.ts @@ -69,7 +69,7 @@ export const useAccountSession = (): UseAccountSessionHook => { // dispatch the session data and turn off the loading dispatch(setSession({ session: data })) dispatch(setAuthLoading({ isSessionLoading: false })); - }, [isSessionLoading, data]); + }, [isSessionLoading, data?.authenticated, data?.type]); return { logout: handleSessionExpired, diff --git a/vite.config.ts b/vite.config.ts index 66161996..8fb581bc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -41,4 +41,5 @@ export default defineConfig(({ mode }) => { 'process.env': env, // Make sure to define process.env for compatibility }, }; + }); From d963910a075363aa9a6a2b2e7adf511d92178232 Mon Sep 17 00:00:00 2001 From: Carlos Andres Perez Ubeda Date: Fri, 24 Jan 2025 15:34:08 -0600 Subject: [PATCH 06/12] feat(profile): refactor profile header with modular components - Refactored `profile-header.tsx` to simplify and modularize components. - Added `ProfileReport` to handle report functionality for authenticated users. - Added `ProfileRightSidebar` for displaying profile-related sidebar content. - Integrated `ProfileToolbar` for toolbar functionalities such as share options. - Added `ProfileUserInfo` for structured user information display. - Introduced `ProfileJoin` for manage joining features (subscription or follow). This modularization simplifies `ProfileHeader`, reduces complexity, and makes it easier to maintain. --- src/sections/user/profile-header.tsx | 745 +----------------- src/sections/user/profile-join.tsx | 57 ++ src/sections/user/profile-report.tsx | 85 ++ src/sections/user/profile-right-sidebar.tsx | 171 ++++ .../user/profile-set-joining-price.tsx | 70 ++ src/sections/user/profile-share.tsx | 259 ++++++ src/sections/user/profile-toolbar.tsx | 68 ++ src/sections/user/profile-update-button.tsx | 73 ++ src/sections/user/profile-user-info.tsx | 60 ++ src/sections/user/profile-wrapper.tsx | 44 ++ 10 files changed, 912 insertions(+), 720 deletions(-) create mode 100644 src/sections/user/profile-join.tsx create mode 100644 src/sections/user/profile-report.tsx create mode 100644 src/sections/user/profile-right-sidebar.tsx create mode 100644 src/sections/user/profile-set-joining-price.tsx create mode 100644 src/sections/user/profile-share.tsx create mode 100644 src/sections/user/profile-toolbar.tsx create mode 100644 src/sections/user/profile-update-button.tsx create mode 100644 src/sections/user/profile-user-info.tsx create mode 100644 src/sections/user/profile-wrapper.tsx diff --git a/src/sections/user/profile-header.tsx b/src/sections/user/profile-header.tsx index 875c0eaf..eb68b999 100644 --- a/src/sections/user/profile-header.tsx +++ b/src/sections/user/profile-header.tsx @@ -1,20 +1,12 @@ // REACT IMPORTS -import { PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react'; +import { PropsWithChildren } from 'react'; // Redux -import { openLoginModal } from '@redux/auth'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; // MUI IMPORTS import Box from '@mui/material/Box'; import Stack from '@mui/material/Stack'; -import Button from '@mui/material/Button'; -import Divider from '@mui/material/Divider'; -import Popover from '@mui/material/Popover'; -import MenuItem from '@mui/material/MenuItem'; -import { styled, useTheme } from '@mui/material/styles'; -import Typography from '@mui/material/Typography'; -import LoadingButton from '@mui/lab/LoadingButton'; import { Profile } from '@lens-protocol/api-bindings'; import CircularProgress from '@mui/material/CircularProgress'; @@ -24,107 +16,37 @@ import { appId, PublicationType, usePublications } from '@lens-protocol/react-we // VIEM IMPORTS import { Address } from 'viem'; -// ICONS IMPORTS -import { IconDots, IconRosetteDiscountCheckFilled } from '@tabler/icons-react'; - // LOCAL IMPORTS import ProfileCover from './profile-cover'; -import Iconify from '@src/components/iconify'; -import { truncateAddress } from '@src/utils/wallet'; import { GLOBAL_CONSTANTS } from '@src/config-global.ts'; -import { UpdateModal } from '@src/components/update-modal'; import { useHasAccess } from '@src/hooks/use-has-access.ts'; -import { CopyableText } from '@src/components/copyable-text/index.ts'; -import { ReportProfileModal } from '@src/components/report-profile-modal.tsx'; import { useIsPolicyAuthorized } from '@src/hooks/use-is-policy-authorized.ts'; -import { SubscribeProfileModal } from '@src/components/subscribe-profile-modal.tsx'; -import { ActivateSubscriptionProfileModal } from '@src/components/activate-subscription-profile-modal.tsx'; import FollowUnfollowButton from '@src/components/follow-unfollow-button.tsx'; -import { randomColors } from '@src/components/poster/variants/poster-latest-content.tsx'; -import { OpenableText } from '@src/components/openable-text/index.ts'; import { useGetPolicyAttestation } from '@src/hooks/use-get-policy-attestation.ts'; -// Notifcations -import { notifyError, notifySuccess } from '@notifications/internal-notifications.ts'; -import { SUCCESS } from '@notifications/success.ts'; -import { ERRORS } from '@notifications/errors.ts'; -import AvatarProfile from "@src/components/avatar/avatar.tsx"; -import BadgeVerified from "@src/components/user-item/BadgeVerified.tsx"; - -// ---------------------------------------------------------------------- - -const urlToShare = 'https://app.watchit.movie/profileId'; -const urlAttestationBase = 'https://polygon-amoy.easscan.org/attestation/view/'; - -const shareLinks = [ - { - icon: 'mingcute:social-x-line', - label: 'X', - url: `https://x.com/share/?url=${encodeURIComponent(urlToShare)}&text=Visit%20my%20profile%20on%20Watchit&hashtags=Watchit,Blockchain,Crypto`, - }, - { - icon: 'mdi:facebook', - label: 'Facebook', - url: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(urlToShare)}`, - }, - { - icon: 'mdi:telegram', - label: 'Telegram', - url: `https://telegram.me/share/?url=${encodeURIComponent(urlToShare)}&title=Watchit`, - }, -]; - -const socialMedia = [ - { key: 'twitter', icon: 'mingcute:social-x-line' }, - { key: 'facebook', icon: 'mdi:facebook' }, - { key: 'instagram', icon: 'mdi:instagram' }, -]; +// Profile Components +import ProfileReport from '@src/sections/user/profile-report.tsx'; +import ProfileRightSidebar from "@src/sections/user/profile-right-sidebar.tsx"; +import ProfileJoin from "@src/sections/user/profile-join.tsx"; +import ProfileUserInfo from "@src/sections/user/profile-user-info.tsx"; +import ProfileWrapper from './profile-wrapper'; +import ProfileToolbar from "@src/sections/user/profile-toolbar.tsx"; // ---------------------------------------------------------------------- - -interface ProfileHeaderProps { +export interface ProfileHeaderProps { profile: Profile; } -interface SocialMediaUrls { - twitter?: string; - facebook?: string; - instagram?: string; -} - -const prependProfileIdToUrl = (url: string, profileId: string) => { - return url.replace('profileId', 'profile/' + profileId); -}; - // ---------------------------------------------------------------------- - const ProfileHeader = ({ profile: profileData, children, }: PropsWithChildren) => { - const dispatch = useDispatch(); - const navRef = useRef(null); - const navRefSocial = useRef(null); - const navRefSettings = useRef(null); - const [openTooltip, setOpenTooltip] = useState(false); - const [openTooltipShare, setOpenTooltipShare] = useState(false); - const [openTooltipSettings, setOpenTooltipSettings] = useState(false); - - const theme = useTheme(); const sessionData = useSelector((state: any) => state.auth.session); - const [anchorEl, setAnchorEl] = useState(null); - const [menuAnchorEl, setMenuAnchorEl] = useState(null); - const [openReportModal, setOpenReportModal] = useState(false); - const [openSubscribeModal, setOpenSubscribeModal] = useState(false); - const open = Boolean(anchorEl); - const openMenu = Boolean(menuAnchorEl); + const profile = sessionData && sessionData?.profile?.id === profileData?.id ? sessionData.profile : profileData; - // State to handle error and success messages - const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); - const [isActivateModalOpen, setIsActivateModalOpen] = useState(false); - const { attestation, loading: attestationLoading, @@ -145,38 +67,6 @@ const ProfileHeader = ({ profile?.ownedBy?.address as Address ); - const attestationAddress = `0x${BigInt(attestation ?? '').toString(16)}`; - - const handleClose = useCallback(() => { - setOpenTooltip(false); - }, []); - - useEffect(() => { - if (open) { - handleClose(); - } - }, [handleClose, open]); - - const handleOpen = useCallback(() => { - setOpenTooltip(true); - }, []); - - const handleOpenShare = useCallback(() => { - setOpenTooltipShare(true); - }, []); - - const handleCloseShare = useCallback(() => { - setOpenTooltipShare(false); - }, []); - - const handleOpenSettings = useCallback(() => { - setOpenTooltipSettings(true); - }, []); - - const handleCloseSettings = useCallback(() => { - setOpenTooltipSettings(false); - }, []); - usePublications({ where: { from: [...(profile?.id ? [profile.id] : [])], @@ -187,277 +77,34 @@ const ProfileHeader = ({ }, }); - const socialMediaUrls: SocialMediaUrls = - profile?.metadata?.attributes?.reduce((acc: SocialMediaUrls, attr: any) => { - if (['twitter', 'facebook', 'instagram'].includes(attr.key)) { - acc[attr.key as keyof SocialMediaUrls] = attr.value; - } - return acc; - }, {} as SocialMediaUrls) || {}; - // Function to handle following a profile const onSubscribe = async () => { refetchAccess(); refetchAttestation(); }; - const handlePopoverOpen = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handlePopoverClose = () => { - setAnchorEl(null); - }; - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText( - urlToShare.replace('profileId', 'profile/' + profile?.id) - ); - notifySuccess(SUCCESS.LINK_COPIED_TO_CLIPBOARD); - } catch (err) { - notifyError(ERRORS.LINK_COPIED_ERROR); - } - }; - - const handleSubscription = async () => { - if (!sessionData?.authenticated) return dispatch(openLoginModal()); - - if (!hasAccess) setOpenSubscribeModal(true); - }; - const profileImage = (profile?.metadata?.picture as any)?.optimized?.uri; return ( <> - + - {sessionData?.authenticated ? ( - - ) : ( - <> - )} + {sessionData?.authenticated ? : <>} - setMenuAnchorEl(null)} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - transformOrigin={{ vertical: 'top', horizontal: 'center' }} - PaperProps={{ - sx: { - background: 'linear-gradient(90deg, #1C1C1E, #2C2C2E)', - borderRadius: 1, - p: 1, - display: 'flex', - flexDirection: 'column', - ml: -3, - alignItems: 'center', - boxShadow: '0 4px 12px rgba(0, 0, 0, 0.5)', - }, - }} - > - - { - setOpenReportModal(true); - setMenuAnchorEl(null); - }} - > - Report - - - + }> + + - - - - - {socialMedia.map( - ({ key, icon }) => - socialMediaUrls[key as keyof SocialMediaUrls] && ( - - ) - )} - - - Share - - - + - - - - - {profile?.metadata?.displayName ?? ''} - - - {profile?.handle?.localName ?? ''} - - - - {profile?.metadata?.bio ?? ''} - - {authorizedLoading && ( )} - {isAuthorized && !authorizedLoading && profile?.id !== sessionData?.profile?.id && ( - - {hasAccess ? 'Joined!' : 'Join'} - - )} - - {sessionData?.authenticated && sessionData?.profile?.id === profile?.id ? ( - <> - - - - Set joining pricing - - - ) : ( - <> - )} + {isAuthorized && !authorizedLoading && profile?.id !== sessionData?.profile?.id && } {profile?.id !== sessionData?.profile?.id && ( )} - - {sessionData?.profile && profile?.id === sessionData?.profile?.id && ( - <> - - - Update your profile information - - - )} - - - - Share link to this page - - - {shareLinks.map((item, index) => ( - - - - {item.label} - - - ))} - - - - Copy - - - - - - - - Lens ID - - - - - Address - - - {isAuthorized && - !authorizedLoading && - attestation && - !attestationLoading && - hasAccess && - !accessLoading && - profile?.id !== sessionData?.profile?.id && ( - <> - - - License - - - - )} - - - Distribution Partners - - {['Watchit'].map((partner, index) => ( - - - {partner} - - - - ))} - - - - - + {children} - - setIsUpdateModalOpen(false)} /> - - setOpenSubscribeModal(false)} - onSubscribe={onSubscribe} - profile={profile} - /> - - setIsActivateModalOpen(false)} - /> - - setOpenReportModal(false)} - /> ); }; export default ProfileHeader; - -const StyledBoxGradient = styled(Box)<{ color1?: string; color2?: string }>(({ - theme, - color1, - color2, -}) => { - const defaultColor1 = theme.palette.primary.main; - const defaultColor2 = theme.palette.secondary.main; - - return { - background: `linear-gradient(300deg, ${color1 || defaultColor1} 0%, ${color2 || defaultColor2} 25%, ${color1 || defaultColor1} 50%, ${color2 || defaultColor2} 75%, ${color1 || defaultColor1} 100%)`, - backgroundSize: '400%', - animation: 'gradientShift 20s infinite', - padding: '4px 8px', - borderRadius: 20, - display: 'flex', - alignItems: 'center', - width: 'fit-content', - gap: '3px', - '@keyframes gradientShift': { - '0%': { backgroundPosition: '0% 50%' }, - '50%': { backgroundPosition: '100% 50%' }, - '100%': { backgroundPosition: '0% 50%' }, - }, - }; -}); diff --git a/src/sections/user/profile-join.tsx b/src/sections/user/profile-join.tsx new file mode 100644 index 00000000..41fd98ba --- /dev/null +++ b/src/sections/user/profile-join.tsx @@ -0,0 +1,57 @@ +import LoadingButton from "@mui/lab/LoadingButton"; +import {SubscribeProfileModal} from "@src/components/subscribe-profile-modal.tsx"; +import {ProfileHeaderProps} from "@src/sections/user/profile-header.tsx"; +import {FC, useState} from "react"; +import {openLoginModal} from "@redux/auth"; +import {useDispatch, useSelector} from "react-redux"; + +interface ProfileJoinProps extends ProfileHeaderProps{ + profileJoinProps: { + hasAccess?: boolean; + accessLoading: boolean; + accessFetchingLoading: boolean; + onSubscribe: () => void; + } +} + +const ProfileJoin: FC = ({profile, profileJoinProps}) => { + const dispatch = useDispatch(); + const sessionData = useSelector((state: any) => state.auth.session); + + const {hasAccess, accessLoading, accessFetchingLoading, onSubscribe} = profileJoinProps; + + const [openSubscribeModal, setOpenSubscribeModal] = useState(false); + + const handleSubscription = async () => { + if (!sessionData?.authenticated) return dispatch(openLoginModal()); + + if (!hasAccess) setOpenSubscribeModal(true); + }; + + + return (<> + + {hasAccess ? 'Joined!' : 'Join'} + + + setOpenSubscribeModal(false)} + onSubscribe={onSubscribe} + profile={profile} + /> + + ) +} + +export default ProfileJoin diff --git a/src/sections/user/profile-report.tsx b/src/sections/user/profile-report.tsx new file mode 100644 index 00000000..28d2816e --- /dev/null +++ b/src/sections/user/profile-report.tsx @@ -0,0 +1,85 @@ +import { IconDots } from "@tabler/icons-react"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import MenuItem from "@mui/material/MenuItem"; +import Popover from "@mui/material/Popover"; +import { FC, useState } from "react"; +import { ReportProfileModal } from "@src/components/report-profile-modal.tsx"; +import { Profile } from "@lens-protocol/api-bindings"; +import styled from "@emotion/styled"; + +interface ProfileReportProps { + profile: Profile; +} + +const StyledButton = styled(Button)` + border-color: #ffffff; + color: #ffffff; + height: 40px; + min-width: 40px; + position: absolute; + z-index: 1; + right: 5px; + top: 5px; +`; + +const StyledPopover = styled(Popover)` + .MuiPaper-root { + background: linear-gradient(90deg, #1c1c1e, #2c2c2e); + border-radius: 8px; + padding: 8px; + display: flex; + flex-direction: column; + margin-left: -24px; + align-items: center; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); + } +`; + +const StyledMenuItem = styled(MenuItem)` + padding: 8px; +`; + +const ProfileReport: FC = ({ profile }) => { + const [openReportModal, setOpenReportModal] = useState(false); + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const openMenu = Boolean(menuAnchorEl); + + return ( + <> + setMenuAnchorEl(event.currentTarget)} + > + + + + setMenuAnchorEl(null)} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + transformOrigin={{ vertical: 'top', horizontal: 'center' }} + > + + { + setOpenReportModal(true); + setMenuAnchorEl(null); + }} + > + Report + + + + + setOpenReportModal(false)} + /> + + ); +}; + +export default ProfileReport; diff --git a/src/sections/user/profile-right-sidebar.tsx b/src/sections/user/profile-right-sidebar.tsx new file mode 100644 index 00000000..8f872e0a --- /dev/null +++ b/src/sections/user/profile-right-sidebar.tsx @@ -0,0 +1,171 @@ +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import {CopyableText} from "@src/components/copyable-text"; +import Divider from "@mui/material/Divider"; +import {truncateAddress} from "@src/utils/wallet.ts"; +import {OpenableText} from "@src/components/openable-text"; +import Box from "@mui/material/Box"; +import {randomColors} from "@src/components/poster/variants/poster-latest-content.tsx"; +import {IconRosetteDiscountCheckFilled} from "@tabler/icons-react"; +import {FC} from "react"; +import {ProfileHeaderProps} from "@src/sections/user/profile-header.tsx"; +import {styled} from "@mui/material/styles"; +import {useSelector} from "react-redux"; + +// ---------------------------------------------------------------------- +const urlAttestationBase = 'https://polygon-amoy.easscan.org/attestation/view/'; + +interface ProfileRightSidebarProps extends ProfileHeaderProps{ + sidebarProps: { + isAuthorized?: boolean; + attestation?: string; + hasAccess?: boolean; + accessLoading: boolean; + authorizedLoading: boolean; + attestationLoading: boolean; + } +} +const ProfileRightSidebar: FC = ({profile, sidebarProps}) => { + const sessionData = useSelector((state: any) => state.auth.session); + const {isAuthorized, authorizedLoading, accessLoading, hasAccess, attestation, attestationLoading} = sidebarProps; + const attestationAddress = `0x${BigInt(attestation ?? '').toString(16)}`; + + return (<> + + + Lens ID + + + + + Address + + + {isAuthorized && + !authorizedLoading && + attestation && + !attestationLoading && + hasAccess && + !accessLoading && + profile?.id !== sessionData?.profile?.id && ( + <> + + + License + + + + )} + + + Distribution Partners + + {['Watchit'].map((partner, index) => ( + + + {partner} + + + + ))} + + + + ); +} + +const StyledBoxGradient = styled(Box)<{ color1?: string; color2?: string }>(({ + theme, + color1, + color2, + }) => { + const defaultColor1 = theme.palette.primary.main; + const defaultColor2 = theme.palette.secondary.main; + + return { + background: `linear-gradient(300deg, ${color1 || defaultColor1} 0%, ${color2 || defaultColor2} 25%, ${color1 || defaultColor1} 50%, ${color2 || defaultColor2} 75%, ${color1 || defaultColor1} 100%)`, + backgroundSize: '400%', + animation: 'gradientShift 20s infinite', + padding: '4px 8px', + borderRadius: 20, + display: 'flex', + alignItems: 'center', + width: 'fit-content', + gap: '3px', + '@keyframes gradientShift': { + '0%': { backgroundPosition: '0% 50%' }, + '50%': { backgroundPosition: '100% 50%' }, + '100%': { backgroundPosition: '0% 50%' }, + }, + }; +}); + +export default ProfileRightSidebar; diff --git a/src/sections/user/profile-set-joining-price.tsx b/src/sections/user/profile-set-joining-price.tsx new file mode 100644 index 00000000..70943424 --- /dev/null +++ b/src/sections/user/profile-set-joining-price.tsx @@ -0,0 +1,70 @@ +import Button from "@mui/material/Button"; +import Iconify from "@src/components/iconify"; +import Popover from "@mui/material/Popover"; +import Typography from "@mui/material/Typography"; +import { useCallback, useRef, useState} from "react"; +import {ActivateSubscriptionProfileModal} from "@src/components/activate-subscription-profile-modal.tsx"; + +const ProfileSetJoiningPrice = () => { + const [isActivateModalOpen, setIsActivateModalOpen] = useState(false); + const navRefSettings = useRef(null); + const [openTooltipSettings, setOpenTooltipSettings] = useState(false); + const open = Boolean(); + + const handleOpenSettings = useCallback(() => { + setOpenTooltipSettings(true); + }, []); + + const handleCloseSettings = useCallback(() => { + setOpenTooltipSettings(false); + }, []); + + + return (<> + + + + Set joining pricing + + + setIsActivateModalOpen(false)} + /> + + ) +} + +export default ProfileSetJoiningPrice diff --git a/src/sections/user/profile-share.tsx b/src/sections/user/profile-share.tsx new file mode 100644 index 00000000..29596bc5 --- /dev/null +++ b/src/sections/user/profile-share.tsx @@ -0,0 +1,259 @@ +import Iconify from '@src/components/iconify'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import Popover from '@mui/material/Popover'; +import { FC, useCallback, useEffect, useRef, useState } from 'react'; +import Stack from '@mui/material/Stack'; +import { notifyError, notifySuccess } from '@notifications/internal-notifications.ts'; +import { SUCCESS } from '@notifications/success.ts'; +import { ERRORS } from '@notifications/errors.ts'; +import { Profile } from '@lens-protocol/api-bindings'; + +export const urlToShare = 'https://app.watchit.movie/profileId'; + +const shareLinks = [ + { + icon: 'mingcute:social-x-line', + label: 'X', + url: `https://x.com/share/?url=${encodeURIComponent(urlToShare)}&text=Visit%20my%20profile%20on%20Watchit&hashtags=Watchit,Blockchain,Crypto`, + }, + { + icon: 'mdi:facebook', + label: 'Facebook', + url: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(urlToShare)}`, + }, + { + icon: 'mdi:telegram', + label: 'Telegram', + url: `https://telegram.me/share/?url=${encodeURIComponent(urlToShare)}&title=Watchit`, + }, +]; + +const socialMedia = [ + { key: 'twitter', icon: 'mingcute:social-x-line' }, + { key: 'facebook', icon: 'mdi:facebook' }, + { key: 'instagram', icon: 'mdi:instagram' }, +]; + +interface SocialMediaUrls { + twitter?: string; + facebook?: string; + instagram?: string; +} + +interface ProfileShareProps { + profile: Profile; +} + +const ProfileShare: FC = ({ profile }) => { + const [openTooltipShare, setOpenTooltipShare] = useState(false); + const navRefSocial = useRef(null); + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const socialMediaUrls: SocialMediaUrls = + profile?.metadata?.attributes?.reduce((acc: SocialMediaUrls, attr: any) => { + if (['twitter', 'facebook', 'instagram'].includes(attr.key)) { + acc[attr.key as keyof SocialMediaUrls] = attr.value; + } + return acc; + }, {} as SocialMediaUrls) || {}; + + const prependProfileIdToUrl = (url: string, profileId: string) => { + return url.replace('profileId', 'profile/' + profileId); + }; + + const handlePopoverOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleOpenShare = useCallback(() => { + setOpenTooltipShare(true); + }, []); + + const handleCloseShare = useCallback(() => { + setOpenTooltipShare(false); + }, []); + + const handlePopoverClose = () => { + setAnchorEl(null); + }; + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText( + urlToShare.replace('profileId', 'profile/' + profile?.id) + ); + notifySuccess(SUCCESS.LINK_COPIED_TO_CLIPBOARD); + } catch (err) { + notifyError(ERRORS.LINK_COPIED_ERROR); + } + }; + + const handleClose = useCallback(() => { + setOpenTooltipShare(false); + }, []); + + useEffect(() => { + if (open) { + handleClose(); + } + }, [handleClose, open]); + + return ( + <> + + {socialMedia.map( + ({ key, icon }) => + socialMediaUrls[key as keyof SocialMediaUrls] && ( + + ) + )} + + + + Share + + + + + Share link to this page + + + {shareLinks.map((item, index) => ( + + + + {item.label} + + + ))} + + + + Copy + + + + + + ); +}; + +export default ProfileShare; diff --git a/src/sections/user/profile-toolbar.tsx b/src/sections/user/profile-toolbar.tsx new file mode 100644 index 00000000..e39132da --- /dev/null +++ b/src/sections/user/profile-toolbar.tsx @@ -0,0 +1,68 @@ +import AvatarProfile from "@src/components/avatar/avatar.tsx"; +import Stack from "@mui/material/Stack"; +import ProfileShare from "@src/sections/user/profile-share.tsx"; +import ProfileSetJoiningPrice from "@src/sections/user/profile-set-joining-price.tsx"; +import ProfileUpdateButton from "@src/sections/user/profile-update-button.tsx"; +import {FC} from "react"; +import {Profile} from "@lens-protocol/api-bindings"; +import {useTheme} from "@mui/material/styles"; +import {useSelector} from "react-redux"; + +interface ProfileToolbarProps { + profile: Profile; + profileImage?: string; +} + +const ProfileToolbar: FC = ({profile, profileImage}) => { + const theme = useTheme(); + const sessionData = useSelector((state: any) => state.auth.session); + return ( + + + + + {sessionData?.authenticated && sessionData?.profile?.id === profile?.id ? ( + + ) : ( + <> + )} + {sessionData?.profile && profile?.id === sessionData?.profile?.id && ( + + )} + + + ) +} + +export default ProfileToolbar; diff --git a/src/sections/user/profile-update-button.tsx b/src/sections/user/profile-update-button.tsx new file mode 100644 index 00000000..24b1931d --- /dev/null +++ b/src/sections/user/profile-update-button.tsx @@ -0,0 +1,73 @@ +import Button from "@mui/material/Button"; +import Iconify from "@src/components/iconify"; +import Popover from "@mui/material/Popover"; +import Typography from "@mui/material/Typography"; +import {UpdateModal} from "@src/components/update-modal"; +import {useCallback, useEffect, useRef, useState} from "react"; + +const ProfileUpdateButton = () => { + const open = Boolean(); + const navRef = useRef(null); + const [openTooltip, setOpenTooltip] = useState(false); + const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); + + const handleOpen = useCallback(() => { + setOpenTooltip(true); + }, []); + + const handleClose = useCallback(() => { + setOpenTooltip(false); + }, []); + + useEffect(() => { + if (open) { + handleClose(); + } + }, [handleClose, open]); + + return (<> + + + Update your profile information + + + setIsUpdateModalOpen(false)} /> + ) +} + +export default ProfileUpdateButton diff --git a/src/sections/user/profile-user-info.tsx b/src/sections/user/profile-user-info.tsx new file mode 100644 index 00000000..5da4755c --- /dev/null +++ b/src/sections/user/profile-user-info.tsx @@ -0,0 +1,60 @@ +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import BadgeVerified from "@src/components/user-item/BadgeVerified.tsx"; +import {FC} from "react"; +import {ProfileHeaderProps} from "@src/sections/user/profile-header.tsx"; +import { Address } from "viem"; + +const ProfileUserInfo: FC = ({profile}) => { + return ( + + + + {profile?.metadata?.displayName ?? ''}{' '} + + + + {profile?.handle?.localName ?? ''} + + + + + {profile?.metadata?.bio ?? ''} + + + ) +} + +export default ProfileUserInfo; diff --git a/src/sections/user/profile-wrapper.tsx b/src/sections/user/profile-wrapper.tsx new file mode 100644 index 00000000..7ad6888b --- /dev/null +++ b/src/sections/user/profile-wrapper.tsx @@ -0,0 +1,44 @@ +import {FC, PropsWithChildren} from "react"; +import Stack from "@mui/material/Stack"; + +interface ProfileWrapperProps extends PropsWithChildren { + sidebar: React.ReactNode; +} + +const ProfileWrapper: FC = ({children, sidebar}) => { + return ( + + + {children} + + + {sidebar} + + + ); +} + +export default ProfileWrapper; From 8682f5fce986f3758514c9f0f1fa916222fcc85c Mon Sep 17 00:00:00 2001 From: jadapema Date: Fri, 24 Jan 2025 16:17:05 -0600 Subject: [PATCH 07/12] fix: delay after quick transfer --- .../finance/components/finance-quick-transfer-modal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sections/finance/components/finance-quick-transfer-modal.tsx b/src/sections/finance/components/finance-quick-transfer-modal.tsx index bd794bd7..ecdc1ad0 100644 --- a/src/sections/finance/components/finance-quick-transfer-modal.tsx +++ b/src/sections/finance/components/finance-quick-transfer-modal.tsx @@ -102,6 +102,8 @@ function FinanceQuickTransferModal({ try { await transfer({ amount, recipient: address ?? '' }); + onFinish(); + const senderId = sessionData?.profile?.id ?? address; // Build the notification payload @@ -136,7 +138,6 @@ function FinanceQuickTransferModal({ notifySuccess(SUCCESS.TRANSFER_CREATED_SUCCESSFULLY, { destination: isSame ? contactInfo?.metadata?.displayName : truncateAddress(address ?? ''), }); - onFinish(); } catch (err: any) { notifyError(ERRORS.TRANSFER_FAILED_ERROR); } From fe7ee419ceb82444166fed3dd1b81d40bb4c8fb9 Mon Sep 17 00:00:00 2001 From: jadapema Date: Fri, 24 Jan 2025 16:45:27 -0600 Subject: [PATCH 08/12] fix: profile tabs on mobile --- src/sections/user/view/user-profile-view.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sections/user/view/user-profile-view.tsx b/src/sections/user/view/user-profile-view.tsx index e08b4e55..dda889b7 100644 --- a/src/sections/user/view/user-profile-view.tsx +++ b/src/sections/user/view/user-profile-view.tsx @@ -126,7 +126,8 @@ const UserProfileView = ({ id }: any) => { width: 1, zIndex: 9, borderBottom: '1px solid rgba(255, 255, 255, 0.08)', - [`& .${tabsClasses.flexContainer}`]: { justifyContent: 'center' }, + [`& .${tabsClasses.flexContainer}`]: { justifyContent: 'center', px: 1, pl: { xs: 10, md: 0 } }, + [`& .${tabsClasses.scroller}`]: { display: 'flex', justifyContent: 'center' }, }} > {tabsWithCounts.map((tab) => ( From 8b1e9234f1ca9a0b60962aaea55869b4bb39f55f Mon Sep 17 00:00:00 2001 From: Carlos Andres Perez Ubeda Date: Fri, 24 Jan 2025 21:38:18 -0600 Subject: [PATCH 09/12] refactor: improve profile referrals table structure - Updated `Invitation` import path in `profile-referrals-table-row.tsx` and `profile-referrals.tsx` to use a type-specific module. - Moved the `status` cell content in `profile-referrals-table-row.tsx` from a separate `TableCell` to inside the conditional rendering within the `profile` column. - Simplified the `TABLE_HEAD` in `profile-referrals.tsx` by removing the redundant `profile` column definition. --- src/sections/user/profile-referrals.tsx | 5 ++--- .../user/view/profile-referrals-table-row.tsx | 13 +++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/sections/user/profile-referrals.tsx b/src/sections/user/profile-referrals.tsx index 974a1416..9dc3f1fc 100644 --- a/src/sections/user/profile-referrals.tsx +++ b/src/sections/user/profile-referrals.tsx @@ -3,7 +3,7 @@ import {FC} from "react"; import Typography from "@mui/material/Typography"; -import {Invitation} from "@src/hooks/use-referrals.ts"; +import {Invitation} from "@src/types/invitation"; import TableContainer from "@mui/material/TableContainer"; import Scrollbar from "@src/components/scrollbar"; import { @@ -25,8 +25,7 @@ interface ProfileReferralsProps { const TABLE_HEAD = [ { id: 'email', label: 'Email'}, - { id: 'status', label: 'Status' }, - { id: 'profile', label: 'Profile' }, + { id: 'status', label: 'Status' } ]; const ProfileReferrals : FC = ({ referrals, loading }) => { diff --git a/src/sections/user/view/profile-referrals-table-row.tsx b/src/sections/user/view/profile-referrals-table-row.tsx index 2ef72e77..1e02af2e 100644 --- a/src/sections/user/view/profile-referrals-table-row.tsx +++ b/src/sections/user/view/profile-referrals-table-row.tsx @@ -9,7 +9,7 @@ import ListItemText from '@mui/material/ListItemText'; import Button from "@mui/material/Button"; // LOCAL IMPORTS -import {Invitation} from "@src/hooks/use-referrals.ts"; +import {Invitation} from "@src/types/invitation"; import AvatarProfile from "@src/components/avatar/avatar.tsx"; import {useRouter} from "@src/routes/hooks"; import {paths} from "@src/routes/paths.ts"; @@ -55,20 +55,17 @@ export default function ProfileReferralsTableRow({ row, selected }: Readonly - - - {capitalizeFirstLetter(status)} - - - { receiver_id ? ( - ) : <> + ) : + {capitalizeFirstLetter(status)} + } + ); From e2f302650d1663f085b94584d2e54428a3459d66 Mon Sep 17 00:00:00 2001 From: jadapema Date: Sat, 25 Jan 2025 10:42:02 -0600 Subject: [PATCH 10/12] feat: added truncate text to name in user card --- src/components/user-item/index.tsx | 41 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/components/user-item/index.tsx b/src/components/user-item/index.tsx index feefbf50..4ba2ec43 100644 --- a/src/components/user-item/index.tsx +++ b/src/components/user-item/index.tsx @@ -151,24 +151,37 @@ interface UserNameAndBadgeProps { address: Address; } -export const UserNameAndBadge : FC = ({ name, address}) => { +export const UserNameAndBadge: FC = ({ name, address }) => { return ( - - - {name} - + justifyContent: 'flex-start', + }} + > + + {name} + + - ); -} +}; + From ed1a8aa4864d53a5f51c046167d213a9b14c85ad Mon Sep 17 00:00:00 2001 From: jadapema Date: Sat, 25 Jan 2025 10:49:03 -0600 Subject: [PATCH 11/12] fix: sonarcloud --- src/sections/user/profile-header.tsx | 92 ++++++++++----------- src/sections/user/profile-right-sidebar.tsx | 6 +- src/sections/user/profile-share.tsx | 4 +- src/sections/user/profile-toolbar.tsx | 2 +- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/sections/user/profile-header.tsx b/src/sections/user/profile-header.tsx index eb68b999..9922f58e 100644 --- a/src/sections/user/profile-header.tsx +++ b/src/sections/user/profile-header.tsx @@ -86,53 +86,51 @@ const ProfileHeader = ({ const profileImage = (profile?.metadata?.picture as any)?.optimized?.uri; return ( - <> - - - - {sessionData?.authenticated ? : <>} - - }> - - - - - - - - - {authorizedLoading && ( - - - - )} - - {isAuthorized && !authorizedLoading && profile?.id !== sessionData?.profile?.id && } - - {profile?.id !== sessionData?.profile?.id && ( - - )} - - - - {children} - - + + + + {sessionData?.authenticated ? : <>} + + }> + + + + + + + + + {authorizedLoading && ( + + + + )} + + {isAuthorized && !authorizedLoading && profile?.id !== sessionData?.profile?.id && } + + {profile?.id !== sessionData?.profile?.id && ( + + )} + + + + {children} + ); }; diff --git a/src/sections/user/profile-right-sidebar.tsx b/src/sections/user/profile-right-sidebar.tsx index 8f872e0a..9458cc07 100644 --- a/src/sections/user/profile-right-sidebar.tsx +++ b/src/sections/user/profile-right-sidebar.tsx @@ -30,7 +30,7 @@ const ProfileRightSidebar: FC = ({profile, sidebarProp const {isAuthorized, authorizedLoading, accessLoading, hasAccess, attestation, attestationLoading} = sidebarProps; const attestationAddress = `0x${BigInt(attestation ?? '').toString(16)}`; - return (<> + return ( = ({profile, sidebarProp > {['Watchit'].map((partner, index) => ( @@ -139,7 +139,7 @@ const ProfileRightSidebar: FC = ({profile, sidebarProp - ); + ); } const StyledBoxGradient = styled(Box)<{ color1?: string; color2?: string }>(({ diff --git a/src/sections/user/profile-share.tsx b/src/sections/user/profile-share.tsx index 29596bc5..c15b35e0 100644 --- a/src/sections/user/profile-share.tsx +++ b/src/sections/user/profile-share.tsx @@ -191,9 +191,9 @@ const ProfileShare: FC = ({ profile }) => { Share link to this page - {shareLinks.map((item, index) => ( + {shareLinks.map((item) => ( = ({profile, profileImage}) => { }} > Date: Sat, 25 Jan 2025 11:08:55 -0600 Subject: [PATCH 12/12] refactor: remove ! uneeded --- src/sections/user/profile-join.tsx | 7 ++----- src/utils/finance-graphs/groupedTransactions.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sections/user/profile-join.tsx b/src/sections/user/profile-join.tsx index 41fd98ba..3da98405 100644 --- a/src/sections/user/profile-join.tsx +++ b/src/sections/user/profile-join.tsx @@ -17,21 +17,18 @@ interface ProfileJoinProps extends ProfileHeaderProps{ const ProfileJoin: FC = ({profile, profileJoinProps}) => { const dispatch = useDispatch(); const sessionData = useSelector((state: any) => state.auth.session); - const {hasAccess, accessLoading, accessFetchingLoading, onSubscribe} = profileJoinProps; - const [openSubscribeModal, setOpenSubscribeModal] = useState(false); const handleSubscription = async () => { if (!sessionData?.authenticated) return dispatch(openLoginModal()); - if (!hasAccess) setOpenSubscribeModal(true); }; return (<> = ({profile, profileJoinProps}) => { disabled={accessLoading || hasAccess || accessFetchingLoading} loading={accessLoading || accessFetchingLoading} > - {hasAccess ? 'Joined!' : 'Join'} + {hasAccess ? 'Joined' : 'Join'} { case 'approved': return 'Approved'; case 'collected': - return 'Paid'; + return 'Settled'; // case 'released': // return 'Released';