Skip to content

Commit

Permalink
Merge pull request #635 from us3r-network/u3-dev
Browse files Browse the repository at this point in the history
beta.20240320
  • Loading branch information
sin-bufan authored Mar 21, 2024
2 parents dc8afed + eda3b30 commit 50ed866
Show file tree
Hide file tree
Showing 20 changed files with 492 additions and 255 deletions.
70 changes: 70 additions & 0 deletions apps/u3/src/components/community/FarcasterMemberItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState } from 'react';
import useFarcasterFollowAction from '@/hooks/social/farcaster/useFarcasterFollowAction';
import useFarcasterUserData from '@/hooks/social/farcaster/useFarcasterUserData';
import { MemberEntity } from '@/services/community/types/community';
import { SocialPlatform } from '@/services/social/types';
import MemberItem from './MemberItem';

const formatFarcasterUserData = (data) => {
const temp: {
[key: string]: { type: number; value: string }[];
} = {};
data.forEach((item) => {
if (temp[item.fid]) {
temp[item.fid].push(item);
} else {
temp[item.fid] = [item];
}
});
return temp;
};
export default function FarcasterMemberItem({
following,
data,
}: {
following: string[];
data: MemberEntity;
}) {
const { fid, data: memberData } = data;
const [followPending, setFollowPending] = useState(false);
const [unfollowPending, setUnfollowPending] = useState(false);
const [followed, setFollowed] = useState(following.includes(fid));

const { followAction, unfollowAction } = useFarcasterFollowAction();

const farcasterUserData = useFarcasterUserData({
fid,
farcasterUserData: formatFarcasterUserData(memberData),
});
const avatar = farcasterUserData.pfp;
const name = farcasterUserData.display || farcasterUserData.fid;
const { bio } = farcasterUserData;
const handle = farcasterUserData.userName;
return (
<MemberItem
data={{
handle,
address: '',
name,
avatar,
bio,
isFollowed: followed,
platforms: [SocialPlatform.Farcaster],
}}
followPending={followPending}
unfollowPending={unfollowPending}
followAction={async () => {
setFollowPending(true);
await followAction(Number(fid));
setFollowed(true);
setFollowPending(false);
}}
unfollowAction={async () => {
setUnfollowPending(true);
await unfollowAction(Number(fid));
setFollowed(false);
setUnfollowPending(false);
}}
/>
);
}
121 changes: 121 additions & 0 deletions apps/u3/src/components/community/MemberItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { ComponentPropsWithRef, useEffect, useMemo } from 'react';
import ColorButton from '../common/button/ColorButton';
import { SocialPlatform } from '@/services/social/types';
import { useXmtpClient } from '@/contexts/message/XmtpClientCtx';
import useCanMessage from '@/hooks/message/xmtp/useCanMessage';
import {
farcasterHandleToBioLinkHandle,
lensHandleToBioLinkHandle,
} from '@/utils/profile/biolink';
import { cn } from '@/lib/utils';
import NavigateToProfileLink from '../profile/info/NavigateToProfileLink';

export type MemberData = {
handle: string;
avatar: string;
name: string;
address: string;
bio: string;
platforms: SocialPlatform[];
isFollowed: boolean;
};
export type MemberItemProps = ComponentPropsWithRef<'div'> & {
data: MemberData;
followPending?: boolean;
unfollowPending?: boolean;
followAction?: () => void;
unfollowAction?: () => void;
};
export default function MemberItem({
data,
followPending,
unfollowPending,
followAction,
unfollowAction,
className,
...props
}: MemberItemProps) {
const { setCanEnableXmtp } = useXmtpClient();
useEffect(() => {
setCanEnableXmtp(true);
}, []);

const { handle, avatar, name, address, bio, platforms, isFollowed } = data;
const { canMessage } = useCanMessage(address);
const { setMessageRouteParams } = useXmtpClient();

const profileIdentity = useMemo(() => {
if (handle.endsWith('.eth')) return handle;
const firstPlatform = platforms?.[0];
switch (firstPlatform) {
case SocialPlatform.Lens:
return lensHandleToBioLinkHandle(handle);
case SocialPlatform.Farcaster:
return farcasterHandleToBioLinkHandle(handle);
default:
return '';
}
}, [handle, platforms]);

const profileUrl = useMemo(() => {
if (profileIdentity) {
return `/u/${profileIdentity}`;
}
return '';
}, [profileIdentity]);
return (
<div
className={cn(
'flex px-[10px] py-[20px] box-border items-start gap-[10px] self-stretch max-sm:py-[10px]',
className
)}
{...props}
>
<NavigateToProfileLink href={profileUrl}>
<img
src={avatar}
alt=""
className="w-[50px] h-[50px] rounded-[50%] max-sm:w-[40px] max-sm:h-[40px]"
/>
</NavigateToProfileLink>

<div className="flex-1 flex flex-col gap-[5px]">
<div>
<NavigateToProfileLink href={profileUrl}>
<span className="text-[#FFF] text-[16px] font-medium">{name}</span>
</NavigateToProfileLink>
</div>

<span className="text-[#718096] text-[12px] font-normal">{handle}</span>

<span className="text-[#FFF] text-[16px] font-normal leading-[20px] break-all">
{bio}
</span>
</div>
<ColorButton
className="bg-[#00D1A7] hover:bg-[#00D1A7] h-[30px] rounded-[20px] text-[12px] font-normal max-sm:h-[30px]"
disabled={followPending || unfollowPending}
onClick={() => {
if (isFollowed) {
unfollowAction?.();
} else {
followAction?.();
}
}}
>
{(() => {
if (followPending) {
return 'Following';
}
if (unfollowPending) {
return 'Unfollowing';
}
if (isFollowed) {
return 'Following';
}
return 'Follow';
})()}
</ColorButton>
</div>
);
}
26 changes: 15 additions & 11 deletions apps/u3/src/components/explore/posts/ImgPostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ function FirstCard({ data, ...wrapperProps }: Props) {
</AuthorDisplayName>
</UserNameWrapper>
</div>
{!isMobile && <RecReason>{recReason}</RecReason>}
<div className="flex items-center gap-[10px]">
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
<RecReason>{recReason}</RecReason>
</div>
</div>

{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
</div>
</CardBody>
</CardWrapper>
Expand Down Expand Up @@ -139,9 +140,11 @@ function RightCard({ idx, data, ...wrapperProps }: Props) {
{authorDisplayName} {authorHandle && `@${authorHandle}`}
</AuthorDisplayName>
</UserNameWrapper>
{!isMobile && <RecReason>{recReason}</RecReason>}
<div className="flex items-center gap-[10px]">
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
<RecReason>{recReason}</RecReason>
</div>
</div>
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
</div>
</div>
</CardBody>
Expand Down Expand Up @@ -194,9 +197,11 @@ function BottomCard({ idx, data, ...wrapperProps }: Props) {
{authorDisplayName} {authorHandle && `@${authorHandle}`}
</AuthorDisplayName>
</UserNameWrapper>
{!isMobile && <RecReason>{recReason}</RecReason>}
<div className="flex items-center gap-[10px]">
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
<RecReason>{recReason}</RecReason>
</div>
</div>
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
</div>
</div>
</CardBody>
Expand All @@ -223,7 +228,7 @@ function CardBody({ className, ...props }: ComponentPropsWithRef<'div'>) {
<div
className={cn(
'w-full h-full [transition:all_0.3s] flex gap-[20px] relative',
isMobile ? 'flex-col gap-[10px]' : '',
'max-sm:flex-col max-sm:gap-[10px]',
className || ''
)}
{...props}
Expand All @@ -236,7 +241,7 @@ function Title({ className, ...props }: ComponentPropsWithRef<'span'>) {
<span
className={cn(
'font-bold text-[18px] leading-[19px] text-[#ffffff]',
isMobile ? 'text-[16px] leading-[19px] font-medium' : '',
'max-sm:text-[16px] max-sm:leading-[19px] max-sm:font-medium',
className || ''
)}
{...props}
Expand Down Expand Up @@ -311,8 +316,7 @@ function PlatformIcon({ className, ...props }: ComponentPropsWithRef<'img'>) {
return (
<img
className={cn(
'w-[30px] h-[30px] rounded-[50%] object-cover flex-shrink-0',
isMobile ? 'w-[14px] h-[14px]' : '',
'w-[14px] h-[14px] rounded-[50%] object-cover flex-shrink-0',
className || ''
)}
alt=""
Expand Down
33 changes: 18 additions & 15 deletions apps/u3/src/components/explore/posts/PostCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import styled, { StyledComponentPropsWithRef } from 'styled-components';

import { isMobile } from 'react-device-detect';
import { ComponentPropsWithRef } from 'react';
import CardBase from '../../common/card/CardBase';
import EllipsisText from '../../common/text/EllipsisText';
import { HeartIcon3 } from '../../common/icons/HeartIcon';
import { cn } from '@/lib/utils';

export type PostCardData = {
title: string;
Expand Down Expand Up @@ -43,10 +45,11 @@ export default function PostCard({ data, ...wrapperProps }: Props) {
{authorDisplayName} {authorHandle && `@${authorHandle}`}
</AuthorDisplayName>
</UserWrapper>
{!isMobile && <RecReason>{recReason}</RecReason>}
<div className="flex items-center gap-[10px]">
{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
<RecReason>{recReason}</RecReason>
</div>
</BottomLeft>

{platformIconUrl && <PlatformIcon src={platformIconUrl} />}
</BottomWrapper>
</CardBody>
</CardWrapper>
Expand Down Expand Up @@ -234,15 +237,15 @@ const RecReason = styled.div`
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
`;
const PlatformIcon = styled.img`
width: 30px;
height: 30px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
${isMobile &&
`
width: 14px;
height: 14px;
`}
`;
function PlatformIcon({ className, ...props }: ComponentPropsWithRef<'img'>) {
return (
<img
className={cn(
'w-[14px] h-[14px] rounded-[50%] object-cover flex-shrink-0',
className || ''
)}
alt=""
{...props}
/>
);
}
28 changes: 23 additions & 5 deletions apps/u3/src/components/layout/LoginButtonV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
LensAccount,
} from '../profile/info/PlatformAccounts';
import LoginIcon from './nav-icons/LoginIcon';
import useUnreadNotificationsCount from '@/hooks/social/useUnreadNotificationsCount';

const CONTACT_LINKS = [
{
Expand Down Expand Up @@ -66,7 +67,7 @@ export default function LoginButtonV2() {
const [openMenu, setOpenMenu] = useState(false);
const [openLogoutConfirm, setOpenLogoutConfirm] = useState(false);
const navigate = useNavigate();

const { unreadCount, clearUnread } = useUnreadNotificationsCount();
if (!isLogin) {
return (
<ButtonWrapper onClick={login}>
Expand Down Expand Up @@ -99,7 +100,12 @@ export default function LoginButtonV2() {
setOpenMenu((pre) => !pre);
}}
>
<UserAvatar style={{ width: '40px', height: '40px' }} />
<div className="relative">
<UserAvatar style={{ width: '40px', height: '40px' }} />
{unreadCount > 0 && (
<div className="absolute top-0 right-0 size-2 rounded-full bg-[#F81775] " />
)}
</div>
</ButtonWrapper>
</DropdownMenuTrigger>
<DropdownMenuContent
Expand All @@ -124,10 +130,22 @@ export default function LoginButtonV2() {

<DropdownMenuItemWarper
className="max-sm:hidden"
onClick={() => navigate('/notification/activity')}
onClick={() => {
clearUnread();
navigate('/notification/activity');
}}
>
<NotificationIcon />
Notifications
<div className="w-full flex justify-between items-center">
<div className="flex items-center gap-2">
<NotificationIcon />
Notifications
</div>
{unreadCount > 0 && (
<div className="flex items-center justify-center size-4 rounded-full bg-[#F81775] text-[12px] p-0 text-white">
{unreadCount}
</div>
)}
</div>
</DropdownMenuItemWarper>

<DropdownMenuItemWarper
Expand Down
Loading

0 comments on commit 50ed866

Please sign in to comment.