Skip to content

Commit 2061482

Browse files
authored
feat: use github.ts for fetchGistAndLoad() and getGistRevisions() (#1941)
* refactor: use GITHUB_GIST_LOAD in fetchGistAndLoad() * refactor: use GITHUB_GIST_LIST_COMMITS in getGistRevisions()
1 parent efc89e9 commit 2061482

8 files changed

Lines changed: 190 additions & 229 deletions

File tree

src/ambient.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
FiddleEvent,
88
FileTransformOperation,
99
Files,
10+
GistLoadParams,
11+
GistLoadResult,
12+
GistRevision,
1013
IPackageManager,
1114
InstallState,
1215
InstallStateEvent,
@@ -104,6 +107,8 @@ declare global {
104107
): Promise<void>;
105108
fetchVersions(): Promise<Version[]>;
106109
fetchExample(ref: string, path: string): Promise<EditorValues>;
110+
gistListCommits(gistId: string): Promise<GistRevision[]>;
111+
gistLoad(params: GistLoadParams): Promise<GistLoadResult>;
107112
getAvailableThemes(): Promise<Array<LoadedFiddleTheme>>;
108113
getElectronTypes(ver: RunnableVersion): Promise<string | undefined>;
109114
getIsPackageManagerInstalled(

src/interfaces.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,17 @@ export interface GistRevision {
217217
};
218218
}
219219

220+
export interface GistLoadParams {
221+
gistId: string;
222+
revision?: string;
223+
}
224+
225+
export interface GistLoadResult {
226+
files: Record<string, { filename: string; content: string }>;
227+
id: string;
228+
revision?: string;
229+
}
230+
220231
export enum GlobalSetting {
221232
acceleratorsToBlock = 'acceleratorsToBlock',
222233
channelsToShow = 'channelsToShow',

src/main/github.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IpcMainInvokeEvent, app, safeStorage } from 'electron';
66

77
import { getTemplate } from './content';
88
import { ipcMainManager } from './ipc';
9-
import { EditorValues, GistRevision } from '../interfaces';
9+
import { EditorValues, GistLoadResult, GistRevision } from '../interfaces';
1010
import { IpcEvents } from '../ipc-events';
1111
import { isSupportedFile } from '../utils/editor-utils';
1212

@@ -284,15 +284,6 @@ async function handleGistDelete(
284284
return { success: true };
285285
}
286286

287-
interface GistLoadResult {
288-
files: Record<
289-
string,
290-
{ filename: string; content: string; truncated: boolean; rawUrl?: string }
291-
>;
292-
id: string;
293-
revision?: string;
294-
}
295-
296287
async function handleGistLoad(
297288
_event: IpcMainInvokeEvent,
298289
params: unknown,
@@ -328,8 +319,6 @@ async function handleGistLoad(
328319
files[id] = {
329320
filename: data.filename ?? id,
330321
content,
331-
truncated: false,
332-
rawUrl: data.raw_url ?? undefined,
333322
};
334323
}
335324

src/preload/preload.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
FiddleEvent,
88
FileTransformOperation,
99
Files,
10+
GistLoadParams,
1011
IPackageManager,
1112
MessageOptions,
1213
PMOperationOptions,
@@ -109,6 +110,10 @@ export async function setupFiddleGlobal() {
109110
},
110111
fetchExample: (ref: string, path: string) =>
111112
ipcRenderer.invoke(IpcEvents.GITHUB_FETCH_EXAMPLE, { ref, path }),
113+
gistListCommits: (gistId: string) =>
114+
ipcRenderer.invoke(IpcEvents.GITHUB_GIST_LIST_COMMITS, gistId),
115+
gistLoad: (params: GistLoadParams) =>
116+
ipcRenderer.invoke(IpcEvents.GITHUB_GIST_LOAD, params),
112117
getElectronTypes(ver: RunnableVersion) {
113118
return ipcRenderer.invoke(IpcEvents.GET_ELECTRON_TYPES, ver);
114119
},

src/renderer/remote-loader.ts

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import semver from 'semver';
33
import { AppState } from './state';
44
import { disableDownload } from './utils/disable-download';
55
import { isKnownFile, isSupportedFile } from './utils/editor-utils';
6-
import { getOctokit } from './utils/octokit';
76
import { getReleaseChannel } from './versions';
87
import {
98
EditorValues,
@@ -80,31 +79,7 @@ export class RemoteLoader {
8079

8180
public async getGistRevisions(gistId: string): Promise<GistRevision[]> {
8281
try {
83-
const octo = await getOctokit(this.appState);
84-
const { data: revisions } = await octo.gists.listCommits({
85-
gist_id: gistId,
86-
});
87-
88-
const oldestRevision = revisions[revisions.length - 1];
89-
const nonEmptyRevisions = revisions.filter(
90-
(r) =>
91-
r === oldestRevision ||
92-
(r.change_status.additions ?? 0) > 0 ||
93-
(r.change_status.deletions ?? 0) > 0,
94-
);
95-
96-
return nonEmptyRevisions.reverse().map((r, i) => {
97-
return {
98-
sha: r.version,
99-
date: r.committed_at,
100-
changes: {
101-
total: r.change_status.total ?? 0,
102-
additions: r.change_status.additions ?? 0,
103-
deletions: r.change_status.deletions ?? 0,
104-
},
105-
title: i === 0 ? 'Created' : `Revision ${i}`,
106-
};
107-
});
82+
return await window.ElectronFiddle.gistListCommits(gistId);
10883
} catch (error: any) {
10984
this.handleLoadingFailed(error);
11085
return [];
@@ -119,18 +94,12 @@ export class RemoteLoader {
11994
revision?: string,
12095
): Promise<boolean> {
12196
try {
122-
const octo = await getOctokit(this.appState);
123-
const gist = revision
124-
? await octo.gists.getRevision({ gist_id: gistId, sha: revision })
125-
: await octo.gists.get({ gist_id: gistId });
97+
const gist = await window.ElectronFiddle.gistLoad({ gistId, revision });
12698

12799
const values: EditorValues = {};
128100

129-
for (const [id, data] of Object.entries(gist.data.files ?? {})) {
130-
if (!data) continue;
131-
const content = data.truncated
132-
? await fetch(data.raw_url!).then((r) => r.text())
133-
: data.content!;
101+
for (const [id, data] of Object.entries(gist.files)) {
102+
const { content } = data;
134103

135104
if (id === PACKAGE_NAME) {
136105
const deps: Record<string, string> = {};
@@ -205,7 +174,7 @@ export class RemoteLoader {
205174
const result = await this.handleLoadingSuccess(values, gistId);
206175

207176
// Set the active revision - either the specified revision or the latest one
208-
const activeRevision = revision || gist.data.history?.[0]?.version;
177+
const activeRevision = revision || gist.revision;
209178
if (activeRevision) {
210179
this.appState.activeGistRevision = activeRevision;
211180
}

tests/main/github.spec.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,6 @@ describe('github', () => {
606606
'https://gist.githubusercontent.com/raw/large.js',
607607
);
608608
expect(result.files['large.js'].content).toBe(fullContent);
609-
expect(result.files['large.js'].truncated).toBe(false);
610609

611610
fetchSpy.mockRestore();
612611
});
@@ -626,6 +625,71 @@ describe('github', () => {
626625
expect(result[1].title).toBe('Revision 1');
627626
});
628627

628+
it('always keeps the initial revision even with empty change_status', async () => {
629+
await handleTokenSignOut(MOCK_EVENT);
630+
mockOctokitInstance({
631+
gists: {
632+
listCommits: vi.fn().mockResolvedValue({
633+
data: [
634+
{
635+
version: 'sha2',
636+
committed_at: '2026-02-05T12:00:00Z',
637+
change_status: { additions: 5, deletions: 2, total: 7 },
638+
},
639+
{
640+
version: 'sha1',
641+
committed_at: '2026-02-01T10:00:00Z',
642+
change_status: { additions: 0, deletions: 0, total: 0 },
643+
},
644+
],
645+
}),
646+
},
647+
});
648+
await handleTokenSignIn(MOCK_EVENT, VALID_GHP_TOKEN);
649+
650+
const result = await handleGistListCommits(MOCK_EVENT, VALID_GIST_ID);
651+
652+
// The initial revision should NOT be filtered out.
653+
expect(result).toHaveLength(2);
654+
expect(result[0].sha).toBe('sha1');
655+
expect(result[0].title).toBe('Created');
656+
});
657+
658+
it('filters out empty revisions except the initial one', async () => {
659+
await handleTokenSignOut(MOCK_EVENT);
660+
mockOctokitInstance({
661+
gists: {
662+
listCommits: vi.fn().mockResolvedValue({
663+
data: [
664+
{
665+
version: 'sha3',
666+
committed_at: '2026-02-10T12:00:00Z',
667+
change_status: { additions: 3, deletions: 1, total: 4 },
668+
},
669+
{
670+
version: 'sha2',
671+
committed_at: '2026-02-05T12:00:00Z',
672+
change_status: { additions: 0, deletions: 0, total: 0 },
673+
},
674+
{
675+
version: 'sha1',
676+
committed_at: '2026-02-01T10:00:00Z',
677+
change_status: { additions: 0, deletions: 0, total: 0 },
678+
},
679+
],
680+
}),
681+
},
682+
});
683+
await handleTokenSignIn(MOCK_EVENT, VALID_GHP_TOKEN);
684+
685+
const result = await handleGistListCommits(MOCK_EVENT, VALID_GIST_ID);
686+
687+
// sha2 (empty, not initial) is dropped; sha1 (initial) and sha3 are kept.
688+
expect(result).toHaveLength(2);
689+
expect(result[0].sha).toBe('sha1');
690+
expect(result[1].sha).toBe('sha3');
691+
});
692+
629693
it('rejects invalid gist IDs', async () => {
630694
for (const gistId of INVALID_GIST_IDS) {
631695
await expect(handleGistListCommits(MOCK_EVENT, gistId)).rejects.toThrow(

tests/mocks/electron-fiddle.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export class ElectronFiddleMock {
1212
public downloadVersion = vi.fn();
1313
public fetchExample = vi.fn();
1414
public fetchVersions = vi.fn();
15+
public gistListCommits = vi.fn();
16+
public gistLoad = vi.fn();
1517
public getAvailableThemes = vi.fn();
1618
public getElectronTypes = vi.fn();
1719
public getIsPackageManagerInstalled = vi.fn();

0 commit comments

Comments
 (0)