Skip to content

Commit d21770d

Browse files
committed
feat(app): use DevPlaza design
1 parent 06ee8a7 commit d21770d

File tree

7 files changed

+106
-128
lines changed

7 files changed

+106
-128
lines changed

src/app/u/[handle]/creator/page.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { fetchUser, fetchUserActivityList } from '#/domain/profile/repository';
17+
import { fetchUser } from '#/domain/profile/repository';
1818

1919
import ProjectOwner from './ProjectOwner';
2020

@@ -25,7 +25,5 @@ export default async function CreatorProfile({ params }) {
2525
return <div>This user is not a creator.</div>;
2626
}
2727

28-
const { data: activityData } = await fetchUserActivityList(data?.base.user_id);
29-
30-
return <ProjectOwner data={data} activities={activityData?.list || []} />;
28+
return <ProjectOwner data={data} />;
3129
}

src/domain/profile/repository.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ async function updateUser(data) {
4444
return httpClient.post('/user/info', data);
4545
}
4646

47-
async function fetchUserActivityList(uid) {
48-
return httpClient.get(`/user/info/${uid}/creator/activity`);
49-
}
50-
5147
function resolvePaginationParams(params) {
5248
return { ...params, take: 20 };
5349
}
@@ -88,7 +84,7 @@ async function updateBlockContent(data) {
8884
}
8985

9086
export {
91-
fetchUser, updateUser, fetchUserActivityList,
87+
fetchUser, updateUser,
9288
fetchFollowerList, fetchFollowedList, followUser, unfollowUser,
9389
updateBanner,
9490
fetchBlockContent, updateBlockContent,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 { useState } from 'react';
18+
import { useDebouncedCallback } from 'use-debounce';
19+
20+
import { isBlockDataValid } from '@/components/control/block-editor/helper';
21+
import useMounted from '@/hooks/useMounted';
22+
23+
import type { JSONContent } from '@/components/control/block-editor/typing';
24+
25+
import { useViewingSelf } from '../../../auth/hooks';
26+
import { fetchBlockContent, updateBlockContent } from '../../repository';
27+
import CustomContent from './CustomContent';
28+
29+
function DevPlaza({ params }: { params?: { userId: number } }) {
30+
const [blockContent, setBlockContent] = useState<JSONContent | null>(null);
31+
const viewingSelf = useViewingSelf(params?.userId);
32+
33+
useMounted(() => {
34+
fetchBlockContent(params?.userId).then(res => {
35+
if (res.success) {
36+
setBlockContent(res.data);
37+
}
38+
});
39+
});
40+
41+
const handleBlockChange = useDebouncedCallback(updateBlockContent, 3000);
42+
43+
const rerenderKey = [
44+
'CustomContent',
45+
`${viewingSelf ? 'editable' : 'readonly'}`,
46+
isBlockDataValid(blockContent),
47+
].join('-');
48+
49+
return (
50+
<CustomContent
51+
key={rerenderKey}
52+
data={blockContent}
53+
onChange={handleBlockChange}
54+
editable={viewingSelf}
55+
/>
56+
);
57+
}
58+
59+
export default DevPlaza;

src/domain/profile/views/team-profile/LatestActivityList.js

-44
This file was deleted.

src/domain/profile/views/team-profile/TeamProfile.js

+26-44
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,20 @@
1515
*/
1616

1717
import { useState } from 'react';
18-
import { useDebouncedCallback } from 'use-debounce';
1918

20-
import { isBlockDataValid } from '@/components/control/block-editor/helper';
2119
import useAppConfig from '@/hooks/useAppConfig';
22-
import useMounted from '@/hooks/useMounted';
2320

24-
import { useViewingSelf } from '../../../auth/hooks';
2521
import PublishedBountyListView from '../../../bounty/views/published-bounty-list';
2622
import PublishedChallengeListView from '../../../challenge/views/published-challenge-list';
2723
import PublishedCourseListView from '../../../course/views/published-course-list';
2824
import PublishedQuizListView from '../../../quiz/views/published-quiz-list';
29-
import { fetchBlockContent, updateBlockContent } from '../../repository';
3025
import ActivityTabListWidget from '../../widgets/activity-tab-list';
3126
import SocialInfoWidget from '../../widgets/social-info';
3227
import TabBarWidget from '../../widgets/tab-bar';
33-
import CustomContent from './CustomContent';
34-
import LatestActivityList from './LatestActivityList';
28+
import DevPlaza from './DevPlaza';
3529

36-
function resolveTabs(published) {
37-
return [
30+
function resolveTabs(published, extraTabs = []) {
31+
return [].concat(extraTabs, [
3832
{
3933
text: 'Open Course',
4034
node: (
@@ -75,56 +69,44 @@ function resolveTabs(published) {
7569
),
7670
view: PublishedQuizListView,
7771
},
78-
];
72+
]);
7973
};
8074

81-
function TeamProfileView({ data, activities }) {
82-
const [tabActive, setTabActive] = useState(1);
83-
const [blockContent, setBlockContent] = useState(null);
84-
const viewingSelf = useViewingSelf(data?.base.user_id);
75+
function TeamProfileView({ data }) {
76+
const [tabActive, setTabActive] = useState(0);
8577
const devPlazaEnabled = useAppConfig('devPlaza.enabled');
8678

87-
useMounted(() => {
88-
devPlazaEnabled &&
89-
fetchBlockContent(data?.base.user_id).then(res => {
90-
if (res.success) {
91-
setBlockContent(res.data);
92-
}
93-
});
94-
});
95-
96-
const handleBlockChange = useDebouncedCallback(updateBlockContent, 3000);
97-
9879
const tabContent = [
9980
<SocialInfoWidget key="social" data={data} />,
100-
<LatestActivityList key="activity" activities={activities} />,
10181
];
10282

103-
const rerenderKey = [
104-
'CustomContent',
105-
`${viewingSelf ? 'editable' : 'readonly'}`,
106-
isBlockDataValid(blockContent),
107-
].join('-');
83+
const extraTabs = [];
84+
85+
if (devPlazaEnabled) {
86+
extraTabs.push({
87+
text: 'DevPlaza',
88+
node: (
89+
<>
90+
<span className="inline md:hidden">DevPlaza</span>
91+
<span className="hidden md:inline">DevPlaza</span>
92+
</>
93+
),
94+
view: DevPlaza,
95+
filterable: false,
96+
});
97+
}
10898

10999
return (
110100
<div className="md:pl-[410px] md:pb-14 md:pr-14">
111-
{devPlazaEnabled && (
112-
<CustomContent
113-
key={rerenderKey}
114-
className="mb-6"
115-
data={blockContent}
116-
onChange={handleBlockChange}
117-
editable={viewingSelf}
118-
/>
119-
)}
120101
<TabBarWidget
121-
tabs={['Info', 'Activities']}
122-
tabClassName="h-14 md:h-9 md:w-[111px] md:first:hidden"
102+
className="md:hidden"
103+
tabs={['Info']}
104+
tabClassName="h-14 md:h-9 md:w-[111px]"
123105
current={tabActive}
124106
onChange={setTabActive}
125107
/>
126-
{tabContent[tabActive]}
127-
<ActivityTabListWidget userId={data?.base.user_id} tabs={resolveTabs(data?.num)} />
108+
<div className="mb-9 md:hidden">{tabContent[tabActive]}</div>
109+
<ActivityTabListWidget userId={data?.base.user_id} tabs={resolveTabs(data?.num, extraTabs)} />
128110
</div>
129111
);
130112
}

src/domain/profile/widgets/activity-tab-list/ActivityTabList.js

+17-14
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,33 @@ function ActivityTabList({ userId, tabs }) {
2929
const [activity, setActivity] = useState(0);
3030
const [sort, setSort] = useState();
3131

32-
const ActivityList = tabs[activity].view;
32+
const activeTab = tabs[activity];
33+
const ActivityList = activeTab.view;
3334
const params = { userId, sort: sort?.value };
3435

3536
return (
3637
<>
37-
<div className="flex flex-col justify-between pt-9 pb-6 md:flex-row">
38+
<div className="flex flex-col justify-between pb-6 md:flex-row">
3839
<TabBar
3940
tabs={tabs.map(({ text, node }) => node ? { text, node } : text)}
4041
tabClassName="h-14 md:h-9 md:!px-6"
4142
current={activity}
4243
onChange={setActivity}
4344
/>
44-
<div className="flex gap-3 mt-6 md:mt-0">
45-
<ReactSelect
46-
id="learn-order-select"
47-
isClearable
48-
value={sort}
49-
isSearchable={false}
50-
className="w-full no-bg showDropdownIndicator bg-transparent md:w-[200px]"
51-
onChange={setSort}
52-
options={sortOptions}
53-
placeholder={'Sort by'}
54-
/>
55-
</div>
45+
{activeTab.filterable !== false && (
46+
<div className="flex gap-3 mt-6 md:mt-0">
47+
<ReactSelect
48+
id="learn-order-select"
49+
isClearable
50+
value={sort}
51+
isSearchable={false}
52+
className="w-full no-bg showDropdownIndicator bg-transparent md:w-[200px]"
53+
onChange={setSort}
54+
options={sortOptions}
55+
placeholder={'Sort by'}
56+
/>
57+
</div>
58+
)}
5659
</div>
5760
{ActivityList && <ActivityList params={params} />}
5861
</>

src/shared/hooks/useIsMobile.js src/shared/components/control/block-editor/typing.ts

+1-17
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,4 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useEffect, useState } from 'react';
18-
19-
export function useIsMobile() {
20-
const [isMobile, setIsMobile] = useState(false);
21-
useEffect(() => {
22-
if (
23-
window.navigator.userAgent.match(
24-
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
25-
)
26-
) {
27-
setIsMobile(true);
28-
} else {
29-
setIsMobile(false);
30-
}
31-
}, []);
32-
return isMobile;
33-
}
17+
export type { JSONContent } from 'novel';

0 commit comments

Comments
 (0)