Skip to content

Commit 9ebf4bd

Browse files
feat: allow access to fiddle history (#1745)
* feat: allow access to fiddle history * fix: gist updating and order * chore: address review feedback --------- Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
1 parent 82c0ce3 commit 9ebf4bd

20 files changed

+597
-101
lines changed

src/interfaces.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,17 @@ export interface PMOperationOptions {
215215
packageManager: IPackageManager;
216216
}
217217

218+
export interface GistRevision {
219+
sha: string;
220+
date: string;
221+
title: string;
222+
changes: {
223+
deletions: number;
224+
additions: number;
225+
total: number;
226+
};
227+
}
228+
218229
export enum GlobalSetting {
219230
acceleratorsToBlock = 'acceleratorsToBlock',
220231
channelsToShow = 'channelsToShow',
@@ -236,6 +247,7 @@ export enum GlobalSetting {
236247
knownVersion = 'known-electron-versions',
237248
localVersion = 'local-electron-versions',
238249
packageAuthor = 'packageAuthor',
250+
isShowingGistHistory = 'isShowingGistHistory',
239251
packageManager = 'packageManager',
240252
showObsoleteVersions = 'showObsoleteVersions',
241253
showUndownloadedVersions = 'showUndownloadedVersions',

src/less/components/history.less

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
.revision-list {
2+
max-height: 400px;
3+
overflow-y: auto;
4+
}
5+
6+
.revision-list ul {
7+
list-style-type: none;
8+
padding: 0;
9+
margin: 0;
10+
}
11+
12+
.revision-item {
13+
padding: 10px;
14+
margin-bottom: 5px;
15+
border-radius: 3px;
16+
cursor: pointer;
17+
transition: background-color 0.2s ease;
18+
border-left: 3px solid #106ba3;
19+
}
20+
21+
.revision-item:hover {
22+
background-color: rgba(167, 182, 194, 0.3);
23+
}
24+
25+
.revision-item.active {
26+
background-color: rgba(16, 107, 163, 0.15);
27+
border-left-color: #48aff0;
28+
}
29+
30+
.revision-item.active:hover {
31+
background-color: rgba(16, 107, 163, 0.25);
32+
}
33+
34+
.active-tag {
35+
margin-left: 10px;
36+
}
37+
38+
.revision-content {
39+
display: flex;
40+
flex-direction: column;
41+
}
42+
43+
.revision-icon {
44+
margin-right: 8px;
45+
}
46+
47+
.sha-label {
48+
font-size: 12px;
49+
color: #738694;
50+
margin-left: 10px;
51+
font-family: monospace;
52+
}
53+
54+
.revision-details {
55+
display: flex;
56+
justify-content: space-between;
57+
align-items: center;
58+
margin-top: 5px;
59+
}
60+
61+
.revision-date {
62+
color: #738694;
63+
font-size: 12px;
64+
}
65+
66+
.revision-changes {
67+
display: flex;
68+
gap: 5px;
69+
}
70+
71+
.history-loading {
72+
display: flex;
73+
flex-direction: column;
74+
align-items: center;
75+
padding: 20px;
76+
}

src/less/root.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// Components
1111
@import 'components/commands.less';
1212
@import 'components/output.less';
13+
@import 'components/history.less';
1314
@import 'components/dialogs.less';
1415
@import 'components/mosaic.less';
1516
@import 'components/settings.less';

src/renderer/app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export class App {
8282
);
8383

8484
this.state.gistId = gistId || '';
85+
this.state.activeGistRevision = undefined;
8586
this.state.localPath = localFiddle?.filePath;
8687
this.state.templateName = templateName;
8788

src/renderer/components/commands-action-button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export const GistActionButton = observer(
133133
});
134134

135135
appState.gistId = gist.data.id;
136+
appState.activeGistRevision = undefined;
136137
appState.localPath = undefined;
137138

138139
if (appState.isPublishingGistAsRevision) {
@@ -270,6 +271,7 @@ export const GistActionButton = observer(
270271
}
271272

272273
appState.gistId = undefined;
274+
appState.activeGistRevision = undefined;
273275
appState.activeGistAction = GistActionState.none;
274276
this.setActionType(GistActionType.publish);
275277
}

src/renderer/components/commands.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { AddressBar } from './commands-address-bar';
99
import { BisectHandler } from './commands-bisect';
1010
import { Runner } from './commands-runner';
1111
import { VersionChooser } from './commands-version-chooser';
12+
import { HistoryWrapper } from './history-wrapper';
1213
import { AppState } from '../state';
1314

1415
interface CommandsProps {
@@ -75,7 +76,14 @@ export const Commands = observer(
7576
<div className="title">{title}</div>
7677
) : undefined}
7778
<div>
78-
<AddressBar appState={appState} />
79+
<ControlGroup vertical={false}>
80+
<AddressBar appState={appState} />
81+
</ControlGroup>
82+
{appState.isShowingGistHistory && (
83+
<ControlGroup vertical={false}>
84+
<HistoryWrapper appState={appState} />
85+
</ControlGroup>
86+
)}
7987
<GistActionButton appState={appState} />
8088
</div>
8189
</div>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import * as React from 'react';
2+
3+
import { Button } from '@blueprintjs/core';
4+
import { observer } from 'mobx-react';
5+
6+
import { GistHistoryDialog } from './history';
7+
import { AppState } from '../state';
8+
9+
interface HistoryWrapperProps {
10+
appState: AppState;
11+
buttonOnly?: boolean;
12+
className?: string;
13+
}
14+
15+
/**
16+
* A component that observes the appState and manages the history dialog.
17+
* Can be rendered as just a button or as a button with a dialog.
18+
*/
19+
@observer
20+
export class HistoryWrapper extends React.Component<HistoryWrapperProps> {
21+
private toggleHistory = () => {
22+
const { appState } = this.props;
23+
appState.toggleHistory();
24+
};
25+
26+
private handleRevisionSelect = async (revisionId: string) => {
27+
const { remoteLoader } = window.app;
28+
try {
29+
await remoteLoader.fetchGistAndLoad(
30+
this.props.appState.gistId!,
31+
revisionId,
32+
);
33+
} catch (error: any) {
34+
console.error('Failed to load revision', error);
35+
this.props.appState.showErrorDialog(
36+
`Failed to load revision: ${error.message || 'Unknown error'}`,
37+
);
38+
}
39+
};
40+
41+
public renderHistoryButton() {
42+
const { className } = this.props;
43+
44+
return (
45+
<Button
46+
icon="history"
47+
onClick={this.toggleHistory}
48+
className={className}
49+
aria-label="View revision history"
50+
data-testid="history-button"
51+
/>
52+
);
53+
}
54+
55+
public render() {
56+
const { appState, buttonOnly } = this.props;
57+
const dialogKey = 'history-dialog';
58+
59+
return (
60+
<>
61+
{buttonOnly ? (
62+
this.renderHistoryButton()
63+
) : (
64+
<>
65+
{this.renderHistoryButton()}
66+
<GistHistoryDialog
67+
key={dialogKey}
68+
appState={appState}
69+
isOpen={appState.isHistoryShowing}
70+
onClose={this.toggleHistory}
71+
onRevisionSelect={this.handleRevisionSelect}
72+
activeRevision={appState.activeGistRevision}
73+
/>
74+
</>
75+
)}
76+
</>
77+
);
78+
}
79+
}

0 commit comments

Comments
 (0)