Skip to content

Commit

Permalink
Few more UI fixes and tweaks around sorting tables
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Aug 14, 2024
1 parent 5902a6c commit c79fb57
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 46 deletions.
8 changes: 8 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.23] - 2024-08-14

### Changed

- Added better error handling for null values when sorting tables
- Added default value when opening tabs from right clicking
- Added an auto-scroll to the bottom of a tasking/split tasking panel when opening it initially

## [0.2.22] - 2024-08-13

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ const VirtualTreeRow = React.memo(({
</div>
);
}, areEqual);
const caseInsensitiveCompare = (a, b) => {
try{
return a.localeCompare(b);
}catch(error){
console.log("localeCompare failed for", a, b);
return a < b;
}
}
const FileBrowserVirtualTreePreMemo = ({
treeRootData,
treeAdjMatrix,
Expand Down Expand Up @@ -312,7 +320,7 @@ const FileBrowserVirtualTreePreMemo = ({
group,
root: true
},
...(Object.keys(treeAdjMatrix[group][host]?.[node] || {})).reduce( (prev, cur) => {
...(Object.keys(treeAdjMatrix[group][host]?.[node] || {})).sort(caseInsensitiveCompare).reduce( (prev, cur) => {
if(!treeRootData[group][host][cur].can_have_children){return [...prev]}
return [...prev, flattenNode(cur, group, host, depth+1)];
}, []).flat()
Expand All @@ -334,7 +342,7 @@ const FileBrowserVirtualTreePreMemo = ({
group,
root: false,
},
...(Object.keys(treeAdjMatrix[group][host]?.[node] || {})).reduce( (prev, cur) => {
...(Object.keys(treeAdjMatrix[group][host]?.[node] || {})).sort(caseInsensitiveCompare).reduce( (prev, cur) => {
if(!treeRootData[group][host][cur].can_have_children){return [...prev]}
if(!showDeletedFiles && treeRootData[group][host][cur].deleted){return [...prev]}
return [...prev, flattenNode(cur, group, host, depth+1)];
Expand Down Expand Up @@ -366,7 +374,7 @@ const FileBrowserVirtualTreePreMemo = ({
// need to return an array
let finalData = [];
//console.log(treeAdjMatrix);
const groupKeys = Object.keys(treeAdjMatrix).sort();
const groupKeys = Object.keys(treeAdjMatrix).sort(caseInsensitiveCompare);
for(let i = 0; i < groupKeys.length; i++){
finalData.push({
id: groupKeys[i],
Expand All @@ -383,7 +391,7 @@ const FileBrowserVirtualTreePreMemo = ({
children: treeAdjMatrix[groupKeys[i]],
full_path_text: groupKeys[i],
});
const hostKeys = Object.keys(treeAdjMatrix[groupKeys[i]]).sort();
const hostKeys = Object.keys(treeAdjMatrix[groupKeys[i]]).sort(caseInsensitiveCompare);
for(let j = 0; j < hostKeys.length; j++){
//for(const [host, matrix] of Object.entries(hosts)){
finalData.push({
Expand All @@ -401,7 +409,7 @@ const FileBrowserVirtualTreePreMemo = ({
full_path_text: hostKeys[j],
});
//console.log(matrix);
finalData.push(...Object.keys(treeAdjMatrix[groupKeys[i]][hostKeys[j]][""]).reduce((prev, c) => {
finalData.push(...Object.keys(treeAdjMatrix[groupKeys[i]][hostKeys[j]][""]).sort(caseInsensitiveCompare).reduce((prev, c) => {
if(!showDeletedFiles && c.deleted) {
return [...prev];
} else {
Expand Down
2 changes: 2 additions & 0 deletions MythicReactUI/src/components/pages/Callbacks/Callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const StyledSpeedDial = styled(SpeedDial)(({theme}) => ({
}
}));
export const getCallbackIdFromClickedTab = (tabId) => {
if(tabId === null || tabId === undefined){return 0}
if(tabId === ""){return 0}
if(tabId.includes("fileBrowser")) {
return Number(tabId.split("fileBrowser")[0]);
}else if(tabId.includes("interact")){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function CallbacksTablePreMemo(props){
const callbacks = useContext(CallbacksContext);
const onOpenTab = useContext(OnOpenTabContext);
const onOpenTabs = useContext(OnOpenTabsContext);
const interactType = useMythicSetting({setting_name: "interactType", output: "string"})
const interactType = useMythicSetting({setting_name: "interactType", default_value: "interact", output: "string"})
const theme = useTheme();
const [openMultipleTabsDialog, setOpenMultipleTabsDialog] = React.useState({open: false, tabType: "interact"});
const [openMetaDialog, setOpenMetaDialog] = React.useState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,58 @@ export const CallbacksTabsFileBrowserTable = (props) => {
if (sortData.sortType === 'number' || sortData.sortType === 'size' || sortData.sortType === 'date') {
tempData.sort((a, b) => {
if(sortData.inMetadata){
return parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a]?.metadata[sortData.sortKey]) >
parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b]?.metadata[sortData.sortKey]) ? 1 : -1
try {
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a]?.metadata[sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a]?.metadata[sortData.sortKey] === undefined) {
return -1;
}
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b]?.metadata[sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b]?.metadata[sortData.sortKey] === undefined) {
return 1;
}
return parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a]?.metadata[sortData.sortKey]) >
parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b]?.metadata[sortData.sortKey]) ? 1 : -1;
}catch(error) {
console.log("failed to parse data for sorting", error);
return props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a]?.metadata[sortData.sortKey] >
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b]?.metadata[sortData.sortKey] ? 1 : -1
}
} else {
return parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey]) > parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey]) ? 1 : -1
try {
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] === undefined) {
return -1;
}
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] === undefined) {
return 1;
}
return parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey]) > parseInt(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey]) ? 1 : -1;
} catch (error) {
console.log("failed to parse data for sorting", error);
return props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] > props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] ? 1 : -1;
}
}

})
} else if (sortData.sortType === 'string') {
tempData.sort((a, b) => (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey].toLowerCase() > props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey].toLowerCase() ? 1 : -1));
tempData.sort((a, b) => {
try{
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] === undefined) {
return -1;
}
if (props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] === null ||
props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] === undefined) {
return 1;
}
return props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey].localeCompare(props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey]);
}catch(error){
console.log("failed to parse data for sorting", error);
return props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][a][sortData.sortKey] > props.treeRootData[props.selectedFolderData.group][props.selectedFolderData.host][b][sortData.sortKey] ? 1 : -1
}

});
}
if (sortData.sortDirection === 'DESC') {
tempData.reverse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ query getBatchTasking($callback_id: Int!, $offset: Int!, $fetchLimit: Int!){
`;
export const CallbacksTabsTaskingPanel = ({tabInfo, index, value, onCloseTab, parentMountedRef, me}) =>{
const [taskLimit, setTaskLimit] = React.useState(10);
const [scrollToBottom, setScrollToBottom] = React.useState(false);
const [openParametersDialog, setOpenParametersDialog] = React.useState(false);
const [commandInfo, setCommandInfo] = React.useState({});
const [taskingData, setTaskingData] = React.useState({task: []});
Expand Down Expand Up @@ -247,6 +248,7 @@ export const CallbacksTabsTaskingPanel = ({tabInfo, index, value, onCloseTab, pa
setFetchedAllTasks(false);
}
}
if(!scrollToBottom){setScrollToBottom(true)}
},
fetchPolicy: "no-cache"
});
Expand All @@ -258,6 +260,11 @@ export const CallbacksTabsTaskingPanel = ({tabInfo, index, value, onCloseTab, pa
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect( () => {
if(scrollToBottom){
messagesEndRef.current.scrollIntoView();
}
}, [scrollToBottom]);
const loadMoreTasks = () => {
getInfiniteScrollTasking({variables: {callback_id: tabInfo.callbackID, offset: taskingData.task.length, fetchLimit}});
}
Expand Down Expand Up @@ -364,9 +371,6 @@ export const CallbacksTabsTaskingPanel = ({tabInfo, index, value, onCloseTab, pa
setSelectedToken(token);
}
}
if(index !== value){
return null
}
return (
<MythicTabPanel index={index} value={value} >
{!fetched && <LinearProgress color="primary" thickness={2} style={{paddingTop: "5px"}}/>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ query getBatchTasking($callback_id: Int!, $offset: Int!, $fetchLimit: Int!){
`;
export const CallbacksTabsTaskingConsolePanel = ({tabInfo, index, value, onCloseTab, parentMountedRef, me}) =>{
const [taskLimit, setTaskLimit] = React.useState(10);
const [scrollToBottom, setScrollToBottom] = React.useState(false);
const [openParametersDialog, setOpenParametersDialog] = React.useState(false);
const [commandInfo, setCommandInfo] = React.useState({});
const [taskingData, setTaskingData] = React.useState({task: []});
Expand Down Expand Up @@ -248,6 +249,7 @@ export const CallbacksTabsTaskingConsolePanel = ({tabInfo, index, value, onClose
setFetchedAllTasks(false);
}
}
if(!scrollToBottom){setScrollToBottom(true)}
},
fetchPolicy: "no-cache"
});
Expand All @@ -258,7 +260,12 @@ export const CallbacksTabsTaskingConsolePanel = ({tabInfo, index, value, onClose
mountedRef.current = false;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}, []);
useEffect( () => {
if(scrollToBottom){
messagesEndRef.current.scrollIntoView();
}
}, [scrollToBottom]);
const loadMoreTasks = () => {
getInfiniteScrollTasking({variables: {callback_id: tabInfo.callbackID, offset: taskingData.task.length, fetchLimit}});
}
Expand Down Expand Up @@ -357,12 +364,12 @@ export const CallbacksTabsTaskingConsolePanel = ({tabInfo, index, value, onClose
{loadingMore && <LinearProgress color="primary" thickness={2} style={{paddingTop: "5px"}}/>}
<div style={{overflowY: "auto", flexGrow: 1, width: "100%"}} id={`taskingPanelConsole${tabInfo.callbackID}`}>
{!fetchedAllTasks &&
<MythicStyledTooltip title="Fetch Older Tasks">
<MythicStyledTooltip title="Fetch Older Tasks" style={{marginLeft: "50%"}}>
<IconButton
onClick={loadMoreTasks}
variant="contained"
color="success"
style={{marginLeft: "50%"}}

size="large"><AutorenewIcon /></IconButton>
</MythicStyledTooltip>}
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ query getBatchTasking($callback_id: Int!, $offset: Int!, $fetchLimit: Int!){
`;
export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTab, me}) =>{
const [taskLimit, setTaskLimit] = React.useState(10);
const [scrollToBottom, setScrollToBottom] = React.useState(false);
const [openParametersDialog, setOpenParametersDialog] = React.useState(false);
const [commandInfo, setCommandInfo] = React.useState({});
const [taskingData, setTaskingData] = React.useState({task: []});
Expand Down Expand Up @@ -252,6 +253,7 @@ export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTa
setFetchedAllTasks(false);
}
}
if(!scrollToBottom){setScrollToBottom(true)}
},
fetchPolicy: "no-cache"
});
Expand All @@ -261,7 +263,12 @@ export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTa
mountedRef.current = false;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}, []);
useEffect( () => {
if(scrollToBottom){
messagesEndRef.current.scrollIntoView();
}
}, [scrollToBottom]);
const loadMoreTasks = () => {
getInfiniteScrollTasking({variables: {callback_id: tabInfo.callbackID, offset: taskingData.task.length, fetchLimit}});
}
Expand Down Expand Up @@ -397,27 +404,30 @@ export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTa

{!fetchedAllTasks &&
<MythicStyledTooltip title="Fetch Older Tasks">
<IconButton
onClick={loadMoreTasks}
variant="contained"
color="success"
style={{marginLeft: "50%"}}
size="large"><AutorenewIcon /></IconButton>
<IconButton
onClick={loadMoreTasks}
variant="contained"
color="success"
style={{marginLeft: "50%"}}
size="large"><AutorenewIcon/></IconButton>
</MythicStyledTooltip>}
{!fetched && <LinearProgress color="primary" thickness={2} style={{paddingTop: "5px"}}/>}
{loadingMore && <LinearProgress color="info" thickness={2} style={{paddingTop: "5px"}}/>}
{
taskingData.task.map( (task) => (
<TaskDisplayFlat key={"taskinteractdisplaysplit" + task.id} me={me} task={task}
command_id={task.command == null ? 0 : task.command.id}
filterOptions={filterOptions}
onSelectTask={(tsk) => {changeSelectedTask(tsk)}}
showOnSelectTask={true} selectedTask={selectedTask}
/>
))
taskingData.task.map((task) => (
<TaskDisplayFlat key={"taskinteractdisplaysplit" + task.id} me={me} task={task}
command_id={task.command == null ? 0 : task.command.id}
filterOptions={filterOptions}
onSelectTask={(tsk) => {
changeSelectedTask(tsk)
}}
showOnSelectTask={true} selectedTask={selectedTask}
/>
))
}
<div ref={messagesEndRef}/>
</div>
<div ref={messagesEndRef} />

</div>
<div className="bg-gray-light" style={{display: "inline-flex", height: "100%"}}>
<CallbacksTabsTaskingSplitTable selectedTask={selectedTask} me={me} filterOptions={filterOptions}
Expand Down
Loading

0 comments on commit c79fb57

Please sign in to comment.