Skip to content

File tree

3 files changed

+182
-174
lines changed

3 files changed

+182
-174
lines changed

src/hooks/useAsyncRequest.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useEffect, useState } from 'react';
2+
3+
interface Request<Response> {
4+
(): Promise<Response>;
5+
cancel?(): void;
6+
}
7+
8+
interface Options {
9+
resetResponseOnRefresh?: boolean;
10+
}
11+
12+
interface State<T, E = unknown> {
13+
loading: boolean;
14+
response?: T;
15+
error?: E;
16+
}
17+
18+
interface Return<T> extends State<T> {
19+
refresh(): Promise<void>;
20+
}
21+
22+
export function useAsyncRequest<T>(request: Request<T>, options?: Options): Return<T> {
23+
const [state, setState] = useState<State<T>>({ loading: true, response: undefined, error: undefined });
24+
25+
const updateWithRequest = async () => {
26+
try {
27+
setState(({ ...draft }) => {
28+
if (options.resetResponseOnRefresh) draft.response = undefined;
29+
draft.error = undefined;
30+
return draft;
31+
});
32+
33+
const response = await request();
34+
setState((prev) => ({ ...prev, response, loading: false }));
35+
} catch (error) {
36+
setState((prev) => ({ ...prev, error, loading: false }));
37+
}
38+
};
39+
40+
useEffect(() => {
41+
updateWithRequest();
42+
return () => {
43+
if (request.cancel && typeof request.cancel === 'function') {
44+
request.cancel();
45+
}
46+
};
47+
}, []);
48+
49+
return { ...state, refresh: updateWithRequest };
50+
}

src/modules/ChannelSettings/components/ChannelSettingsUI/index.tsx

Lines changed: 78 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -16,69 +16,32 @@ import LeaveChannelModal from '../LeaveChannel';
1616
import UserPanel from '../UserPanel';
1717

1818
export interface ChannelSettingsUIProps {
19-
renderPlaceholderError?: () => React.ReactElement;
2019
renderChannelProfile?: () => React.ReactElement;
2120
renderModerationPanel?: () => React.ReactElement;
2221
renderLeaveChannel?: () => React.ReactElement;
22+
renderPlaceholderError?: () => React.ReactElement;
23+
renderPlaceholderLoading?: () => React.ReactElement;
2324
}
2425

25-
const ChannelSettingsUI: React.FC<ChannelSettingsUIProps> = (props: ChannelSettingsUIProps) => {
26+
const ChannelSettingsUI: React.FC<ChannelSettingsUIProps> = ({
27+
renderLeaveChannel,
28+
renderChannelProfile,
29+
renderModerationPanel,
30+
renderPlaceholderError,
31+
renderPlaceholderLoading,
32+
}: ChannelSettingsUIProps) => {
2633
const { stringSet } = useContext(LocalizationContext);
2734

2835
const state = useSendbirdStateContext();
29-
const channelSettingStore = useChannelSettingsContext();
36+
const { channel, invalidChannel, onCloseClick, loading } = useChannelSettingsContext();
3037

3138
const [showLeaveChannelModal, setShowLeaveChannelModal] = useState(false);
3239

3340
const isOnline = state?.config?.isOnline;
3441
const logger = state?.config?.logger;
3542

36-
const {
37-
channel,
38-
invalidChannel,
39-
onCloseClick,
40-
} = channelSettingStore;
41-
42-
const {
43-
renderPlaceholderError,
44-
renderChannelProfile,
45-
renderModerationPanel,
46-
renderLeaveChannel,
47-
} = props;
48-
49-
if (!channel || invalidChannel) {
43+
const renderHeaderArea = () => {
5044
return (
51-
<div>
52-
<div className="sendbird-channel-settings__header">
53-
<Label type={LabelTypography.H_2} color={LabelColors.ONBACKGROUND_1}>
54-
{stringSet.CHANNEL_SETTING__HEADER__TITLE}
55-
</Label>
56-
<Icon
57-
className="sendbird-channel-settings__close-icon"
58-
type={IconTypes.CLOSE}
59-
height="24px"
60-
width="24px"
61-
onClick={() => {
62-
logger.info('ChannelSettings: Click close');
63-
onCloseClick?.();
64-
}}
65-
/>
66-
</div>
67-
<div>
68-
{
69-
renderPlaceholderError
70-
? renderPlaceholderError()
71-
: (
72-
<PlaceHolder type={PlaceHolderTypes.WRONG} />
73-
)
74-
}
75-
</div>
76-
</div>
77-
);
78-
}
79-
80-
return (
81-
<>
8245
<div className="sendbird-channel-settings__header">
8346
<Label type={LabelTypography.H_2} color={LabelColors.ONBACKGROUND_1}>
8447
{stringSet.CHANNEL_SETTING__HEADER__TITLE}
@@ -92,79 +55,78 @@ const ChannelSettingsUI: React.FC<ChannelSettingsUIProps> = (props: ChannelSetti
9255
onCloseClick?.();
9356
}}
9457
>
95-
<Icon
96-
className="sendbird-channel-settings__close-icon"
97-
type={IconTypes.CLOSE}
98-
height="22px"
99-
width="22px"
100-
/>
58+
<Icon className="sendbird-channel-settings__close-icon" type={IconTypes.CLOSE} height="22px" width="22px" />
10159
</IconButton>
10260
</div>
10361
</div>
62+
);
63+
};
64+
65+
if (loading) {
66+
if (renderPlaceholderLoading) return renderPlaceholderLoading();
67+
return <PlaceHolder type={PlaceHolderTypes.LOADING} />;
68+
}
69+
70+
if (invalidChannel || !channel) {
71+
return (
72+
<div>
73+
{renderHeaderArea()}
74+
<div>{renderPlaceholderError ? renderPlaceholderError() : <PlaceHolder type={PlaceHolderTypes.WRONG} />}</div>
75+
</div>
76+
);
77+
}
78+
79+
return (
80+
<>
81+
{renderHeaderArea()}
10482
<div className="sendbird-channel-settings__scroll-area">
105-
{
106-
renderChannelProfile?.() || (
107-
<ChannelProfile />
108-
)
109-
}
110-
{
111-
renderModerationPanel?.() || (
112-
channel?.myRole === 'operator'
113-
? (<ModerationPanel />)
114-
: (<UserPanel />)
115-
)
116-
}
117-
{
118-
renderLeaveChannel?.() || (
119-
<div
120-
className={[
121-
'sendbird-channel-settings__panel-item',
122-
'sendbird-channel-settings__leave-channel',
123-
!isOnline ? 'sendbird-channel-settings__panel-item__disabled' : '',
124-
].join(' ')}
125-
role="button"
126-
onKeyDown={() => {
127-
if (!isOnline) { return; }
128-
setShowLeaveChannelModal(true);
129-
}}
130-
onClick={() => {
131-
if (!isOnline) { return; }
132-
setShowLeaveChannelModal(true);
133-
}}
134-
tabIndex={0}
135-
>
136-
<Icon
137-
className={[
138-
'sendbird-channel-settings__panel-icon-left',
139-
'sendbird-channel-settings__panel-icon__leave',
140-
].join(' ')}
141-
type={IconTypes.LEAVE}
142-
fillColor={IconColors.ERROR}
143-
height="24px"
144-
width="24px"
145-
/>
146-
<Label
147-
type={LabelTypography.SUBTITLE_1}
148-
color={LabelColors.ONBACKGROUND_1}
149-
>
150-
{stringSet.CHANNEL_SETTING__LEAVE_CHANNEL__TITLE}
151-
</Label>
152-
</div>
153-
)
154-
}
155-
{
156-
showLeaveChannelModal && (
157-
<LeaveChannelModal
158-
onCancel={() => {
159-
setShowLeaveChannelModal(false);
160-
}}
161-
onSubmit={() => {
162-
setShowLeaveChannelModal(false);
163-
onCloseClick?.();
164-
}}
83+
{renderChannelProfile?.() || <ChannelProfile />}
84+
{renderModerationPanel?.() || (channel?.myRole === 'operator' ? <ModerationPanel /> : <UserPanel />)}
85+
{renderLeaveChannel?.() || (
86+
<div
87+
className={[
88+
'sendbird-channel-settings__panel-item',
89+
'sendbird-channel-settings__leave-channel',
90+
!isOnline ? 'sendbird-channel-settings__panel-item__disabled' : '',
91+
].join(' ')}
92+
role="button"
93+
onKeyDown={() => {
94+
if (!isOnline) {
95+
return;
96+
}
97+
setShowLeaveChannelModal(true);
98+
}}
99+
onClick={() => {
100+
if (!isOnline) {
101+
return;
102+
}
103+
setShowLeaveChannelModal(true);
104+
}}
105+
tabIndex={0}
106+
>
107+
<Icon
108+
className={['sendbird-channel-settings__panel-icon-left', 'sendbird-channel-settings__panel-icon__leave'].join(' ')}
109+
type={IconTypes.LEAVE}
110+
fillColor={IconColors.ERROR}
111+
height="24px"
112+
width="24px"
165113
/>
166-
)
167-
}
114+
<Label type={LabelTypography.SUBTITLE_1} color={LabelColors.ONBACKGROUND_1}>
115+
{stringSet.CHANNEL_SETTING__LEAVE_CHANNEL__TITLE}
116+
</Label>
117+
</div>
118+
)}
119+
{showLeaveChannelModal && (
120+
<LeaveChannelModal
121+
onCancel={() => {
122+
setShowLeaveChannelModal(false);
123+
}}
124+
onSubmit={() => {
125+
setShowLeaveChannelModal(false);
126+
onCloseClick?.();
127+
}}
128+
/>
129+
)}
168130
</div>
169131
</>
170132
);

0 commit comments

Comments
 (0)