issue-sticker 0.10.0 → 0.12.0
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 +60 -0
- package/dist/_commonjsHelpers-DWwsNxpa.js +8 -0
- package/dist/{externalUserPayload-DNGAp9bP.js → externalUserPayload-DWcXyz2U.js} +2538 -473
- package/dist/index-DA6SoSp7.js +3009 -0
- package/dist/issue-sticker-headless-react.js +3 -3
- package/dist/issue-sticker-headless.js +136 -74
- package/dist/issue-sticker-react.js +1489 -642
- package/dist/{three-_bdgDAmI.js → three-B228j4M6.js} +11 -11
- package/dist/{tooltip-UijEbdDX.js → tooltip-CT7e9xbU.js} +1 -1
- package/headless.d.ts +67 -0
- package/package.json +2 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { c as createLucideIcon, u as ungzip_1, R as REPLAY_EVENT_TYPE, C as CANVAS_INTERACTION_TAG, a as cn, b as Replayer, M as MousePointer2 } from "./three-
|
|
2
|
-
import { r } from "./three-
|
|
1
|
+
import { c as createLucideIcon, u as ungzip_1, R as REPLAY_EVENT_TYPE, C as CANVAS_INTERACTION_TAG, a as cn, b as Replayer, M as MousePointer2 } from "./three-B228j4M6.js";
|
|
2
|
+
import { r } from "./three-B228j4M6.js";
|
|
3
3
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
4
4
|
import { memo, useRef, useEffect, useState, useCallback, useMemo } from "react";
|
|
5
|
-
import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-
|
|
5
|
+
import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-CT7e9xbU.js";
|
|
6
6
|
/**
|
|
7
7
|
* @license lucide-react v0.468.0 - ISC
|
|
8
8
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { u as uploadScreenshotDataUrls, a as uploadSignedMediaWithApi, d as directUploadToSignedUrl, p as pristineFetch, g as getElementPosition, c as
|
|
2
|
-
import { r } from "./three-
|
|
1
|
+
import { u as uploadScreenshotDataUrls, a as uploadSignedMediaWithApi, d as directUploadToSignedUrl, p as pristineFetch, g as getElementPosition, c as captureViewportWithElementCrop, b as captureWithModernScreenshot, e as buildJiraCreateFields, f as buildNotionCreateFields, h as buildGuestOwnerQuery, i as composeIssueFilterGroups, j as buildIssueIntegrationsPayload, k as devLogger, W as WebSocketCollector, C as ConsoleCollector, N as NetworkCollector, s as selectNetworkLogsForSubmit, l as filterExcludeServiceRelatedLogs, m as filterExcludeServiceRelatedConsoleLogs, n as compactNetworkLogsForTransport, o as CONSOLE_LOG_SUBMIT_LIMIT, q as buildExternalUserPayload, r as shrinkNetworkLogsToFit, t as measureJsonBytes, v as sanitizeString, w as collectAttachmentUploads, x as hasReplayUpload, y as getCurrentPageUrl, z as createIssueUploadQueues, S as ScreenRecordingController, A as SessionReplayCaptureController, B as preloadModernScreenshot, D as enqueueIssueUploads, E as normalizeUploadMimeType, I as ISSUE_STICKER_INTERNAL_LOG_PREFIX, F as toSessionReplayMeta, G as loadTusClient, H as createIndexedDbPendingUploadStore, J as ATTACHMENT_UPLOAD_DB_NAME, V as VIDEO_UPLOAD_DB_NAME, K as createMediaUploadApi } from "./externalUserPayload-DWcXyz2U.js";
|
|
2
|
+
import { r } from "./three-B228j4M6.js";
|
|
3
3
|
import "react/jsx-runtime";
|
|
4
4
|
import "react";
|
|
5
5
|
const WINDOW_MS = 6e4;
|
|
@@ -30,10 +30,11 @@ class AutoIssueRateLimit {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
class IssueStickerHeadlessError extends Error {
|
|
33
|
-
constructor(code, message, cause) {
|
|
33
|
+
constructor(code, message, cause, status) {
|
|
34
34
|
super(message);
|
|
35
35
|
this.code = code;
|
|
36
36
|
this.cause = cause;
|
|
37
|
+
this.status = status;
|
|
37
38
|
this.name = "IssueStickerHeadlessError";
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -117,7 +118,8 @@ class HeadlessTransport {
|
|
|
117
118
|
throw new IssueStickerHeadlessError(
|
|
118
119
|
response.status === 401 ? "MEMBER_AUTH_REQUIRED" : "NETWORK_ERROR",
|
|
119
120
|
extractErrorMessage(json == null ? void 0 : json.error, (json == null ? void 0 : json.message) || `요청 실패: ${response.status}`),
|
|
120
|
-
json
|
|
121
|
+
json,
|
|
122
|
+
response.status
|
|
121
123
|
);
|
|
122
124
|
}
|
|
123
125
|
return json;
|
|
@@ -476,21 +478,14 @@ async function captureHeadlessScreenshots({
|
|
|
476
478
|
excludeSelectors
|
|
477
479
|
}) {
|
|
478
480
|
const targetIsDocumentRoot = target === document.body || target === document.documentElement;
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
captureWithModernScreenshot({
|
|
488
|
-
element: target,
|
|
489
|
-
excludeSelectors
|
|
490
|
-
}).catch(() => void 0)
|
|
491
|
-
]
|
|
492
|
-
]);
|
|
493
|
-
return screenshots.filter(
|
|
481
|
+
const { fullDataUrl, elementDataUrl } = await captureViewportWithElementCrop({
|
|
482
|
+
element: target,
|
|
483
|
+
excludeSelectors
|
|
484
|
+
});
|
|
485
|
+
const elementShot = targetIsDocumentRoot || elementDataUrl ? elementDataUrl : await captureWithModernScreenshot({ element: target, excludeSelectors }).catch(
|
|
486
|
+
() => void 0
|
|
487
|
+
);
|
|
488
|
+
return [fullDataUrl, elementShot].filter(
|
|
494
489
|
(dataUrl) => typeof dataUrl === "string" && dataUrl.length > 0
|
|
495
490
|
);
|
|
496
491
|
}
|
|
@@ -954,20 +949,6 @@ class HeadlessPendingMedia {
|
|
|
954
949
|
this.file = null;
|
|
955
950
|
}
|
|
956
951
|
}
|
|
957
|
-
function createReplayFile(snapshot) {
|
|
958
|
-
return new File([snapshot.payload], "session-replay.gz", {
|
|
959
|
-
type: "application/gzip"
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
function createSessionReplayMeta(snapshot) {
|
|
963
|
-
return {
|
|
964
|
-
window_seconds: snapshot.windowSeconds,
|
|
965
|
-
event_count: snapshot.eventCount,
|
|
966
|
-
size_bytes: snapshot.sizeBytes,
|
|
967
|
-
rrweb_version: snapshot.rrwebVersion,
|
|
968
|
-
privacy_config_version: snapshot.privacyConfigVersion
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
952
|
function createInitialHeadlessSnapshot() {
|
|
972
953
|
return {
|
|
973
954
|
phase: "idle",
|
|
@@ -993,6 +974,8 @@ function createInitialHeadlessSnapshot() {
|
|
|
993
974
|
enabled: true,
|
|
994
975
|
error: null
|
|
995
976
|
},
|
|
977
|
+
videoUploads: [],
|
|
978
|
+
attachmentUploads: [],
|
|
996
979
|
formSchema: null,
|
|
997
980
|
filterSchema: null,
|
|
998
981
|
error: null
|
|
@@ -1117,12 +1100,8 @@ function buildIssueCreatePayload({
|
|
|
1117
1100
|
...intent ? { intent } : {},
|
|
1118
1101
|
position: draft.position,
|
|
1119
1102
|
...uploads.screenshotUploadIds.length > 0 ? { screenshot_upload_ids: uploads.screenshotUploadIds } : {},
|
|
1120
|
-
...uploads.
|
|
1121
|
-
...uploads.
|
|
1122
|
-
session_replay_upload_id: uploads.replayUpload.uploadId,
|
|
1123
|
-
session_replay_meta: uploads.replayUpload.meta
|
|
1124
|
-
} : {},
|
|
1125
|
-
...uploads.socketUpload ? { socket_log_upload_id: uploads.socketUpload.uploadId } : {},
|
|
1103
|
+
...uploads.videoPending ? { video_pending: true } : {},
|
|
1104
|
+
...uploads.replayPending ? { session_replay_pending: true } : {},
|
|
1126
1105
|
console_logs: logs.consoleLogs.length > 0 ? logs.consoleLogs : void 0,
|
|
1127
1106
|
user_agent: userAgent,
|
|
1128
1107
|
tenant_id: auth.tenantId,
|
|
@@ -1154,27 +1133,25 @@ async function submitHeadlessIssueDraft({
|
|
|
1154
1133
|
logs,
|
|
1155
1134
|
currentUrl,
|
|
1156
1135
|
userAgent,
|
|
1157
|
-
|
|
1136
|
+
takeSessionReplaySnapshot,
|
|
1158
1137
|
getSocketConnections
|
|
1159
1138
|
}) {
|
|
1160
1139
|
const screenshotUploads = draft.mediaMode === "screenshot" ? await transport.uploadScreenshotPreviews(draft.screenshotPreviewUrls) : [];
|
|
1161
1140
|
const screenshotUploadIds = screenshotUploads.map((upload) => upload.id);
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
{
|
|
1167
|
-
post: (endpoint, body) => transport.request(endpoint, { method: "POST", body }),
|
|
1168
|
-
directUpload: directUploadToSignedUrl
|
|
1169
|
-
},
|
|
1170
|
-
socketSnapshot
|
|
1171
|
-
) : null;
|
|
1141
|
+
const attachments = await collectAttachmentUploads({
|
|
1142
|
+
takeReplay: draft.sessionReplayEnabled ? takeSessionReplaySnapshot : void 0,
|
|
1143
|
+
getConnections: getSocketConnections
|
|
1144
|
+
});
|
|
1172
1145
|
const { payload: issueData, stillOverBudget } = buildIssueCreatePayload({
|
|
1173
1146
|
draft,
|
|
1174
1147
|
auth,
|
|
1175
1148
|
config,
|
|
1176
1149
|
projectId,
|
|
1177
|
-
uploads: {
|
|
1150
|
+
uploads: {
|
|
1151
|
+
screenshotUploadIds,
|
|
1152
|
+
videoPending: draft.mediaMode === "video" && Boolean(pendingVideoFile),
|
|
1153
|
+
replayPending: hasReplayUpload(attachments)
|
|
1154
|
+
},
|
|
1178
1155
|
logs,
|
|
1179
1156
|
currentUrl,
|
|
1180
1157
|
userAgent
|
|
@@ -1186,9 +1163,9 @@ async function submitHeadlessIssueDraft({
|
|
|
1186
1163
|
);
|
|
1187
1164
|
}
|
|
1188
1165
|
const integrations = await issueRepository.buildIntegrationsPayload(draft, formSchema);
|
|
1189
|
-
const
|
|
1190
|
-
await issueRepository.syncDynamicFields(
|
|
1191
|
-
return
|
|
1166
|
+
const issue = await issueRepository.createIssue({ ...issueData, integrations });
|
|
1167
|
+
await issueRepository.syncDynamicFields(issue, draft, formSchema);
|
|
1168
|
+
return { issue, attachments };
|
|
1192
1169
|
}
|
|
1193
1170
|
const HEADLESS_CAPTURE_STYLE_ID = "issue-sticker-headless-capture-style";
|
|
1194
1171
|
const HEADLESS_CAPTURE_STYLE = `
|
|
@@ -1280,6 +1257,21 @@ const DEFAULT_EXCLUDE_SELECTORS = [
|
|
|
1280
1257
|
"#issue-sticker-host",
|
|
1281
1258
|
'[data-issue-sticker-headless-auth="true"]'
|
|
1282
1259
|
];
|
|
1260
|
+
function toHeadlessVideoUpload(state) {
|
|
1261
|
+
return {
|
|
1262
|
+
issueId: state.issueId,
|
|
1263
|
+
status: state.status,
|
|
1264
|
+
progress: state.bytesTotal > 0 ? state.bytesSent / state.bytesTotal : 0
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function toHeadlessAttachmentUpload(state) {
|
|
1268
|
+
return {
|
|
1269
|
+
issueId: state.issueId,
|
|
1270
|
+
kind: state.mediaType === "socket" ? "socket" : "replay",
|
|
1271
|
+
status: state.status,
|
|
1272
|
+
progress: state.bytesTotal > 0 ? state.bytesSent / state.bytesTotal : 0
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1283
1275
|
class HeadlessClientImpl {
|
|
1284
1276
|
constructor(config) {
|
|
1285
1277
|
var _a;
|
|
@@ -1291,6 +1283,7 @@ class HeadlessClientImpl {
|
|
|
1291
1283
|
this.captureCleanup = null;
|
|
1292
1284
|
this.captureSession = null;
|
|
1293
1285
|
this.suppressNextVideoStopSelection = false;
|
|
1286
|
+
this.destroyed = false;
|
|
1294
1287
|
if (!((_a = config.apiKey) == null ? void 0 : _a.trim())) {
|
|
1295
1288
|
throw new IssueStickerHeadlessError("VALIDATION_ERROR", "apiKey가 필요합니다.");
|
|
1296
1289
|
}
|
|
@@ -1314,6 +1307,22 @@ class HeadlessClientImpl {
|
|
|
1314
1307
|
},
|
|
1315
1308
|
getCurrentUrl: () => getCurrentPageUrl() || void 0
|
|
1316
1309
|
});
|
|
1310
|
+
this.uploadQueues = createIssueUploadQueues({
|
|
1311
|
+
api: createMediaUploadApi({
|
|
1312
|
+
post: (endpoint, body) => this.transport.request(endpoint, { method: "POST", body })
|
|
1313
|
+
}),
|
|
1314
|
+
clientInfo: "issue-sticker-sdk",
|
|
1315
|
+
videoStore: createIndexedDbPendingUploadStore(VIDEO_UPLOAD_DB_NAME),
|
|
1316
|
+
attachmentStore: createIndexedDbPendingUploadStore(ATTACHMENT_UPLOAD_DB_NAME),
|
|
1317
|
+
loadTus: loadTusClient,
|
|
1318
|
+
onEvent: (event) => event.mediaType === "video" ? void this.handleVideoUploadEvent(event) : this.handleAttachmentUploadEvent(event)
|
|
1319
|
+
});
|
|
1320
|
+
this.uploadQueues.video.subscribe((states) => {
|
|
1321
|
+
this.state.setSnapshot({ videoUploads: states.map(toHeadlessVideoUpload) });
|
|
1322
|
+
});
|
|
1323
|
+
this.uploadQueues.attachments.subscribe((states) => {
|
|
1324
|
+
this.state.setSnapshot({ attachmentUploads: states.map(toHeadlessAttachmentUpload) });
|
|
1325
|
+
});
|
|
1317
1326
|
this.logCollectors = new HeadlessLogCollectors(this.config, getApiBaseUrl);
|
|
1318
1327
|
this.screenRecordingController = new ScreenRecordingController({
|
|
1319
1328
|
onStopped: (file) => {
|
|
@@ -1366,7 +1375,7 @@ class HeadlessClientImpl {
|
|
|
1366
1375
|
this.state.setSnapshot({ phase: "loadingIssues", error: null });
|
|
1367
1376
|
try {
|
|
1368
1377
|
const issues = await this.issueRepository.fetchAllIssueStickers(filters);
|
|
1369
|
-
this.state.setSnapshot({ issues, phase: "idle" });
|
|
1378
|
+
this.state.setSnapshot({ issues: this.keepKnownVideos(issues), phase: "idle" });
|
|
1370
1379
|
} catch (error) {
|
|
1371
1380
|
const nextError = this.toHeadlessError(error);
|
|
1372
1381
|
this.state.setSnapshot({ phase: "idle", error: nextError });
|
|
@@ -1512,7 +1521,8 @@ class HeadlessClientImpl {
|
|
|
1512
1521
|
intent: input.intent,
|
|
1513
1522
|
dynamicFields: input.dynamicFields ?? {}
|
|
1514
1523
|
};
|
|
1515
|
-
const
|
|
1524
|
+
const videoFile = submission.mediaMode === "video" ? this.pendingMedia.videoFile : null;
|
|
1525
|
+
const { issue: createdIssue, attachments } = await submitHeadlessIssueDraft({
|
|
1516
1526
|
draft: submission,
|
|
1517
1527
|
pendingVideoFile: this.pendingMedia.videoFile,
|
|
1518
1528
|
transport: this.transport,
|
|
@@ -1524,9 +1534,17 @@ class HeadlessClientImpl {
|
|
|
1524
1534
|
logs: this.logCollectors.collect(),
|
|
1525
1535
|
currentUrl: getCurrentPageUrl(),
|
|
1526
1536
|
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
1527
|
-
|
|
1537
|
+
takeSessionReplaySnapshot: () => this.takeSessionReplaySnapshot(),
|
|
1528
1538
|
getSocketConnections: () => this.logCollectors.getSocketConnections()
|
|
1529
1539
|
});
|
|
1540
|
+
await enqueueIssueUploads({
|
|
1541
|
+
videoQueue: this.uploadQueues.video,
|
|
1542
|
+
attachmentQueue: this.uploadQueues.attachments,
|
|
1543
|
+
issueId: createdIssue.id,
|
|
1544
|
+
projectKey: this.state.requireProjectId(),
|
|
1545
|
+
video: videoFile ? { blob: videoFile, mimeType: normalizeUploadMimeType(videoFile.type, "video") } : null,
|
|
1546
|
+
attachments
|
|
1547
|
+
});
|
|
1530
1548
|
this.clearCaptureListeners();
|
|
1531
1549
|
this.clearPendingMedia();
|
|
1532
1550
|
this.captureSession = null;
|
|
@@ -1543,6 +1561,9 @@ class HeadlessClientImpl {
|
|
|
1543
1561
|
throw nextError;
|
|
1544
1562
|
}
|
|
1545
1563
|
}
|
|
1564
|
+
retryVideoUpload(issueId) {
|
|
1565
|
+
this.uploadQueues.retry(issueId);
|
|
1566
|
+
}
|
|
1546
1567
|
async createIssue(input) {
|
|
1547
1568
|
var _a, _b;
|
|
1548
1569
|
try {
|
|
@@ -1569,7 +1590,7 @@ class HeadlessClientImpl {
|
|
|
1569
1590
|
() => captureHeadlessScreenshots({ target, excludeSelectors: this.getExcludeSelectors() })
|
|
1570
1591
|
);
|
|
1571
1592
|
const formSchema = this.snapshot.auth.mode === "member" && Object.keys(input.dynamicFields ?? {}).length > 0 ? await this.loadFormSchema({}) : null;
|
|
1572
|
-
const createdIssue = await submitHeadlessIssueDraft({
|
|
1593
|
+
const { issue: createdIssue, attachments } = await submitHeadlessIssueDraft({
|
|
1573
1594
|
draft: {
|
|
1574
1595
|
...draft,
|
|
1575
1596
|
screenshotPreviewUrls,
|
|
@@ -1587,9 +1608,17 @@ class HeadlessClientImpl {
|
|
|
1587
1608
|
logs,
|
|
1588
1609
|
currentUrl: getCurrentPageUrl(),
|
|
1589
1610
|
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
1590
|
-
|
|
1611
|
+
takeSessionReplaySnapshot: () => this.takeSessionReplaySnapshot(),
|
|
1591
1612
|
getSocketConnections: () => this.logCollectors.getSocketConnections()
|
|
1592
1613
|
});
|
|
1614
|
+
await enqueueIssueUploads({
|
|
1615
|
+
videoQueue: this.uploadQueues.video,
|
|
1616
|
+
attachmentQueue: this.uploadQueues.attachments,
|
|
1617
|
+
issueId: createdIssue.id,
|
|
1618
|
+
projectKey: this.state.requireProjectId(),
|
|
1619
|
+
video: null,
|
|
1620
|
+
attachments
|
|
1621
|
+
});
|
|
1593
1622
|
this.state.setSnapshot({
|
|
1594
1623
|
issues: [createdIssue, ...this.snapshot.issues]
|
|
1595
1624
|
});
|
|
@@ -1642,6 +1671,8 @@ class HeadlessClientImpl {
|
|
|
1642
1671
|
void this.sessionReplayController.refresh();
|
|
1643
1672
|
}
|
|
1644
1673
|
destroy() {
|
|
1674
|
+
this.destroyed = true;
|
|
1675
|
+
this.uploadQueues.stop();
|
|
1645
1676
|
this.clearCaptureListeners();
|
|
1646
1677
|
this.suppressNextVideoStopSelection = true;
|
|
1647
1678
|
this.captureSession = null;
|
|
@@ -1675,6 +1706,11 @@ class HeadlessClientImpl {
|
|
|
1675
1706
|
filterSchema: { groups: [], unavailableReason: "public-mode" }
|
|
1676
1707
|
});
|
|
1677
1708
|
this.logCollectors.start();
|
|
1709
|
+
const { video, attachments } = this.uploadQueues;
|
|
1710
|
+
void this.uploadQueues.resume([result.projectId]).then(() => {
|
|
1711
|
+
void video.pruneExpired();
|
|
1712
|
+
void attachments.pruneExpired();
|
|
1713
|
+
});
|
|
1678
1714
|
this.authBridge.mountHiddenBridgeFrame();
|
|
1679
1715
|
void this.sessionReplayController.initialize();
|
|
1680
1716
|
} catch (error) {
|
|
@@ -1688,27 +1724,53 @@ class HeadlessClientImpl {
|
|
|
1688
1724
|
if (!sessionReplay.enabled || !sessionReplay.canAttach) return null;
|
|
1689
1725
|
try {
|
|
1690
1726
|
const snapshot = await this.sessionReplayController.takeSnapshot();
|
|
1691
|
-
return snapshot ?
|
|
1727
|
+
return snapshot ? toSessionReplayMeta(snapshot) : null;
|
|
1692
1728
|
} catch {
|
|
1693
1729
|
return null;
|
|
1694
1730
|
}
|
|
1695
1731
|
}
|
|
1696
|
-
async
|
|
1732
|
+
async takeSessionReplaySnapshot() {
|
|
1697
1733
|
const sessionReplay = this.sessionReplayController.getSnapshot();
|
|
1698
1734
|
if (!sessionReplay.enabled || !sessionReplay.canAttach) return null;
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1735
|
+
return this.sessionReplayController.takeSnapshot().catch(() => null);
|
|
1736
|
+
}
|
|
1737
|
+
/** 스티커 목록에는 영상 URL 이 없다. 업로드 완료 때 받아 둔 URL 을 같은 이슈에 남긴다. */
|
|
1738
|
+
keepKnownVideos(issues) {
|
|
1739
|
+
const known = new Map(
|
|
1740
|
+
this.snapshot.issues.flatMap((issue) => issue.video ? [[issue.id, issue.video]] : [])
|
|
1741
|
+
);
|
|
1742
|
+
return issues.map(
|
|
1743
|
+
(issue) => !issue.video && known.has(issue.id) ? { ...issue, video: known.get(issue.id) } : issue
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* 업로드가 끝나면 목록·상세의 같은 이슈에 영상 상태를 반영한다. 완료는 재조회해 영상 URL 까지
|
|
1748
|
+
* 채우고, 재조회가 안 되면(config.user 없는 게스트 등) 상태만 고친다.
|
|
1749
|
+
*/
|
|
1750
|
+
async handleVideoUploadEvent(event) {
|
|
1751
|
+
if (event.type === "failed" || this.destroyed) return;
|
|
1752
|
+
const refreshed = event.type === "completed" ? await this.issueRepository.openIssue(event.issueId).catch(() => null) : null;
|
|
1753
|
+
if (this.destroyed) return;
|
|
1754
|
+
const videoStatus = event.type === "completed" ? "ready" : "failed";
|
|
1755
|
+
const { issues, activeIssue } = this.snapshot;
|
|
1756
|
+
this.state.setSnapshot({
|
|
1757
|
+
issues: issues.map(
|
|
1758
|
+
(issue) => issue.id === event.issueId ? { ...issue, video: (refreshed == null ? void 0 : refreshed.video) ?? issue.video, video_status: videoStatus } : issue
|
|
1759
|
+
),
|
|
1760
|
+
activeIssue: (activeIssue == null ? void 0 : activeIssue.id) === event.issueId ? refreshed ?? { ...activeIssue, video_status: videoStatus } : activeIssue
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
/** 리플레이가 연결되거나 포기되면 목록·상세의 같은 이슈에 상태를 반영한다. 소켓 전사는 상태가 없다. */
|
|
1764
|
+
handleAttachmentUploadEvent(event) {
|
|
1765
|
+
if (event.mediaType !== "replay" || event.type === "failed" || this.destroyed) return;
|
|
1766
|
+
const replayStatus = event.type === "completed" ? "ready" : "failed";
|
|
1767
|
+
const { issues, activeIssue } = this.snapshot;
|
|
1768
|
+
this.state.setSnapshot({
|
|
1769
|
+
issues: issues.map(
|
|
1770
|
+
(issue) => issue.id === event.issueId ? { ...issue, replay_status: replayStatus } : issue
|
|
1771
|
+
),
|
|
1772
|
+
activeIssue: (activeIssue == null ? void 0 : activeIssue.id) === event.issueId ? { ...activeIssue, replay_status: replayStatus } : activeIssue
|
|
1773
|
+
});
|
|
1712
1774
|
}
|
|
1713
1775
|
async handleRecordedVideoStopped(file) {
|
|
1714
1776
|
if (this.suppressNextVideoStopSelection) {
|