github-issue-tower-defence-management 1.116.9 → 1.117.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +3 -3
- package/bin/adapter/entry-points/console/consoleReadApi.js +13 -1
- package/bin/adapter/entry-points/console/consoleReadApi.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-CJJsRF8i.js +101 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-D1V2fi29.css → index-DGVKvwux.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.js +9 -2
- package/bin/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.js.map +1 -1
- package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js +3 -2
- package/bin/adapter/repositories/TranscriptSessionSubAgentActivityRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +1 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +13 -3
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleReadApi.test.ts +4 -0
- package/src/adapter/entry-points/console/consoleReadApi.ts +18 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +3 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +15 -5
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +38 -8
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +35 -12
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +117 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +83 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +67 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +26 -6
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +48 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +24 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +4 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +9 -1
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/index.css +10 -1
- package/src/adapter/entry-points/console/ui-dist/assets/index-CJJsRF8i.js +101 -0
- package/src/adapter/entry-points/console/ui-dist/assets/{index-D1V2fi29.css → index-DGVKvwux.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/console/webServer.test.ts +2 -0
- package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.test.ts +40 -4
- package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.ts +12 -6
- package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +107 -29
- package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +7 -2
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +1 -0
- package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +1 -0
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +1 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +84 -4
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +16 -3
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +1 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +9 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +1 -0
- package/src/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.ts +1 -0
- package/src/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.ts +4 -1
- package/types/adapter/entry-points/console/consoleReadApi.d.ts +4 -0
- package/types/adapter/entry-points/console/consoleReadApi.d.ts.map +1 -1
- package/types/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.d.ts +1 -1
- package/types/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.d.ts.map +1 -1
- package/types/adapter/repositories/TranscriptSessionSubAgentActivityRepository.d.ts +1 -1
- package/types/adapter/repositories/TranscriptSessionSubAgentActivityRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.d.ts +1 -1
- package/types/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.d.ts +1 -1
- package/types/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-Bm0wt3Mn.js +0 -101
- package/src/adapter/entry-points/console/ui-dist/assets/index-Bm0wt3Mn.js +0 -101
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ConsoleListItem,
|
|
23
23
|
ConsoleTabName,
|
|
24
24
|
} from '../logic/types';
|
|
25
|
+
import type { ConsoleCaches } from './useConsoleCaches';
|
|
25
26
|
import type { ConsoleOverlayState } from './useConsoleOverlay';
|
|
26
27
|
import { useConsoleToken } from './useConsoleToken';
|
|
27
28
|
|
|
@@ -113,15 +114,29 @@ export const useConsoleOperations = (
|
|
|
113
114
|
pjcode: string | null,
|
|
114
115
|
mode: ConsoleTabName,
|
|
115
116
|
overlayState: ConsoleOverlayState,
|
|
117
|
+
caches?: ConsoleCaches,
|
|
116
118
|
): ConsoleOperationsApi => {
|
|
117
119
|
const { appendToken } = useConsoleToken();
|
|
118
120
|
const { patchOverlay } = overlayState;
|
|
119
121
|
|
|
122
|
+
const invalidateItemContent = useCallback(
|
|
123
|
+
(item: ConsoleListItem) => {
|
|
124
|
+
if (caches === undefined) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const key = `${item.repo}#${item.number}`;
|
|
128
|
+
caches.body.invalidate(key);
|
|
129
|
+
caches.comments.invalidate(key);
|
|
130
|
+
},
|
|
131
|
+
[caches],
|
|
132
|
+
);
|
|
133
|
+
|
|
120
134
|
const markDone = useCallback(
|
|
121
135
|
(item: ConsoleListItem) => {
|
|
136
|
+
invalidateItemContent(item);
|
|
122
137
|
patchOverlay(overlayKeyForItem(item), { done: true }, mode);
|
|
123
138
|
},
|
|
124
|
-
[patchOverlay, mode],
|
|
139
|
+
[invalidateItemContent, patchOverlay, mode],
|
|
125
140
|
);
|
|
126
141
|
|
|
127
142
|
const reviewPullRequest = useCallback(
|
|
@@ -173,13 +188,14 @@ export const useConsoleOperations = (
|
|
|
173
188
|
storyOptionId: option.id,
|
|
174
189
|
};
|
|
175
190
|
await postConsoleOperation(appendToken, TRIAGE_OPERATION_PATH, request);
|
|
191
|
+
invalidateItemContent(item);
|
|
176
192
|
patchOverlay(
|
|
177
193
|
overlayKeyForItem(item),
|
|
178
194
|
{ done: true, story: { name: option.name, color: option.color } },
|
|
179
195
|
mode,
|
|
180
196
|
);
|
|
181
197
|
},
|
|
182
|
-
[pjcode, appendToken, patchOverlay, mode],
|
|
198
|
+
[pjcode, appendToken, invalidateItemContent, patchOverlay, mode],
|
|
183
199
|
);
|
|
184
200
|
|
|
185
201
|
const setStatus = useCallback(
|
|
@@ -195,13 +211,14 @@ export const useConsoleOperations = (
|
|
|
195
211
|
statusName: option.name,
|
|
196
212
|
};
|
|
197
213
|
await postConsoleOperation(appendToken, TRIAGE_OPERATION_PATH, request);
|
|
214
|
+
invalidateItemContent(item);
|
|
198
215
|
patchOverlay(
|
|
199
216
|
overlayKeyForItem(item),
|
|
200
217
|
{ done: true, status: { name: option.name, color: option.color } },
|
|
201
218
|
mode,
|
|
202
219
|
);
|
|
203
220
|
},
|
|
204
|
-
[pjcode, appendToken, patchOverlay, mode],
|
|
221
|
+
[pjcode, appendToken, invalidateItemContent, patchOverlay, mode],
|
|
205
222
|
);
|
|
206
223
|
|
|
207
224
|
const setInTmuxByHuman = useCallback(
|
|
@@ -216,13 +233,14 @@ export const useConsoleOperations = (
|
|
|
216
233
|
projectItemId: item.projectItemId,
|
|
217
234
|
};
|
|
218
235
|
await postConsoleOperation(appendToken, INTMUX_OPERATION_PATH, request);
|
|
236
|
+
invalidateItemContent(item);
|
|
219
237
|
patchOverlay(
|
|
220
238
|
overlayKeyForItem(item),
|
|
221
239
|
{ done: true, status: { name: option.name, color: option.color } },
|
|
222
240
|
mode,
|
|
223
241
|
);
|
|
224
242
|
},
|
|
225
|
-
[pjcode, appendToken, patchOverlay, mode],
|
|
243
|
+
[pjcode, appendToken, invalidateItemContent, patchOverlay, mode],
|
|
226
244
|
);
|
|
227
245
|
|
|
228
246
|
const closeIssue = useCallback(
|
|
@@ -247,13 +265,15 @@ export const useConsoleOperations = (
|
|
|
247
265
|
if (pjcode === null) {
|
|
248
266
|
throw missingPjcodeError();
|
|
249
267
|
}
|
|
250
|
-
|
|
268
|
+
const comment = await postConsoleComment(appendToken, {
|
|
251
269
|
pjcode,
|
|
252
270
|
url: item.url,
|
|
253
271
|
body,
|
|
254
272
|
});
|
|
273
|
+
invalidateItemContent(item);
|
|
274
|
+
return comment;
|
|
255
275
|
},
|
|
256
|
-
[pjcode, appendToken],
|
|
276
|
+
[pjcode, appendToken, invalidateItemContent],
|
|
257
277
|
);
|
|
258
278
|
|
|
259
279
|
const addInlineReviewComment = useCallback(
|
|
@@ -191,6 +191,7 @@ describe('createConsoleApiClient', () => {
|
|
|
191
191
|
found: true,
|
|
192
192
|
status: {
|
|
193
193
|
isConflicted: true,
|
|
194
|
+
mergeableStatus: 'CONFLICTING',
|
|
194
195
|
isPassedAllCiJob: false,
|
|
195
196
|
isCiStateSuccess: false,
|
|
196
197
|
isBranchOutOfDate: true,
|
|
@@ -203,6 +204,7 @@ describe('createConsoleApiClient', () => {
|
|
|
203
204
|
).toEqual({
|
|
204
205
|
found: true,
|
|
205
206
|
isConflicted: true,
|
|
207
|
+
mergeableStatus: 'CONFLICTING',
|
|
206
208
|
isPassedAllCiJob: false,
|
|
207
209
|
isCiStateSuccess: false,
|
|
208
210
|
isBranchOutOfDate: true,
|
|
@@ -210,6 +212,24 @@ describe('createConsoleApiClient', () => {
|
|
|
210
212
|
});
|
|
211
213
|
});
|
|
212
214
|
|
|
215
|
+
it('defaults the mergeable status to unknown when the server omits it', async () => {
|
|
216
|
+
mockFetchOnce({
|
|
217
|
+
found: true,
|
|
218
|
+
status: {
|
|
219
|
+
isConflicted: false,
|
|
220
|
+
isPassedAllCiJob: true,
|
|
221
|
+
isCiStateSuccess: true,
|
|
222
|
+
isBranchOutOfDate: false,
|
|
223
|
+
missingRequiredCheckNames: [],
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
const client = createConsoleApiClient(appendToken);
|
|
227
|
+
const status = await client.fetchPullRequestStatus(
|
|
228
|
+
'https://github.com/o/r/pull/1',
|
|
229
|
+
);
|
|
230
|
+
expect(status.mergeableStatus).toBe('UNKNOWN');
|
|
231
|
+
});
|
|
232
|
+
|
|
213
233
|
it('returns a not-found pull request status when the server reports none', async () => {
|
|
214
234
|
mockFetchOnce({ found: false, status: null });
|
|
215
235
|
const client = createConsoleApiClient(appendToken);
|
|
@@ -218,6 +238,7 @@ describe('createConsoleApiClient', () => {
|
|
|
218
238
|
).toEqual({
|
|
219
239
|
found: false,
|
|
220
240
|
isConflicted: false,
|
|
241
|
+
mergeableStatus: 'UNKNOWN',
|
|
221
242
|
isPassedAllCiJob: false,
|
|
222
243
|
isCiStateSuccess: false,
|
|
223
244
|
isBranchOutOfDate: false,
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
ConsoleComment,
|
|
4
4
|
ConsoleCommit,
|
|
5
5
|
ConsoleIssueState,
|
|
6
|
+
ConsoleMergeableStatus,
|
|
6
7
|
ConsolePullRequestStatus,
|
|
7
8
|
ConsoleRelatedPullRequest,
|
|
8
9
|
} from '../logic/types';
|
|
@@ -136,6 +137,16 @@ const parseStringArray = (value: unknown): string[] =>
|
|
|
136
137
|
? value.filter((name): name is string => typeof name === 'string')
|
|
137
138
|
: [];
|
|
138
139
|
|
|
140
|
+
const parseMergeableStatus = (value: unknown): ConsoleMergeableStatus => {
|
|
141
|
+
if (value === 'MERGEABLE') {
|
|
142
|
+
return 'MERGEABLE';
|
|
143
|
+
}
|
|
144
|
+
if (value === 'CONFLICTING') {
|
|
145
|
+
return 'CONFLICTING';
|
|
146
|
+
}
|
|
147
|
+
return 'UNKNOWN';
|
|
148
|
+
};
|
|
149
|
+
|
|
139
150
|
const parseRelatedPrs = (payload: unknown): ConsoleRelatedPullRequest[] => {
|
|
140
151
|
if (!isRecord(payload) || !Array.isArray(payload.relatedPullRequests)) {
|
|
141
152
|
return [];
|
|
@@ -146,6 +157,7 @@ const parseRelatedPrs = (payload: unknown): ConsoleRelatedPullRequest[] => {
|
|
|
146
157
|
createdAt: getString(pr.createdAt),
|
|
147
158
|
isDraft: getBoolean(pr.isDraft),
|
|
148
159
|
isConflicted: getBoolean(pr.isConflicted),
|
|
160
|
+
mergeableStatus: parseMergeableStatus(pr.mergeableStatus),
|
|
149
161
|
isPassedAllCiJob: getBoolean(pr.isPassedAllCiJob),
|
|
150
162
|
isCiStateSuccess: getBoolean(pr.isCiStateSuccess),
|
|
151
163
|
isResolvedAllReviewComments: getBoolean(pr.isResolvedAllReviewComments),
|
|
@@ -160,6 +172,7 @@ const parsePullRequestStatus = (payload: unknown): ConsolePullRequestStatus => {
|
|
|
160
172
|
return {
|
|
161
173
|
found: false,
|
|
162
174
|
isConflicted: false,
|
|
175
|
+
mergeableStatus: 'UNKNOWN',
|
|
163
176
|
isPassedAllCiJob: false,
|
|
164
177
|
isCiStateSuccess: false,
|
|
165
178
|
isBranchOutOfDate: false,
|
|
@@ -170,6 +183,7 @@ const parsePullRequestStatus = (payload: unknown): ConsolePullRequestStatus => {
|
|
|
170
183
|
return {
|
|
171
184
|
found: true,
|
|
172
185
|
isConflicted: getBoolean(status.isConflicted),
|
|
186
|
+
mergeableStatus: parseMergeableStatus(status.mergeableStatus),
|
|
173
187
|
isPassedAllCiJob: getBoolean(status.isPassedAllCiJob),
|
|
174
188
|
isCiStateSuccess: getBoolean(status.isCiStateSuccess),
|
|
175
189
|
isBranchOutOfDate: getBoolean(status.isBranchOutOfDate),
|
|
@@ -37,6 +37,54 @@ describe('ResourceCache', () => {
|
|
|
37
37
|
expect(await cache.load('k', 'u')).toBe('ok');
|
|
38
38
|
expect(fetcher).toHaveBeenCalledTimes(2);
|
|
39
39
|
});
|
|
40
|
+
|
|
41
|
+
it('prefetches into the cache and serves the next load from it', async () => {
|
|
42
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
43
|
+
const cache = new ResourceCache(fetcher);
|
|
44
|
+
cache.prefetch('k', 'u');
|
|
45
|
+
await Promise.resolve();
|
|
46
|
+
await Promise.resolve();
|
|
47
|
+
expect(cache.peek('k')).toBe('value:u');
|
|
48
|
+
expect(await cache.load('k', 'u')).toBe('value:u');
|
|
49
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('does not prefetch a key that is already cached', async () => {
|
|
53
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
54
|
+
const cache = new ResourceCache(fetcher);
|
|
55
|
+
await cache.load('k', 'u');
|
|
56
|
+
cache.prefetch('k', 'u');
|
|
57
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('does not start a second prefetch while one is in flight', async () => {
|
|
61
|
+
const fetcher = jest.fn(() => new Promise<string>(() => {}));
|
|
62
|
+
const cache = new ResourceCache(fetcher);
|
|
63
|
+
cache.prefetch('k', 'u');
|
|
64
|
+
cache.prefetch('k', 'u');
|
|
65
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('swallows prefetch errors and leaves the key uncached', async () => {
|
|
69
|
+
const fetcher = jest
|
|
70
|
+
.fn<Promise<string>, [string]>()
|
|
71
|
+
.mockRejectedValue(new Error('boom'));
|
|
72
|
+
const cache = new ResourceCache(fetcher);
|
|
73
|
+
cache.prefetch('k', 'u');
|
|
74
|
+
await Promise.resolve();
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
expect(cache.peek('k')).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('invalidate removes a cached value so the next load refetches', async () => {
|
|
80
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
81
|
+
const cache = new ResourceCache(fetcher);
|
|
82
|
+
await cache.load('k', 'u');
|
|
83
|
+
cache.invalidate('k');
|
|
84
|
+
expect(cache.peek('k')).toBeUndefined();
|
|
85
|
+
await cache.load('k', 'u');
|
|
86
|
+
expect(fetcher).toHaveBeenCalledTimes(2);
|
|
87
|
+
});
|
|
40
88
|
});
|
|
41
89
|
|
|
42
90
|
describe('runWithConcurrency', () => {
|
|
@@ -3,6 +3,7 @@ export type ResourceFetcher<T> = (url: string) => Promise<T>;
|
|
|
3
3
|
export class ResourceCache<T> {
|
|
4
4
|
private readonly cache = new Map<string, T>();
|
|
5
5
|
private readonly inFlight = new Map<string, Promise<T>>();
|
|
6
|
+
private readonly prefetchInFlight = new Set<string>();
|
|
6
7
|
|
|
7
8
|
constructor(private readonly fetcher: ResourceFetcher<T>) {}
|
|
8
9
|
|
|
@@ -30,6 +31,29 @@ export class ResourceCache<T> {
|
|
|
30
31
|
this.inFlight.set(key, promise);
|
|
31
32
|
return promise;
|
|
32
33
|
};
|
|
34
|
+
|
|
35
|
+
prefetch = (key: string, url: string): void => {
|
|
36
|
+
if (
|
|
37
|
+
this.cache.has(key) ||
|
|
38
|
+
this.inFlight.has(key) ||
|
|
39
|
+
this.prefetchInFlight.has(key)
|
|
40
|
+
) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.prefetchInFlight.add(key);
|
|
44
|
+
this.fetcher(url)
|
|
45
|
+
.then((value) => {
|
|
46
|
+
this.cache.set(key, value);
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {})
|
|
49
|
+
.finally(() => {
|
|
50
|
+
this.prefetchInFlight.delete(key);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
invalidate = (key: string): void => {
|
|
55
|
+
this.cache.delete(key);
|
|
56
|
+
};
|
|
33
57
|
}
|
|
34
58
|
|
|
35
59
|
export const runWithConcurrency = async (
|
|
@@ -64,9 +64,12 @@ export type ConsoleIssueState = {
|
|
|
64
64
|
title: string;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
export type ConsoleMergeableStatus = 'MERGEABLE' | 'CONFLICTING' | 'UNKNOWN';
|
|
68
|
+
|
|
67
69
|
export type ConsolePullRequestStatus = {
|
|
68
70
|
found: boolean;
|
|
69
71
|
isConflicted: boolean;
|
|
72
|
+
mergeableStatus: ConsoleMergeableStatus;
|
|
70
73
|
isPassedAllCiJob: boolean;
|
|
71
74
|
isCiStateSuccess: boolean;
|
|
72
75
|
isBranchOutOfDate: boolean;
|
|
@@ -100,6 +103,7 @@ export type ConsoleRelatedPullRequest = {
|
|
|
100
103
|
createdAt: string;
|
|
101
104
|
isDraft: boolean;
|
|
102
105
|
isConflicted: boolean;
|
|
106
|
+
mergeableStatus: ConsoleMergeableStatus;
|
|
103
107
|
isPassedAllCiJob: boolean;
|
|
104
108
|
isCiStateSuccess: boolean;
|
|
105
109
|
isResolvedAllReviewComments: boolean;
|
|
@@ -44,6 +44,7 @@ const buildCaches = (overrides: CachesOverrides = {}): ConsoleCaches => {
|
|
|
44
44
|
fetchPullRequestStatus: async () => ({
|
|
45
45
|
found: true,
|
|
46
46
|
isConflicted: false,
|
|
47
|
+
mergeableStatus: 'MERGEABLE' as const,
|
|
47
48
|
isPassedAllCiJob: true,
|
|
48
49
|
isCiStateSuccess: true,
|
|
49
50
|
isBranchOutOfDate: false,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '../components/operations/ConsoleUndoToast';
|
|
8
8
|
import { useConsoleActionQueue } from '../hooks/useConsoleActionQueue';
|
|
9
9
|
import { useConsoleCaches } from '../hooks/useConsoleCaches';
|
|
10
|
+
import { useConsoleDetailPrefetch } from '../hooks/useConsoleDetailPrefetch';
|
|
10
11
|
import { useConsoleNavigation } from '../hooks/useConsoleNavigation';
|
|
11
12
|
import { useConsoleOperations } from '../hooks/useConsoleOperations';
|
|
12
13
|
import { useConsoleOverlay } from '../hooks/useConsoleOverlay';
|
|
@@ -78,7 +79,12 @@ export const ConsolePage = () => {
|
|
|
78
79
|
navigation;
|
|
79
80
|
|
|
80
81
|
const caches = useConsoleCaches();
|
|
81
|
-
const operations = useConsoleOperations(
|
|
82
|
+
const operations = useConsoleOperations(
|
|
83
|
+
pjcode,
|
|
84
|
+
activeTab,
|
|
85
|
+
overlayState,
|
|
86
|
+
caches,
|
|
87
|
+
);
|
|
82
88
|
const actionQueue = useConsoleActionQueue();
|
|
83
89
|
const now = Date.now();
|
|
84
90
|
|
|
@@ -120,6 +126,8 @@ export const ConsolePage = () => {
|
|
|
120
126
|
);
|
|
121
127
|
}, [selectedItemKey, activeSnapshot]);
|
|
122
128
|
|
|
129
|
+
useConsoleDetailPrefetch(caches, selectedItem, pendingItems);
|
|
130
|
+
|
|
123
131
|
useEffect(() => {
|
|
124
132
|
if (selectedItemKey === null) {
|
|
125
133
|
return;
|
|
@@ -343,6 +343,7 @@ export const consoleRelatedPullRequestsFixture: ConsoleRelatedPullRequest[] = [
|
|
|
343
343
|
createdAt: '2026-06-16T22:30:00.000Z',
|
|
344
344
|
isDraft: false,
|
|
345
345
|
isConflicted: false,
|
|
346
|
+
mergeableStatus: 'MERGEABLE',
|
|
346
347
|
isPassedAllCiJob: true,
|
|
347
348
|
isCiStateSuccess: true,
|
|
348
349
|
isResolvedAllReviewComments: true,
|
|
@@ -351,6 +351,15 @@ body {
|
|
|
351
351
|
opacity: 0.85;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
.console-detail-mergeable-chip {
|
|
355
|
+
font-weight: 600;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.console-detail-mergeable-chip-unknown {
|
|
359
|
+
opacity: 0.7;
|
|
360
|
+
font-weight: 400;
|
|
361
|
+
}
|
|
362
|
+
|
|
354
363
|
.console-detail-labels {
|
|
355
364
|
display: flex;
|
|
356
365
|
flex-wrap: wrap;
|
|
@@ -655,7 +664,7 @@ body {
|
|
|
655
664
|
}
|
|
656
665
|
|
|
657
666
|
.console-op-button {
|
|
658
|
-
padding:
|
|
667
|
+
padding: 9px 16px;
|
|
659
668
|
border: 1px solid #30363d;
|
|
660
669
|
border-radius: 6px;
|
|
661
670
|
background: #21262d;
|