Skip to content

Commit 748c091

Browse files
committed
feat(panel): improved layout and performance
1 parent 5d27b13 commit 748c091

File tree

7 files changed

+134
-84
lines changed

7 files changed

+134
-84
lines changed

src/core/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export const NETWORK_REQUEST_HEADER = 'X-React-Native-Xenon';
2+
export const NETWORK_ITEM_HEIGHT = 36;
3+
export const CONSOLE_ITEM_HEIGHT = 36;

src/core/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ export const convertToCurl = (
152152

153153
return curlCommand;
154154
};
155+
156+
export const formatCount = (count: number) => {
157+
if (count < 1000) return count.toString();
158+
if (count < 1000000) return `${(count / 1000).toFixed(1)}K`;
159+
return `${(count / 1000000).toFixed(1)}M`;
160+
};
155161
//#endregion
156162

157163
//#region decorators

src/ui/components/headers/DebuggerHeader.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import icons from '../../../theme/icons';
66
import Divider from '../common/Divider';
77
import DebuggerHeaderItem from '../items/DebuggerHeaderItem';
88
import HeaderComponents from './HeaderComponents';
9+
import { formatCount } from '../../../core/utils';
910

1011
interface DebuggerHeaderProps {
1112
selectedPanel: PanelState;
@@ -54,7 +55,7 @@ const DebuggerHeader = forwardRef<ScrollView, DebuggerHeaderProps>(
5455
<DebuggerHeaderItem
5556
isLabel
5657
isActive={selectedPanel === PanelState.Network}
57-
content="Network Panel"
58+
content={`Network (${formatCount(networkInterceptor.networkRequests.size)})`}
5859
onPress={() => switchTo(PanelState.Network)}
5960
/>
6061

@@ -74,7 +75,7 @@ const DebuggerHeader = forwardRef<ScrollView, DebuggerHeaderProps>(
7475
<DebuggerHeaderItem
7576
isLabel
7677
isActive={selectedPanel === PanelState.Console}
77-
content="Console Panel"
78+
content={`Console (${formatCount(consoleInterceptor.logMessages.length)})`}
7879
onPress={() => switchTo(PanelState.Console)}
7980
/>
8081

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { StyleSheet, Text } from 'react-native';
1+
import { memo } from 'react';
2+
import { StyleSheet, Text, View } from 'react-native';
3+
import { CONSOLE_ITEM_HEIGHT } from '../../../core/constants';
24
import { formatLogMessage, getConsoleTypeColor } from '../../../core/utils';
35
import colors from '../../../theme/colors';
46
import type { LogMessage } from '../../../types';
@@ -8,27 +10,39 @@ interface ConsolePanelItemProps extends LogMessage {
810
onPress: () => void;
911
}
1012

11-
export default function ConsolePanelItem({ type, values, onPress }: ConsolePanelItemProps) {
12-
return (
13-
<Touchable
14-
onPress={onPress}
15-
style={[styles.container, { backgroundColor: getConsoleTypeColor(type) }]}
16-
>
17-
<Text numberOfLines={1} style={styles.text}>
18-
{formatLogMessage(values)}
19-
</Text>
20-
</Touchable>
21-
);
22-
}
13+
const ConsolePanelItem = memo<ConsolePanelItemProps>(
14+
({ type, values, onPress }) => {
15+
return (
16+
<Touchable onPress={onPress} style={styles.container}>
17+
<View style={[styles.wrapper, { backgroundColor: getConsoleTypeColor(type) }]}>
18+
<Text numberOfLines={1} style={styles.text}>
19+
{formatLogMessage(values)}
20+
</Text>
21+
</View>
22+
</Touchable>
23+
);
24+
},
25+
(prevProps, nextProps) => {
26+
return prevProps.type === nextProps.type && prevProps.values === nextProps.values;
27+
},
28+
);
2329

2430
const styles = StyleSheet.create({
2531
container: {
2632
flex: 1,
27-
padding: 8,
33+
paddingBottom: 4,
34+
height: CONSOLE_ITEM_HEIGHT,
35+
},
36+
wrapper: {
37+
flex: 1,
38+
paddingHorizontal: 8,
39+
justifyContent: 'center',
2840
borderRadius: 8,
2941
},
3042
text: {
3143
color: colors.black,
3244
fontSize: 14,
3345
},
3446
});
47+
48+
export default ConsolePanelItem;
Lines changed: 70 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { useMemo } from 'react';
1+
import { memo, useMemo } from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33
import { URL } from 'react-native-url-polyfill';
4+
import { NETWORK_ITEM_HEIGHT } from '../../../core/constants';
45
import {
56
formatRequestDuration,
67
formatRequestMethod,
78
formatRequestStatusCode,
89
} from '../../../core/utils';
910
import colors from '../../../theme/colors';
1011
import type { HttpRequest, NetworkRequest } from '../../../types';
12+
import Divider from '../common/Divider';
1113
import Touchable from '../common/Touchable';
1214

1315
interface NetworkPanelItemProps {
@@ -39,76 +41,85 @@ const getMethodColor = (method: string) => {
3941
}
4042
};
4143

42-
export default function NetworkPanelItem({
43-
method,
44-
name,
45-
startTime,
46-
endTime,
47-
status,
48-
onPress,
49-
}: NetworkPanelItemProps) {
50-
const duration = formatRequestDuration(startTime, endTime);
51-
const requestMethod = formatRequestMethod(method);
52-
const requestStatusCode = formatRequestStatusCode(status);
53-
const isRequestFailed = Number.isInteger(status) && status! >= 400 && status! < 600;
54-
const textStyle = [styles.text, isRequestFailed && styles.failedText];
44+
const NetworkPanelItem = memo<NetworkPanelItemProps>(
45+
({ method, name, startTime, endTime, status, onPress }) => {
46+
const duration = formatRequestDuration(startTime, endTime);
47+
const requestMethod = formatRequestMethod(method);
48+
const requestStatusCode = formatRequestStatusCode(status);
49+
const isRequestFailed = Number.isInteger(status) && status! >= 400 && status! < 600;
50+
const textStyle = [styles.text, isRequestFailed && styles.failedText];
5551

56-
const requestPath = useMemo(() => {
57-
if (!name) return '[failed]';
52+
const requestPath = useMemo(() => {
53+
if (!name) return '[failed]';
5854

59-
try {
60-
const url = new URL(name);
61-
const suffixUrl = url.pathname + url.search;
55+
try {
56+
const url = new URL(name);
57+
const suffixUrl = url.pathname + url.search;
6258

63-
if (suffixUrl === '/') return url.host;
64-
return suffixUrl;
65-
} catch (error) {
66-
return name;
67-
}
68-
}, [name]);
59+
if (suffixUrl === '/') return url.host;
60+
return suffixUrl;
61+
} catch (error) {
62+
return name;
63+
}
64+
}, [name]);
6965

70-
return (
71-
<Touchable onPress={onPress} style={styles.container}>
72-
<View style={styles.column}>
73-
<Text
74-
numberOfLines={1}
75-
style={[
76-
styles.text,
77-
styles.methodText,
78-
{ backgroundColor: getMethodColor(requestMethod) },
79-
]}
80-
>
81-
{requestMethod}
82-
</Text>
83-
</View>
66+
return (
67+
<View style={styles.container}>
68+
<Touchable onPress={onPress} style={styles.wrapper}>
69+
<View style={styles.column}>
70+
<Text
71+
numberOfLines={1}
72+
style={[
73+
styles.text,
74+
styles.methodText,
75+
{ backgroundColor: getMethodColor(requestMethod) },
76+
]}
77+
>
78+
{requestMethod}
79+
</Text>
80+
</View>
8481

85-
<View style={[styles.column, styles.pathColumn]}>
86-
<Text numberOfLines={1} style={textStyle}>
87-
{requestPath}
88-
</Text>
89-
</View>
82+
<View style={[styles.column, styles.pathColumn]}>
83+
<Text numberOfLines={1} style={textStyle}>
84+
{requestPath}
85+
</Text>
86+
</View>
9087

91-
<View style={[styles.column, styles.durationColumn]}>
92-
<Text numberOfLines={1} style={textStyle}>
93-
{duration}
94-
</Text>
95-
</View>
88+
<View style={[styles.column, styles.durationColumn]}>
89+
<Text numberOfLines={1} style={textStyle}>
90+
{duration}
91+
</Text>
92+
</View>
9693

97-
<View style={styles.column}>
98-
<Text numberOfLines={1} style={textStyle}>
99-
{requestStatusCode}
100-
</Text>
94+
<View style={styles.column}>
95+
<Text numberOfLines={1} style={textStyle}>
96+
{requestStatusCode}
97+
</Text>
98+
</View>
99+
</Touchable>
100+
<Divider type="horizontal" />
101101
</View>
102-
</Touchable>
103-
);
104-
}
102+
);
103+
},
104+
(prevProps, nextProps) => {
105+
return (
106+
prevProps.method === nextProps.method &&
107+
prevProps.name === nextProps.name &&
108+
prevProps.startTime === nextProps.startTime &&
109+
prevProps.endTime === nextProps.endTime &&
110+
prevProps.status === nextProps.status
111+
);
112+
},
113+
);
105114

106115
const styles = StyleSheet.create({
107116
container: {
108117
flex: 1,
118+
},
119+
wrapper: {
109120
flexDirection: 'row',
110121
alignItems: 'center',
111-
paddingVertical: 8,
122+
height: NETWORK_ITEM_HEIGHT,
112123
columnGap: 8,
113124
},
114125
pathColumn: {
@@ -138,3 +149,5 @@ const styles = StyleSheet.create({
138149
fontWeight: '600',
139150
},
140151
});
152+
153+
export default NetworkPanelItem;

src/ui/components/panels/ConsolePanel.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@ import { forwardRef, useCallback, useContext, useMemo } from 'react';
22
import {
33
FlatList,
44
StyleSheet,
5-
View,
65
type ListRenderItem,
76
type StyleProp,
87
type ViewStyle,
98
} from 'react-native';
109
import { MainContext } from '../../../contexts';
10+
import { CONSOLE_ITEM_HEIGHT } from '../../../core/constants';
1111
import { formatLogMessage } from '../../../core/utils';
1212
import { DebuggerPanel, type LogMessage } from '../../../types';
1313
import Empty from '../common/Empty';
1414
import ConsolePanelItem from '../items/ConsolePanelItem';
1515

16-
const Separator = () => <View style={styles.spacing} />;
17-
1816
const ConsolePanel = forwardRef<FlatList, { style?: StyleProp<ViewStyle> }>(({ style }, ref) => {
1917
const {
2018
debuggerState: { searchQuery },
@@ -55,17 +53,26 @@ const ConsolePanel = forwardRef<FlatList, { style?: StyleProp<ViewStyle> }>(({ s
5553
[setDebuggerState],
5654
);
5755

56+
const getItemLayout = useCallback(
57+
(_: ArrayLike<LogMessage> | null | undefined, index: number) => ({
58+
length: CONSOLE_ITEM_HEIGHT,
59+
offset: CONSOLE_ITEM_HEIGHT * index,
60+
index,
61+
}),
62+
[],
63+
);
64+
5865
return (
5966
<FlatList
6067
ref={ref}
6168
inverted={!!data.length}
6269
data={data}
6370
renderItem={renderItem}
6471
keyExtractor={(_, index) => index.toString()}
65-
ItemSeparatorComponent={Separator}
6672
style={[styles.container, style]}
6773
contentContainerStyle={data.length ? styles.contentContainer : undefined}
6874
ListEmptyComponent={<Empty>No logs yet</Empty>}
75+
getItemLayout={getItemLayout}
6976
/>
7077
);
7178
});
@@ -77,9 +84,6 @@ const styles = StyleSheet.create({
7784
contentContainer: {
7885
padding: 8,
7986
},
80-
spacing: {
81-
height: 4,
82-
},
8387
});
8488

8589
export default ConsolePanel;

src/ui/components/panels/NetworkPanel.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,17 @@ import {
77
type ViewStyle,
88
} from 'react-native';
99
import { MainContext } from '../../../contexts';
10+
import { NETWORK_ITEM_HEIGHT } from '../../../core/constants';
1011
import {
1112
DebuggerPanel,
1213
NetworkType,
1314
type HttpRequest,
1415
type ID,
1516
type WebSocketRequest,
1617
} from '../../../types';
17-
import Divider from '../common/Divider';
1818
import Empty from '../common/Empty';
1919
import NetworkPanelItem from '../items/NetworkPanelItem';
2020

21-
const Separator = () => <Divider type="horizontal" />;
22-
2321
const NetworkPanel = forwardRef<FlatList, { style?: StyleProp<ViewStyle> }>(({ style }, ref) => {
2422
const {
2523
debuggerState: { searchQuery },
@@ -64,16 +62,28 @@ const NetworkPanel = forwardRef<FlatList, { style?: StyleProp<ViewStyle> }>(({ s
6462
[setDebuggerState],
6563
);
6664

65+
const getItemLayout = useCallback(
66+
(
67+
_: ArrayLike<[NonNullable<ID>, HttpRequest | WebSocketRequest]> | null | undefined,
68+
index: number,
69+
) => ({
70+
length: NETWORK_ITEM_HEIGHT,
71+
offset: NETWORK_ITEM_HEIGHT * index,
72+
index,
73+
}),
74+
[],
75+
);
76+
6777
return (
6878
<FlatList
6979
inverted={!!data.length}
7080
data={data}
7181
ref={ref}
7282
renderItem={renderItem}
7383
keyExtractor={([key]) => key}
74-
ItemSeparatorComponent={Separator}
7584
style={[styles.container, style]}
7685
ListEmptyComponent={<Empty>No records yet</Empty>}
86+
getItemLayout={getItemLayout}
7787
/>
7888
);
7989
});

0 commit comments

Comments
 (0)