Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(log-table): useful context menus #169

Merged
merged 4 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"date-fns": "^2.16.1",
"debounce": "^1.2.0",
"debug": "4.3.1",
"electron-context-menu": "2.3.0",
"electron-devtools-installer": "3.2.0",
"electron-squirrel-startup": "1.0.0",
"electron-window-state": "5.0.3",
Expand Down
9 changes: 9 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,12 @@ export interface RootState {
releaseChannelOverride: string;
};
}

/**
* Context menu actions that can be taken on a specific log line.
*/
export enum LogLineContextMenuActions {
SHOW_IN_CONTEXT,
COPY_TO_CLIPBOARD,
OPEN_SOURCE,
}
1 change: 1 addition & 0 deletions src/ipc-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export const enum IpcEvents {
TOGGLE_FILTER = 'TOGGLE_FILTER',
COPY = 'COPY',
RESET = 'RESET',
OPEN_LOG_CONTEXT_MENU = 'OPEN_LOG_CONTEXT_MENU',
}
71 changes: 53 additions & 18 deletions src/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
dialog,
systemPreferences,
IpcMainEvent,
Menu,
MenuItemConstructorOptions,
} from 'electron';
import * as path from 'path';

import { settingsFileManager } from './settings';
import { changeIcon } from './app-icon';
import { ICON_NAMES } from '../shared-constants';
import { IpcEvents } from '../ipc-events';
import { LogLineContextMenuActions, LogType } from '../interfaces';

export class IpcManager {
constructor() {
Expand All @@ -27,6 +30,7 @@ export class IpcManager {
this.setupQuit();
this.setupOpenRecent();
this.setupTitleBarClickMac();
this.setupContextMenus();
}

public openFile(pathName: string) {
Expand Down Expand Up @@ -123,24 +127,12 @@ export class IpcManager {
}

private setupGetPath() {
type name =
| 'home'
| 'appData'
| 'userData'
| 'temp'
| 'exe'
| 'module'
| 'desktop'
| 'documents'
| 'downloads'
| 'music'
| 'pictures'
| 'videos'
| 'logs';

ipcMain.handle(IpcEvents.GET_PATH, (_event, pathName: name) => {
return app.getPath(pathName);
});
ipcMain.handle(
IpcEvents.GET_PATH,
(_event, pathName: Parameters<typeof app.getPath>[0]) => {
return app.getPath(pathName);
},
);
}

private setupGetUserAgent() {
Expand Down Expand Up @@ -202,6 +194,49 @@ export class IpcManager {
app.addRecentDocument(filename);
});
}

private setupContextMenus() {
ipcMain.handle(IpcEvents.OPEN_LOG_CONTEXT_MENU, (event, type: LogType) => {
return new Promise((resolve) => {
const maybeShowInContext: MenuItemConstructorOptions[] =
type === LogType.BROWSER || type === LogType.WEBAPP
? [
{
type: 'separator',
},
{
label: 'Show in "All Desktop Logs"',
click: () => {
resolve(LogLineContextMenuActions.SHOW_IN_CONTEXT);
},
},
]
: [];

const menu = Menu.buildFromTemplate([
{
label: 'Copy Line',
click: () => {
resolve(LogLineContextMenuActions.COPY_TO_CLIPBOARD);
},
},
{
label: 'Show Line in Source',
click: () => {
resolve(LogLineContextMenuActions.OPEN_SOURCE);
},
},
...maybeShowInContext,
]);
menu.popup({
window: BrowserWindow.fromWebContents(event.sender) || undefined,
callback: () => {
resolve(undefined);
},
});
});
});
}
}

export const ipcManager = new IpcManager();
3 changes: 0 additions & 3 deletions src/main/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ export class AppMenu {
* Actually creates the menu.
*/
public setupMenu() {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('electron-context-menu')();

this.menu = getMenuTemplate({
openItems: this.getOpenItems(),
pruneItems: this.getPruneItems(),
Expand Down
23 changes: 4 additions & 19 deletions src/renderer/components/log-line-details/details.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { observer } from 'mobx-react';
import { exec } from 'child_process';
import { SleuthState } from '../../state/sleuth';
import React from 'react';
import classNames from 'classnames';
import { Card, Button, ButtonGroup, Tag, Elevation } from '@blueprintjs/core';
import debug from 'debug';

import { LogEntry } from '../../../interfaces';
import { LogLineData } from './data';
import { Timestamp } from './timestamp';
import { shell } from 'electron';
import { getIsBookmark, toggleBookmark } from '../../state/bookmarks';
import { capitalize } from '../../../utils/capitalize';

const d = debug('sleuth:details');
import { openLineInSource } from '../../../utils/open-line-in-source';

export interface LogLineDetailsProps {
state: SleuthState;
Expand Down Expand Up @@ -52,20 +48,9 @@ export class LogLineDetails extends React.Component<
if (selectedEntry && selectedEntry.sourceFile) {
const { sourceFile, line } = selectedEntry;

if (defaultEditor) {
const cmd = defaultEditor
.replace('{filepath}', `"${sourceFile}"`)
.replace('{line}', line.toString(10));

d(`Executing ${cmd}`);
exec(cmd, (error: Error) => {
if (!error) return;
d(`Tried to open source file, but failed`, error);
shell.showItemInFolder(sourceFile);
});
} else {
shell.showItemInFolder(sourceFile);
}
openLineInSource(line, sourceFile, {
defaultEditor,
});
}
}

Expand Down
41 changes: 41 additions & 0 deletions src/renderer/components/log-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
DateRange,
LogType,
ProcessableLogType,
LogLineContextMenuActions,
LogFile,
} from '../../interfaces';
import { didFilterChange } from '../../utils/did-filter-change';
import { isReduxAction } from '../../utils/is-redux-action';
Expand All @@ -38,6 +40,10 @@ import { RepeatedLevels } from '../../shared-constants';
import { reaction } from 'mobx';
import { Tag } from 'antd';
import { observer } from 'mobx-react';
import { showLogLineContextMenu } from '../ipc';
import { clipboard } from 'electron';
import { openLineInSource } from '../../utils/open-line-in-source';
import { getCopyText } from '../state/copy';

const d = debug('sleuth:logtable');

Expand Down Expand Up @@ -268,6 +274,40 @@ export class LogTable extends React.Component<LogTableProps, LogTableState> {
});
}

/**
* Show a context menu for the individual log lines in the table
*/
private async onRowRightClick(params: RowMouseEventHandlerParams) {
const rowData: LogEntry = params.rowData;
// type assertion because this component should only appear when you have a LogFile showing
const logType = (this.props.state.selectedLogFile as LogFile).logType;
const response = await showLogLineContextMenu(logType);

switch (response) {
case LogLineContextMenuActions.COPY_TO_CLIPBOARD: {
const copyText = getCopyText(rowData);
clipboard.writeText(copyText);
break;
}
case LogLineContextMenuActions.OPEN_SOURCE: {
const { line, sourceFile } = rowData;
openLineInSource(line, sourceFile, {
defaultEditor: this.props.state.defaultEditor,
});
break;
}
case LogLineContextMenuActions.SHOW_IN_CONTEXT:
{
this.props.state.selectLogFile(null, LogType.ALL);
const matchingIndex = this.state.sortedList.findIndex(
(row) => row.momentValue === rowData.momentValue,
);
this.changeSelection(matchingIndex);
}
break;
}
}

/**
* Handles the change of sorting direction. This method is passed to the LogTableHeaderCell
* components, who call it once the user changes sorting.
Expand Down Expand Up @@ -639,6 +679,7 @@ export class LogTable extends React.Component<LogTableProps, LogTableState> {
rowGetter: this.rowGetter,
rowCount: sortedList.length,
onRowClick: this.onRowClick,
onRowRightClick: this.onRowRightClick,
rowClassName: this.rowClassNameGetter,
headerHeight: 30,
sort: this.onSortChange,
Expand Down
28 changes: 11 additions & 17 deletions src/renderer/ipc.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, app } from 'electron';
import { ICON_NAMES } from '../shared-constants';
import { IpcEvents } from '../ipc-events';
import { LogLineContextMenuActions, LogType } from '../interfaces';

// This file handles sending IPC events. Other classes might
// listen to IPC events.

type name =
| 'home'
| 'appData'
| 'userData'
| 'cache'
| 'temp'
| 'exe'
| 'module'
| 'desktop'
| 'documents'
| 'downloads'
| 'music'
| 'pictures'
| 'videos'
| 'logs';
export function getPath(path: name): Promise<string> {
export function getPath(
path: Parameters<typeof app.getPath>[0],
): Promise<string> {
return ipcRenderer.invoke(IpcEvents.GET_PATH, path);
}

Expand Down Expand Up @@ -55,3 +43,9 @@ export function showMessageBox(
export function changeIcon(iconName: ICON_NAMES) {
return ipcRenderer.invoke(IpcEvents.CHANGE_ICON, iconName);
}

export function showLogLineContextMenu(
type: LogType,
): Promise<LogLineContextMenuActions> {
return ipcRenderer.invoke(IpcEvents.OPEN_LOG_CONTEXT_MENU, type);
}
2 changes: 1 addition & 1 deletion src/renderer/state/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function copy(state: SleuthState): boolean {
return false;
}

function getCopyText(entry: LogEntry) {
export function getCopyText(entry: LogEntry) {
const { message, meta } = entry;
let { timestamp } = entry;

Expand Down
30 changes: 30 additions & 0 deletions src/utils/open-line-in-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { shell } from 'electron';
import { exec } from 'child_process';
import debug from 'debug';

const d = debug('sleuth:open-line-in-source');

interface OpenSourceOptions {
defaultEditor: string;
}

export function openLineInSource(
line: number,
sourceFile: string,
options: OpenSourceOptions,
) {
if (options.defaultEditor) {
const cmd = options.defaultEditor
.replace('{filepath}', `"${sourceFile}"`)
.replace('{line}', line.toString(10));

d(`Executing ${cmd}`);
exec(cmd, (error: Error) => {
if (!error) return;
d(`Tried to open source file, but failed`, error);
shell.showItemInFolder(sourceFile);
});
} else {
shell.showItemInFolder(sourceFile);
}
}
Loading