Skip to content

Commit

Permalink
Merge pull request #14 from thien-magenest/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
purrseus authored Dec 31, 2024
2 parents 7a18592 + 9d54dc8 commit d654cfb
Show file tree
Hide file tree
Showing 28 changed files with 202 additions and 217 deletions.
31 changes: 6 additions & 25 deletions src/contexts/MainContext.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,13 @@
import { createContext, type MutableRefObject } from 'react';
import { createContext } from 'react';
import type { Updater } from 'use-immer';
import type { useConsoleInterceptor, useNetworkInterceptor } from '../hooks';
import type {
DebuggerPanel,
DebuggerPosition,
DebuggerVisibility,
HttpRequest,
LogMessage,
SetState,
WebSocketRequest,
} from '../types';
import type { DebuggerState } from '../types';

export interface MainContextValue {
debuggerVisibility: DebuggerVisibility;
setDebuggerVisibility: SetState<DebuggerVisibility>;
debuggerPosition: DebuggerPosition;
setDebuggerPosition: SetState<DebuggerPosition>;
panelSelected: DebuggerPanel | null;
setPanelSelected: SetState<DebuggerPanel | null>;
interface MainContextValue {
debuggerState: DebuggerState;
setDebuggerState: Updater<DebuggerState>;
networkInterceptor: ReturnType<typeof useNetworkInterceptor>;
logInterceptor: ReturnType<typeof useConsoleInterceptor>;
detailsData: MutableRefObject<
| { [DebuggerPanel.Console]: LogMessage }
| { [DebuggerPanel.Network]: HttpRequest | WebSocketRequest }
| null
>;
screenWidth: number;
screenHeight: number;
verticalSafeMargin: number;
}

const MainContext = createContext<MainContextValue | null>(null);
Expand Down
File renamed without changes.
12 changes: 12 additions & 0 deletions src/core/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { DebuggerPanel, LogMessage, HttpRequest, WebSocketRequest } from '../types';

const detailsData: {
value:
| { [DebuggerPanel.Console]: LogMessage }
| { [DebuggerPanel.Network]: HttpRequest | WebSocketRequest }
| null;
} = {
value: null,
};

export { detailsData };
File renamed without changes.
4 changes: 3 additions & 1 deletion src/utils.ts → src/core/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { HttpRequest } from './types';
import type { HttpRequest } from '../types';

export const getVerticalSafeMargin = (screenHeight: number) => screenHeight / 8;

export const limitChar = (value: any, limit = 5000) => {
const stringValue = typeof value === 'string' ? value : JSON.stringify(value ?? '');
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useNetworkInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { useImmer } from 'use-immer';
import { NETWORK_REQUEST_HEADER } from '../constants';
import { NETWORK_REQUEST_HEADER } from '../core/constants';
import { FetchInterceptor, WebSocketInterceptor, XHRInterceptor } from '../interceptors';
import {
NetworkType,
Expand All @@ -10,7 +10,7 @@ import {
type WebSocketHandlers,
type WebSocketRequest,
} from '../types';
import { keyValueToString } from '../utils';
import { keyValueToString } from '../core/utils';

interface NetworkInterceptorParams {
autoEnabled: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/interceptors/ConsoleInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import type { ConsoleHandlers } from '../types';
import { frozen } from '../utils';
import { frozen } from '../core/utils';
import Interceptor from './Interceptor';

const originalConsoleError = console.error;
Expand Down
5 changes: 2 additions & 3 deletions src/interceptors/FetchInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NETWORK_REQUEST_HEADER } from '../constants';
import { NETWORK_REQUEST_HEADER } from '../core/constants';
import { NetworkType } from '../types';
import { formatRequestMethod, frozen, getHttpInterceptorId, keyValueToString } from '../utils';
import { formatRequestMethod, frozen, getHttpInterceptorId, keyValueToString } from '../core/utils';
import HttpInterceptor from './HttpInterceptor';

const originalFetch = global.fetch;
Expand Down Expand Up @@ -37,7 +37,6 @@ export default class FetchInterceptor extends HttpInterceptor {
const method = formatRequestMethod(init?.method);

let url: string;

switch (true) {
case input instanceof Request:
url = input.url;
Expand Down
2 changes: 1 addition & 1 deletion src/interceptors/Interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { frozen } from '../utils';
import { frozen } from '../core/utils';

export default abstract class Interceptor<T extends Object> {
#isInterceptorEnabled = false;
Expand Down
2 changes: 1 addition & 1 deletion src/interceptors/WebSocketInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NativeEventEmitter, type EmitterSubscription } from 'react-native';
import NativeWebSocketModule from 'react-native/Libraries/WebSocket/NativeWebSocketModule';
import type { WebSocketHandlers } from '../types';
import { NetworkInterceptor } from './NetworkInterceptor';
import { frozen } from '../utils';
import { frozen } from '../core/utils';

const originalWebSocketConnect = NativeWebSocketModule.connect;
const originalWebSocketSend = NativeWebSocketModule.send;
Expand Down
2 changes: 1 addition & 1 deletion src/interceptors/XHRInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NetworkType } from '../types';
import { frozen, getHttpInterceptorId } from '../utils';
import { frozen, getHttpInterceptorId } from '../core/utils';
import HttpInterceptor from './HttpInterceptor';

const originalXHROpen = XMLHttpRequest.prototype.open;
Expand Down
File renamed without changes.
File renamed without changes.
14 changes: 6 additions & 8 deletions src/types/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Dispatch, SetStateAction } from 'react';

export enum NetworkType {
XHR = 'xhr',
Fetch = 'fetch',
Expand All @@ -19,10 +17,10 @@ export interface NetworkRequest {
duration?: number;
}

export type DebuggerVisibility = 'hidden' | 'bubble' | 'panel';

export type DebuggerPosition = 'top' | 'bottom';

export type SetState<T> = Dispatch<SetStateAction<T>>;

export type NetworkTab = 'headers' | 'queryStringParameters' | 'body' | 'response' | 'messages';

export interface DebuggerState {
visibility: 'hidden' | 'bubble' | 'panel';
position: 'top' | 'bottom';
selectedPanel: DebuggerPanel | null;
}
140 changes: 61 additions & 79 deletions src/ui/Xenon.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { enableMapSet } from 'immer';
import {
createRef,
memo,
useImperativeHandle,
useRef,
useState,
type NamedExoticComponent,
} from 'react';
import { Animated, SafeAreaView, StyleSheet, useWindowDimensions, View } from 'react-native';
import colors from '../colors';
import MainContext, { type MainContextValue } from '../contexts/MainContext';
import { createRef, memo, useImperativeHandle, useRef, type NamedExoticComponent } from 'react';
import { Animated, SafeAreaView, StyleSheet, useWindowDimensions } from 'react-native';
import { useImmer } from 'use-immer';
import colors from '../theme/colors';
import MainContext from '../contexts/MainContext';
import { detailsData } from '../core/data';
import { useConsoleInterceptor, useNetworkInterceptor } from '../hooks';
import { DebuggerPanel, type DebuggerPosition, type DebuggerVisibility } from '../types';
import { DebuggerPanel, type DebuggerState } from '../types';
import { getVerticalSafeMargin } from '../core/utils';
import { Bubble, ConsolePanel, DebuggerHeader, DetailsViewer, NetworkPanel } from './components';

interface XenonComponentMethods {
Expand All @@ -36,13 +32,14 @@ const rootRef = createRef<XenonComponentMethods>();

const XenonComponent = memo<XenonComponentProps>(
({ autoInspectNetworkEnabled = true, autoInspectConsoleEnabled = true, bubbleSize = 40 }) => {
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
const verticalSafeMargin = screenHeight / 8;
const pan = useRef(new Animated.ValueXY({ x: 0, y: verticalSafeMargin }));
const detailsData: MainContextValue['detailsData'] = useRef(null);
const [debuggerVisibility, setDebuggerVisibility] = useState<DebuggerVisibility>('hidden');
const [debuggerPosition, setDebuggerPosition] = useState<DebuggerPosition>('bottom');
const [panelSelected, setPanelSelected] = useState<DebuggerPanel | null>(DebuggerPanel.Network);
const { width, height } = useWindowDimensions();
const pan = useRef(new Animated.ValueXY({ x: 0, y: getVerticalSafeMargin(height) }));

const [debuggerState, setDebuggerState] = useImmer<DebuggerState>({
visibility: 'hidden',
position: 'bottom',
selectedPanel: DebuggerPanel.Network,
});

const networkInterceptor = useNetworkInterceptor({
autoEnabled: autoInspectNetworkEnabled,
Expand All @@ -52,72 +49,62 @@ const XenonComponent = memo<XenonComponentProps>(
autoEnabled: autoInspectConsoleEnabled,
});

useImperativeHandle(
rootRef,
() => ({
useImperativeHandle(rootRef, () => {
const changeVisibility = (condition: boolean, value: DebuggerState['visibility']) => {
if (!condition) return;

setDebuggerState(draft => {
draft.visibility = value;
});
};

return {
isVisible() {
return debuggerVisibility !== 'hidden';
return debuggerState.visibility !== 'hidden';
},
show() {
if (!this.isVisible()) setDebuggerVisibility('bubble');
changeVisibility(!this.isVisible(), 'bubble');
},
hide() {
if (this.isVisible()) setDebuggerVisibility('hidden');
changeVisibility(this.isVisible(), 'hidden');
},
}),
[debuggerVisibility],
);
};
}, [debuggerState.visibility, setDebuggerState]);

let content;
switch (debuggerVisibility) {
case 'bubble':
content = (
<View style={styles.bubbleBackdrop}>
<Bubble bubbleSize={bubbleSize} pan={pan} />
</View>
);
break;
case 'panel':
content = (
<SafeAreaView
style={[
styles.container,
// eslint-disable-next-line react-native/no-inline-styles
{
[debuggerPosition]: 0,
height: Math.min(screenWidth, screenHeight) * 0.75,
},
]}
>
<DebuggerHeader />
{panelSelected === DebuggerPanel.Network && <NetworkPanel />}
{panelSelected === DebuggerPanel.Console && <ConsolePanel />}
{!panelSelected && !!detailsData.current && <DetailsViewer />}
</SafeAreaView>
);
break;
default:
content = null;
}
const renderContent = () => {
switch (debuggerState.visibility) {
case 'bubble':
return (
<Bubble bubbleSize={bubbleSize} pan={pan} screenWidth={width} screenHeight={height} />
);
case 'panel':
return (
<SafeAreaView
style={[
styles.container,
// eslint-disable-next-line react-native/no-inline-styles
{
[debuggerState.position]: 0,
height: Math.min(width, height) * 0.75,
},
]}
>
<DebuggerHeader />
{debuggerState.selectedPanel === DebuggerPanel.Network && <NetworkPanel />}
{debuggerState.selectedPanel === DebuggerPanel.Console && <ConsolePanel />}
{!debuggerState.selectedPanel && !!detailsData.value && <DetailsViewer />}
</SafeAreaView>
);
default:
return null;
}
};

return (
<MainContext.Provider
value={{
debuggerVisibility,
setDebuggerVisibility,
debuggerPosition,
setDebuggerPosition,
panelSelected,
setPanelSelected,
networkInterceptor,
logInterceptor,
detailsData,
screenWidth,
screenHeight,
verticalSafeMargin,
}}
value={{ debuggerState, setDebuggerState, networkInterceptor, logInterceptor }}
>
{content}
{renderContent()}
</MainContext.Provider>
);
},
Expand All @@ -132,11 +119,6 @@ const styles = StyleSheet.create({
zIndex: 9999,
backgroundColor: colors.lightGray,
},
bubbleBackdrop: {
flex: 1,
...StyleSheet.absoluteFillObject,
pointerEvents: 'box-none',
},
});

XenonComponent.displayName = 'Xenon';
Expand Down
Loading

0 comments on commit d654cfb

Please sign in to comment.