Skip to content

Commit b88d5e8

Browse files
authored
feat: integrate Web3.bio Profile data
Merge commits below into one: * feat: add web3bio mark of profile avatar * feat: profile desc support web3bio * feat: profile social info support web3.bio * feat: update web3bio profile * feat: profile avatar support web3.bio
1 parent 8132ee6 commit b88d5e8

File tree

10 files changed

+253
-60
lines changed

10 files changed

+253
-60
lines changed

public/images/svg/basenames-blue.svg

+10
Loading
+3
Loading

public/images/svg/lens-green.svg

+10
Loading

public/images/svg/web3bio-2.svg

+11
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Copyright 2024 OpenBuild
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Image from 'next/image';
18+
import Web3BioIcon from 'public/images/svg/web3bio.svg';
19+
20+
import Avatar from '@/components/Avatar';
21+
22+
function ProfileAvatar({
23+
data,
24+
className,
25+
}: {
26+
data: {
27+
base: { user_avatar?: string; user_nick_name: string };
28+
web3Bio?: Array<{
29+
avatar: string;
30+
}>;
31+
};
32+
className: string;
33+
}) {
34+
const size = 110;
35+
const avatarClassName = 'rounded-full object-fill';
36+
37+
const baseAvatar = data?.base?.user_avatar;
38+
const web3BioAvatar = data?.web3Bio?.filter(v => v.avatar).find(v => v.avatar)?.avatar ?? '';
39+
const avatarAlt = data?.base?.user_nick_name;
40+
const showBaseAvatar = (baseAvatar && !baseAvatar.includes('/config/avatar')) || !web3BioAvatar;
41+
42+
return (
43+
<div className={className}>
44+
{showBaseAvatar ? (
45+
<Avatar
46+
size={size}
47+
src={baseAvatar}
48+
className={avatarClassName}
49+
defaultSrc="https://s3.us-west-1.amazonaws.com/file.openbuild.xyz/config/avatar/04.svg"
50+
alt={avatarAlt}
51+
/>
52+
) : (
53+
<>
54+
<Image
55+
src={web3BioAvatar}
56+
alt={avatarAlt}
57+
className={avatarClassName}
58+
width={size}
59+
height={size}
60+
unoptimized
61+
/>
62+
<Image
63+
src={Web3BioIcon}
64+
alt="web3bio"
65+
className="size-6 rounded-full absolute right-1 bottom-1 border-1 border-white"
66+
/>
67+
</>
68+
)}
69+
</div>
70+
);
71+
}
72+
73+
export default ProfileAvatar;

src/domain/profile/widgets/profile-card/ProfileCard.js

+28-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { useState } from 'react';
2222
import { toast } from 'react-toastify';
2323
import useSWR from 'swr';
2424

25-
import Avatar from '@/components/Avatar';
2625
import { Button } from '@/components/Button';
2726
import { RepositioningIcon } from '@/components/Icons';
2827
import { SvgIcon } from '@/components/Image';
@@ -32,6 +31,13 @@ import { useUser } from '#/state/application/hooks';
3231

3332
import SocialInfoWidget from '../social-info';
3433
import IdentitySwitch from './IdentitySwitch';
34+
import ProfileAvatar from './ProfileAvatar';
35+
36+
function getUserDesc(data) {
37+
const baseUserDesc = data?.base.user_bio;
38+
const web3BioDesc = data?.web3Bio?.find(v => v.description)?.description;
39+
return baseUserDesc || web3BioDesc || '--';
40+
}
3541

3642
function ProfileCardWidget({ className, data }) {
3743
const router = useRouter();
@@ -78,27 +84,27 @@ function ProfileCardWidget({ className, data }) {
7884
return (
7985
<div className={clsx('relative md:w-[360px] md:rounded-lg md:p-6 md:bg-white', className)}>
8086
<div className="flex flex-col gap-2 items-center">
81-
<Avatar
82-
className="-mt-[104px] md:mt-0"
83-
size={110}
84-
src={data.base.user_avatar}
85-
defaultSrc="https://s3.us-west-1.amazonaws.com/file.openbuild.xyz/config/avatar/04.svg"
86-
alt={data?.base.user_nick_name}
87-
/>
87+
<ProfileAvatar data={data} className="-mt-[104px] md:mt-0 relative" />
8888
<h6 className="text-[24px] leading-none">
8989
<a href={`/u/${handle}`}>{data?.base.user_nick_name}</a>
9090
</h6>
91-
{!creatorAvailable && <div className="flex items-center text-sm">
92-
<RepositioningIcon className="mr-1" />
93-
<p className="text-sm opacity-60">
94-
{data.base?.user_city}, {data.base?.user_country}
95-
</p>
96-
</div>}
97-
<p className="text-sm text-center">{data?.base.user_bio !== '' ? data?.base.user_bio : '--'}</p>
91+
{!creatorAvailable && (
92+
<div className="flex items-center text-sm">
93+
<RepositioningIcon className="mr-1" />
94+
<p className="text-sm opacity-60">
95+
{data.base?.user_city}, {data.base?.user_country}
96+
</p>
97+
</div>
98+
)}
99+
<p className="text-sm text-center">{getUserDesc(data)}</p>
98100
</div>
99101
<div className="my-6 flex gap-7 justify-center text-sm">
100-
<Link href={`/u/${handle}/followers`}><strong>{data?.follow?.followers}</strong> <span className="opacity-60">followers</span></Link>
101-
<Link href={`/u/${handle}/following`}><strong>{data?.follow?.following}</strong> <span className="opacity-60">following</span></Link>
102+
<Link href={`/u/${handle}/followers`}>
103+
<strong>{data?.follow?.followers}</strong> <span className="opacity-60">followers</span>
104+
</Link>
105+
<Link href={`/u/${handle}/following`}>
106+
<strong>{data?.follow?.following}</strong> <span className="opacity-60">following</span>
107+
</Link>
102108
</div>
103109
{user?.base.user_id === data?.base?.user_id ? (
104110
<Link href="/profile">
@@ -107,11 +113,13 @@ function ProfileCardWidget({ className, data }) {
107113
<span className="!font-bold">Edit</span>
108114
</Button>
109115
</Link>
110-
) : ((status === 'authenticated' && followData?.follow) || followLoading ?
116+
) : (status === 'authenticated' && followData?.follow) || followLoading ? (
111117
<Button className="group" loading={followLoading} fullWidth variant="outlined" onClick={unfollow}>
112118
<span className="!font-bold block group-hover:hidden">Following</span>
113119
<span className="!font-bold hidden group-hover:block">Unfollow</span>
114-
</Button> : <Button fullWidth variant="contained" loading={followLoading} onClick={follow}>
120+
</Button>
121+
) : (
122+
<Button fullWidth variant="contained" loading={followLoading} onClick={follow}>
115123
<SvgIcon name="plus" size={16} />
116124
<span className="!font-bold">Follow</span>
117125
</Button>
@@ -127,9 +135,7 @@ function ProfileCardWidget({ className, data }) {
127135
<Link href="/" className="text-xs opacity-60">+ Follow</Link>
128136
</div>
129137
</>} */}
130-
{creatorAvailable && (
131-
<IdentitySwitch className="absolute top-4 right-4" userName={handle} />
132-
)}
138+
{creatorAvailable && <IdentitySwitch className="absolute top-4 right-4" userName={handle} />}
133139
<SocialInfoWidget className="hidden md:block" data={data} />
134140
</div>
135141
);

src/domain/profile/widgets/social-info/SocialInfo.js

+29-18
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,61 @@ import { SvgIcon } from '@/components/Image';
2121
import SocialLink from './SocialLink';
2222
import Web3BioProfile from './Web3BioProfile';
2323

24-
function socialsInfo(type, link) {
24+
const web3BioSocialKeyMap = {
25+
user_github: 'github',
26+
user_discord: 'discord',
27+
user_x: 'twitter',
28+
};
29+
30+
function socialsInfo(type, link, web3BioLink) {
31+
const showWeb3Bio = !link && web3BioLink;
32+
2533
switch (type) {
2634
case 'user_github':
2735
return {
2836
name: 'GitHub',
2937
icon: 'github-black',
30-
link: link && `https://github.com/${link}`,
38+
link: link ? `https://github.com/${link}` : web3BioLink,
3139
enableKey: 'user_show_github',
40+
showWeb3Bio,
3241
};
3342
case 'user_x':
3443
return {
3544
name: 'X',
3645
icon: 'x-black',
37-
link: link && `https://x.com/${link}`,
46+
link: link ? `https://x.com/${link}` : web3BioLink,
3847
enableKey: 'user_show_x',
48+
showWeb3Bio,
3949
};
4050
case 'user_discord':
4151
return {
4252
name: 'Discord',
4353
icon: 'discord-black',
44-
link: link && `https://discord.com/invite/${link}`,
54+
link: link ? `https://discord.com/invite/${link}` : web3BioLink,
4555
enableKey: 'user_show_discord',
56+
showWeb3Bio,
4657
};
4758
default:
4859
return null;
4960
}
5061
}
5162

5263
function SocialInfoWidget({ className, data }) {
53-
const socials = useMemo(
54-
() =>
55-
Object.keys(data.social)
56-
.map(i => socialsInfo(i, data.social[i]))
57-
.filter(s => {
58-
if (!s) {
59-
return false;
60-
}
64+
const socials = useMemo(() => {
65+
const web3BioSocials = (data?.web3Bio ?? []).reduce((p, c) => (c.links ? { ...p, ...c.links } : p), {});
6166

62-
const enabled = s.enableKey ? data.base[s.enableKey] : true;
67+
return Object.keys(data.social)
68+
.map(i => socialsInfo(i, data.social[i], web3BioSocials[web3BioSocialKeyMap[i]]?.link))
69+
.filter(s => {
70+
if (!s) {
71+
return false;
72+
}
6373

64-
return enabled && !!s.link;
65-
}),
66-
[data],
67-
);
74+
const enabled = s.enableKey ? data.base[s.enableKey] : true;
75+
76+
return enabled && !!s.link;
77+
});
78+
}, [data]);
6879

6980
return (
7081
<div className={className}>
@@ -73,7 +84,7 @@ function SocialInfoWidget({ className, data }) {
7384
<p className="mt-6 uppercase text-xs opacity-60 font-bold">Social Profiles</p>
7485
<div className="border border-gray-600 rounded overflow-hidden mt-2">
7586
{socials.map(i => (
76-
<SocialLink key={`user-social-${i.name}`} url={i.link} icon={i.icon} extra={i.extra}>
87+
<SocialLink key={`user-social-${i.name}`} url={i.link} icon={i.icon} showWeb3Bio={i.showWeb3Bio}>
7788
{i.name}
7889
</SocialLink>
7990
))}

src/domain/profile/widgets/social-info/SocialLink.js

+17-5
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,31 @@
1414
* limitations under the License.
1515
*/
1616

17+
import Image from 'next/image';
18+
import Web3BioIcon from 'public/images/svg/web3bio-2.svg';
19+
1720
import { SvgIcon } from '@/components/Image';
1821

19-
function SocialLink({ url, icon, extra, children }) {
22+
function SocialLink({ url, icon, showWeb3Bio, children }) {
2023
return (
21-
<a className="h-9 flex justify-between items-center px-4 border-b border-gray-600 last:border-0 hover:bg-gray-1000" href={url} target="_blank" rel="noreferrer">
22-
<div className="flex justify-between items-center" style={{ flexGrow: 1, paddingRight: '8px' }}>
24+
<a
25+
className="h-9 flex justify-between gap-2 items-center px-4 border-b border-gray-600 last:border-0 hover:bg-gray-1000"
26+
href={url}
27+
target="_blank"
28+
rel="noreferrer"
29+
>
30+
<div className="flex justify-between items-center flex-1">
2331
<div className="flex items-center">
2432
{icon && <SvgIcon name={icon} size={16} />}
2533
<p className="pl-2 pr-1 text-sm font-semibold">{children}</p>
2634
</div>
27-
{extra}
35+
{showWeb3Bio && (
36+
<div className="border border-gray-600 rounded-md p-0.5 pt-1 shrink-0">
37+
<Image src={Web3BioIcon} alt="web3bio" />
38+
</div>
39+
)}
2840
</div>
29-
<SvgIcon style={{ flexShrink: 0 }} name="share" size={14} />
41+
<SvgIcon className="shrink-0" name="share" size={14} />
3042
</a>
3143
);
3244
}

0 commit comments

Comments
 (0)