Skip to content

Commit

Permalink
Add modal to see debug entries list
Browse files Browse the repository at this point in the history
  • Loading branch information
xepozz committed Jun 4, 2023
1 parent 94b1f2a commit e620e8a
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 104 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import HttpIcon from '@mui/icons-material/Http';
import RefreshIcon from '@mui/icons-material/Refresh';
import SyncAltIcon from '@mui/icons-material/SyncAlt';
import TerminalIcon from '@mui/icons-material/Terminal';
import {
Button,
CircularProgress,
ListItemIcon,
ListItemText,
ToggleButton,
ToggleButtonGroup,
Tooltip,
} from '@mui/material';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import {useCurrentPageRequestIds, useDebugEntry} from '@yiisoft/yii-dev-panel-sdk/API/Debug/Context';
import {DebugEntry, useGetDebugQuery} from '@yiisoft/yii-dev-panel-sdk/API/Debug/Debug';
import {DebugChip} from '@yiisoft/yii-dev-panel-sdk/Component/DebugChip';
import {isDebugEntryAboutConsole, isDebugEntryAboutWeb} from '@yiisoft/yii-dev-panel-sdk/Helper/debugEntry';
import React, {MouseEvent, useEffect, useState} from 'react';

type DebugEntryItemProps = {
entry: DebugEntry;
selected: boolean;
rightText: string | null;
onClick: (entry: DebugEntry) => void;
};

const DebugEntryItem = React.memo(({entry, onClick, selected, rightText}: DebugEntryItemProps) => {
return (
<ListItemButton onClick={() => onClick(entry)} defaultChecked={selected}>
<ListItemIcon>
<DebugChip entry={entry} />
</ListItemIcon>
<ListItemText primary={entry.request?.path ?? entry.command?.input} />
{rightText && (
<Tooltip title="The request was made by the current page">
<SyncAltIcon />
</Tooltip>
)}
</ListItemButton>
);
});

const filterDebugEntry = (filters: string[], currentPageRequestIds: string[]) => (entry: DebugEntry) => {
let result = false;
if (filters.includes('web') && isDebugEntryAboutWeb(entry)) {
result = true;
}
if (filters.includes('console') && isDebugEntryAboutConsole(entry)) {
result = true;
}
if (filters.includes('current') && currentPageRequestIds.includes(entry.id)) {
result = true;
}
return result;
};

export interface DebugEntriesListModalProps {
open: boolean;
onClick: (entry: DebugEntry) => void;
onClose: () => void;
}

export const DebugEntriesListModal = ({onClick, onClose, open}: DebugEntriesListModalProps) => {
const getDebugQuery = useGetDebugQuery();
const currentEntry = useDebugEntry();
const [entries, setEntries] = useState<DebugEntry[]>([]);
const [filters, setFilters] = useState(() => ['web', 'console', 'current']);
const currentPageRequestIds = useCurrentPageRequestIds();

console.log('currentPageRequestIds', currentPageRequestIds);

const handleFormat = (event: MouseEvent<HTMLElement>, newFormats: string[]) => {
setFilters(newFormats);
};
useEffect(() => {
if (!getDebugQuery.isFetching && getDebugQuery.data && getDebugQuery.data.length > 0) {
setEntries(getDebugQuery.data);
}
}, [getDebugQuery.isFetching]);

return (
<Dialog fullWidth onClose={() => onClose()} open={open}>
<DialogTitle>Select a debug entry</DialogTitle>
<List sx={{pt: 0}}>
<ToggleButtonGroup fullWidth size="small" color="primary" value={filters} onChange={handleFormat}>
<ToggleButton value="web">
<HttpIcon />
</ToggleButton>
<ToggleButton value="console">
<TerminalIcon />
</ToggleButton>
<ToggleButton value="current">Current</ToggleButton>
<Button color="primary" onClick={() => getDebugQuery.refetch()} disabled={getDebugQuery.isFetching}>
{getDebugQuery.isFetching ? <CircularProgress size={24} color="info" /> : <RefreshIcon />}
</Button>
</ToggleButtonGroup>
{entries.filter(filterDebugEntry(filters, currentPageRequestIds)).map((entry) => (
<DebugEntryItem
key={entry.id}
entry={entry}
onClick={onClick}
selected={currentEntry && entry.id === currentEntry.id}
rightText={currentPageRequestIds.includes(entry.id) ? 'Current' : null}
/>
))}
</List>
</Dialog>
);
};
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import {
Badge,
ButtonGroup,
IconButton,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Paper,
Portal,
Tooltip,
} from '@mui/material';
import ListIcon from '@mui/icons-material/List';
import {ButtonGroup, Paper, Portal} from '@mui/material';
import Box from '@mui/material/Box';
import {TooltipProps, tooltipClasses} from '@mui/material/Tooltip';
import {styled} from '@mui/material/styles';
import SpeedDial from '@mui/material/SpeedDial';
import SpeedDialAction from '@mui/material/SpeedDialAction';
import {setToolbarOpen} from '@yiisoft/yii-dev-panel-sdk/API/Application/ApplicationContext';
import {changeEntryAction, useDebugEntry} from '@yiisoft/yii-dev-panel-sdk/API/Debug/Context';
import {addCurrentPageRequestId, changeEntryAction, useDebugEntry} from '@yiisoft/yii-dev-panel-sdk/API/Debug/Context';
import {DebugEntry, debugApi, useGetDebugQuery} from '@yiisoft/yii-dev-panel-sdk/API/Debug/Debug';
import {DebugChip} from '@yiisoft/yii-dev-panel-sdk/Component/DebugChip';
import {YiiIcon} from '@yiisoft/yii-dev-panel-sdk/Component/SvgIcon/YiiIcon';
import {isDebugEntryAboutConsole, isDebugEntryAboutWeb} from '@yiisoft/yii-dev-panel-sdk/Helper/debugEntry';
import * as serviceWorkerRegistration from '@yiisoft/yii-dev-panel-sdk/serviceWorkerRegistration';
import {DebugEntriesListModal} from '@yiisoft/yii-dev-toolbar/Module/Toolbar/Component/DebugEntriesListModal';
import {CommandItem} from '@yiisoft/yii-dev-toolbar/Module/Toolbar/Component/Toolbar/Console/CommandItem';
import {DateItem} from '@yiisoft/yii-dev-toolbar/Module/Toolbar/Component/Toolbar/DateItem';
import {EventsItem} from '@yiisoft/yii-dev-toolbar/Module/Toolbar/Component/Toolbar/EventsItem';
Expand All @@ -33,45 +23,6 @@ import {useSelector} from '@yiisoft/yii-dev-toolbar/store';
import {useCallback, useEffect, useState} from 'react';
import {useDispatch} from 'react-redux';

const HtmlTooltip = styled(({className, ...props}: TooltipProps) => (
<Tooltip {...props} classes={{popper: className}} />
))(({theme}) => ({
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: '#f5f5f9',
color: 'rgba(0, 0, 0, 0.87)',
maxWidth: '100%',
fontSize: theme.typography.pxToRem(12),
border: '1px solid #dadde9',
},
}));

type EntriesListProps = {
entries: DebugEntry[];
onClick: (entry: DebugEntry) => void;
};
const EntriesList = ({entries = [], onClick}: EntriesListProps) => {
const currentEntry = useDebugEntry();
if (entries.length === 0) {
return null;
}
return (
<>
{entries.map((entry) => (
<MenuItem
key={entry.id}
onClick={() => onClick(entry)}
selected={currentEntry && entry.id === currentEntry.id}
>
<ListItemIcon>
<DebugChip entry={entry} />
</ListItemIcon>
<ListItemText primary={entry.request?.path ?? entry.command?.input} />
</MenuItem>
))}
</>
);
};

let serviceWorker = navigator?.serviceWorker;

export const DebugToolbar = () => {
Expand All @@ -82,65 +33,68 @@ export const DebugToolbar = () => {
},
onUpdate: (registration) => {
console.log('onUpdate', registration);
if (registration && registration.waiting) {
registration.waiting.postMessage({type: 'SKIP_WAITING'});
}
// if (registration && registration.waiting) {
// registration.waiting.postMessage({type: 'SKIP_WAITING'});
// }
},
};

serviceWorkerRegistration.register(config);

console.log('[START] Listen to message');
console.debug('[START] Listen to message');
const onMessageHandler = (event) => {
console.log('[EVENT] Listen to message', event.data);
debugApi.util.invalidateTags(['debug/list']);
if (!event.data.payload || !('x-debug-id' in event.data.payload.headers)) {
return;
}
console.debug('[EVENT] Listen to message', event.data);
dispatch(debugApi.util.invalidateTags(['debug/list']));
dispatch(addCurrentPageRequestId(event.data.payload.headers['x-debug-id']));
};
serviceWorker?.addEventListener('message', onMessageHandler);
return () => {
console.log('[STOP] Listen to message');
console.debug('[STOP] Listen to message');
// serviceWorkerRegistration.unregister();
serviceWorker?.removeEventListener('message', onMessageHandler);
};
}, []);

const initialState = useSelector((state) => state.application.toolbarOpen);
const [checked, setChecked] = useState<boolean>(initialState);
const [isToolbarOpened, setIsToolbarOpened] = useState<boolean>(
useSelector((state) => state.application.toolbarOpen),
);
const getDebugQuery = useGetDebugQuery();
const debugEntry = useDebugEntry();
const dispatch = useDispatch();

const [selectedEntry, setSelectedEntry] = useState(debugEntry);
const [entries, setEntries] = useState<DebugEntry[]>([]);

useEffect(() => {
if (!getDebugQuery.isFetching && getDebugQuery.data && getDebugQuery.data.length > 0) {
setEntries(getDebugQuery.data.slice(0, 50));
setSelectedEntry(getDebugQuery.data[0]);
}
}, [getDebugQuery.isFetching]);

const onToolbarClickHandler = () => {
setChecked((v) => {
setIsToolbarOpened((v) => {
dispatch(setToolbarOpen(!v));
return !v;
});
};

const [anchorEl, setAnchorEl] = useState(null);

const onMouseEnterHandler = useCallback((event) => {
if (anchorEl !== event.currentTarget) {
setAnchorEl(event.currentTarget);
}
const onChangeHandler = useCallback((entry: DebugEntry) => {
setSelectedEntry(entry);
setIsToolbarOpened(true);
dispatch(setToolbarOpen(true));
dispatch(changeEntryAction(entry));
}, []);

const onMouseLeaveHandler = useCallback(() => {
setAnchorEl(null);
const [open, setOpen] = useState(false);

const handleClickOpen = useCallback(() => {
setOpen(true);
}, []);

const onChangeHandler = useCallback((entry: DebugEntry) => {
setSelectedEntry(entry);
dispatch(changeEntryAction(entry));
const handleClose = useCallback(() => {
setOpen(false);
}, []);

return (
Expand All @@ -150,10 +104,10 @@ export const DebugToolbar = () => {
component={Box}
elevation={10}
sx={{
position: !checked ? 'fixed' : 'sticky',
position: !isToolbarOpened ? 'fixed' : 'sticky',
bottom: 0,
right: 0,
width: !checked ? 'initial' : '100%',
width: !isToolbarOpened ? 'initial' : '100%',
transition: 'width 350ms ease-in-out',
py: 1,
px: 0.5,
Expand All @@ -167,7 +121,7 @@ export const DebugToolbar = () => {
>
<Box
sx={{
display: checked ? 'inline-block' : 'none',
display: isToolbarOpened ? 'inline-block' : 'none',
}}
>
<ButtonGroup disableElevation>
Expand Down Expand Up @@ -195,37 +149,44 @@ export const DebugToolbar = () => {
</Box>

<Box>
<IconButton
onMouseOver={onMouseEnterHandler}
onClick={onToolbarClickHandler}
sx={{marginX: 1, background: 'white'}}
>
<Badge badgeContent={entries.length} color="info">
<SpeedDial
ariaLabel=""
sx={{
bottom: 0,
right: 0,
marginX: 1,
'& .MuiSpeedDial-actions': {
position: 'absolute',
bottom: 32,
marginX: 1,
},
}}
FabProps={{
onClick: onToolbarClickHandler,
size: 'small',
sx: {
background: 'white',
},
}}
icon={
<YiiIcon
sx={{
transform: !checked ? 'rotate(360deg)' : 'rotate(0deg)',
transform: !isToolbarOpened ? 'rotate(360deg)' : 'rotate(0deg)',
transition: 'transform 400ms ease-in-out',
}}
/>
</Badge>
</IconButton>
}
>
<SpeedDialAction
onClick={handleClickOpen}
icon={<ListIcon />}
tooltipTitle="List all debug entries"
/>
</SpeedDial>
</Box>
</Paper>
)}
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={onMouseLeaveHandler}
MenuListProps={{onMouseLeave: onMouseLeaveHandler}}
PaperProps={{
style: {
maxHeight: 120 * 4.5,
width: '40%',
},
}}
>
<EntriesList entries={entries} onClick={onChangeHandler} />
</Menu>
<DebugEntriesListModal open={open} onClick={onChangeHandler} onClose={handleClose} />
</Portal>
);
};

0 comments on commit e620e8a

Please sign in to comment.