@overmap-ai/core 1.0.43-projects-licensing.5 → 1.0.43-tiptap.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/README.md +4 -4
- package/dist/overmap-core.js +567 -122
- package/dist/overmap-core.js.map +1 -1
- package/dist/overmap-core.umd.cjs +567 -122
- package/dist/overmap-core.umd.cjs.map +1 -1
- package/dist/sdk/services/AttachmentService.d.ts +30 -8
- package/dist/store/slices/componentSlice.d.ts +34 -2
- package/dist/store/slices/componentTypeSlice.d.ts +34 -2
- package/dist/store/slices/issueSlice.d.ts +26 -6
- package/dist/store/slices/utils.d.ts +12 -0
- package/dist/typings/models/attachments.d.ts +14 -7
- package/package.json +151 -151
package/dist/overmap-core.js
CHANGED
|
@@ -1515,8 +1515,44 @@ const selectHiddenCategoryCount = (state) => {
|
|
|
1515
1515
|
return hiddenCategoryCount;
|
|
1516
1516
|
};
|
|
1517
1517
|
const categoryReducer = categorySlice.reducer;
|
|
1518
|
+
function setAttachments(state, action) {
|
|
1519
|
+
for (const attachment of action.payload) {
|
|
1520
|
+
state.attachments[attachment.offline_id] = attachment;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
function addAttachment(state, action) {
|
|
1524
|
+
if (action.payload.offline_id in state.attachments) {
|
|
1525
|
+
throw new Error(`Attachment ${action.payload.offline_id} already exists.`);
|
|
1526
|
+
}
|
|
1527
|
+
state.attachments[action.payload.offline_id] = action.payload;
|
|
1528
|
+
}
|
|
1529
|
+
function addAttachments(state, action) {
|
|
1530
|
+
for (const attachment of action.payload) {
|
|
1531
|
+
state.attachments[attachment.offline_id] = attachment;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
function updateAttachment(state, action) {
|
|
1535
|
+
if (action.payload.offline_id in state.attachments) {
|
|
1536
|
+
state.attachments[action.payload.offline_id] = action.payload;
|
|
1537
|
+
} else {
|
|
1538
|
+
throw new Error(`Attachment ${action.payload.offline_id} does not exist.`);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function removeAttachment(state, action) {
|
|
1542
|
+
if (action.payload in state.attachments) {
|
|
1543
|
+
delete state.attachments[action.payload];
|
|
1544
|
+
} else {
|
|
1545
|
+
throw new Error(`Attachment ${action.payload} does not exist.`);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function removeAttachments(state, action) {
|
|
1549
|
+
for (const attachmentId of action.payload) {
|
|
1550
|
+
delete state.attachments[attachmentId];
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1518
1553
|
const initialState$k = {
|
|
1519
|
-
components: {}
|
|
1554
|
+
components: {},
|
|
1555
|
+
attachments: {}
|
|
1520
1556
|
};
|
|
1521
1557
|
const componentSlice = createSlice({
|
|
1522
1558
|
name: "components",
|
|
@@ -1535,6 +1571,12 @@ const componentSlice = createSlice({
|
|
|
1535
1571
|
state.components = toOfflineIdRecord(action.payload);
|
|
1536
1572
|
prevComponents = null;
|
|
1537
1573
|
},
|
|
1574
|
+
setComponentAttachments: setAttachments,
|
|
1575
|
+
addComponentAttachment: addAttachment,
|
|
1576
|
+
addComponentAttachments: addAttachments,
|
|
1577
|
+
updateComponentAttachment: updateAttachment,
|
|
1578
|
+
removeComponentAttachment: removeAttachment,
|
|
1579
|
+
removeComponentAttachments: removeAttachments,
|
|
1538
1580
|
updateComponent: (state, action) => {
|
|
1539
1581
|
if (action.payload.offline_id in state.components) {
|
|
1540
1582
|
state.components[action.payload.offline_id] = action.payload;
|
|
@@ -1624,12 +1666,48 @@ const selectComponentTypesFromIds = (componentTypeIds) => (state) => {
|
|
|
1624
1666
|
return acc;
|
|
1625
1667
|
}, []);
|
|
1626
1668
|
};
|
|
1669
|
+
const selectComponentAttachmentMapping = (state) => state.componentReducer.attachments;
|
|
1670
|
+
const selectAllComponentAttachments = createSelector(
|
|
1671
|
+
[selectComponentAttachmentMapping],
|
|
1672
|
+
(mapping) => Object.values(mapping)
|
|
1673
|
+
);
|
|
1674
|
+
const selectAttachmentsOfComponent = restructureCreateSelectorWithArgs(
|
|
1675
|
+
createSelector(
|
|
1676
|
+
[selectAllComponentAttachments, (_state, componentId) => componentId],
|
|
1677
|
+
(attachments, componentId) => {
|
|
1678
|
+
return attachments.filter(({ component }) => componentId === component);
|
|
1679
|
+
}
|
|
1680
|
+
)
|
|
1681
|
+
);
|
|
1682
|
+
const selectAttachmentsOfComponentByType = restructureCreateSelectorWithArgs(
|
|
1683
|
+
createSelector(
|
|
1684
|
+
[selectAllComponentAttachments, (_state, componentId) => componentId],
|
|
1685
|
+
(attachments, componentId) => {
|
|
1686
|
+
const attachmentsOfComponent = attachments.filter(({ component }) => componentId === component);
|
|
1687
|
+
const fileAttachments = attachmentsOfComponent.filter(
|
|
1688
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
1689
|
+
({ file_type }) => !file_type || !file_type.startsWith("image/")
|
|
1690
|
+
);
|
|
1691
|
+
const imageAttachments = attachmentsOfComponent.filter(
|
|
1692
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
1693
|
+
({ file_type }) => file_type && file_type.startsWith("image/")
|
|
1694
|
+
);
|
|
1695
|
+
return { fileAttachments, imageAttachments };
|
|
1696
|
+
}
|
|
1697
|
+
)
|
|
1698
|
+
);
|
|
1627
1699
|
const {
|
|
1628
1700
|
addComponent,
|
|
1629
1701
|
updateComponent,
|
|
1630
1702
|
removeComponent,
|
|
1631
1703
|
addComponentsInBatches,
|
|
1632
1704
|
setComponents,
|
|
1705
|
+
setComponentAttachments,
|
|
1706
|
+
addComponentAttachment,
|
|
1707
|
+
addComponentAttachments,
|
|
1708
|
+
updateComponentAttachment,
|
|
1709
|
+
removeComponentAttachment,
|
|
1710
|
+
removeComponentAttachments,
|
|
1633
1711
|
removeAllComponentsOfType
|
|
1634
1712
|
} = componentSlice.actions;
|
|
1635
1713
|
const componentReducer = componentSlice.reducer;
|
|
@@ -1789,7 +1867,8 @@ const { addStages, updateStages, removeStages, linkStageToForm, unlinkStageToFor
|
|
|
1789
1867
|
const componentStageReducer = componentStageSlice.reducer;
|
|
1790
1868
|
const initialState$h = {
|
|
1791
1869
|
componentTypes: {},
|
|
1792
|
-
hiddenComponentTypeIds: {}
|
|
1870
|
+
hiddenComponentTypeIds: {},
|
|
1871
|
+
attachments: {}
|
|
1793
1872
|
};
|
|
1794
1873
|
const componentTypeSlice = createSlice({
|
|
1795
1874
|
name: "componentTypes",
|
|
@@ -1802,6 +1881,12 @@ const componentTypeSlice = createSlice({
|
|
|
1802
1881
|
setComponentTypes: (state, action) => {
|
|
1803
1882
|
state.componentTypes = toOfflineIdRecord(action.payload);
|
|
1804
1883
|
},
|
|
1884
|
+
setComponentTypeAttachments: setAttachments,
|
|
1885
|
+
addComponentTypeAttachment: addAttachment,
|
|
1886
|
+
addComponentTypeAttachments: addAttachments,
|
|
1887
|
+
updateComponentTypeAttachment: updateAttachment,
|
|
1888
|
+
removeComponentTypeAttachment: removeAttachment,
|
|
1889
|
+
removeComponentTypeAttachments: removeAttachments,
|
|
1805
1890
|
toggleComponentTypeVisibility: (state, action) => {
|
|
1806
1891
|
state.hiddenComponentTypeIds[action.payload] = !state.hiddenComponentTypeIds[action.payload];
|
|
1807
1892
|
},
|
|
@@ -1851,7 +1936,50 @@ const selectComponentTypesByName = restructureCreateSelectorWithArgs(
|
|
|
1851
1936
|
)
|
|
1852
1937
|
);
|
|
1853
1938
|
const selectHiddenComponentTypeIds = (state) => state.componentTypeReducer.hiddenComponentTypeIds;
|
|
1854
|
-
const
|
|
1939
|
+
const selectComponentTypeAttachmentMapping = (state) => state.componentTypeReducer.attachments;
|
|
1940
|
+
const selectAllComponentTypeAttachments = createSelector(
|
|
1941
|
+
[selectComponentTypeAttachmentMapping],
|
|
1942
|
+
(mapping) => Object.values(mapping)
|
|
1943
|
+
);
|
|
1944
|
+
const selectAttachmentsOfComponentType = restructureCreateSelectorWithArgs(
|
|
1945
|
+
createSelector(
|
|
1946
|
+
[selectAllComponentTypeAttachments, (_state, componentTypeId) => componentTypeId],
|
|
1947
|
+
(attachments, componentTypeId) => {
|
|
1948
|
+
return attachments.filter(({ component_type }) => componentTypeId === component_type);
|
|
1949
|
+
}
|
|
1950
|
+
)
|
|
1951
|
+
);
|
|
1952
|
+
const selectAttachmentsOfComponentTypeByType = restructureCreateSelectorWithArgs(
|
|
1953
|
+
createSelector(
|
|
1954
|
+
[selectAllComponentTypeAttachments, (_state, componentTypeId) => componentTypeId],
|
|
1955
|
+
(attachments, componentTypeId) => {
|
|
1956
|
+
const attachmentsOfComponent = attachments.filter(
|
|
1957
|
+
({ component_type }) => component_type === componentTypeId
|
|
1958
|
+
);
|
|
1959
|
+
const fileAttachments = attachmentsOfComponent.filter(
|
|
1960
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
1961
|
+
({ file_type }) => !file_type || !file_type.startsWith("image/")
|
|
1962
|
+
);
|
|
1963
|
+
const imageAttachments = attachmentsOfComponent.filter(
|
|
1964
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
1965
|
+
({ file_type }) => file_type && file_type.startsWith("image/")
|
|
1966
|
+
);
|
|
1967
|
+
return { fileAttachments, imageAttachments };
|
|
1968
|
+
}
|
|
1969
|
+
)
|
|
1970
|
+
);
|
|
1971
|
+
const {
|
|
1972
|
+
addComponentType,
|
|
1973
|
+
setComponentTypes,
|
|
1974
|
+
setComponentTypeAttachments,
|
|
1975
|
+
addComponentTypeAttachment,
|
|
1976
|
+
addComponentTypeAttachments,
|
|
1977
|
+
updateComponentTypeAttachment,
|
|
1978
|
+
removeComponentTypeAttachment,
|
|
1979
|
+
removeComponentTypeAttachments,
|
|
1980
|
+
toggleComponentTypeVisibility,
|
|
1981
|
+
deleteComponentType
|
|
1982
|
+
} = componentTypeSlice.actions;
|
|
1855
1983
|
const componentTypeReducer = componentTypeSlice.reducer;
|
|
1856
1984
|
const initialState$g = {
|
|
1857
1985
|
workspaces: {},
|
|
@@ -1941,11 +2069,7 @@ const issueSlice = createSlice({
|
|
|
1941
2069
|
});
|
|
1942
2070
|
},
|
|
1943
2071
|
// TODO: Reusable function
|
|
1944
|
-
|
|
1945
|
-
for (const attachment of action.payload) {
|
|
1946
|
-
state.attachments[attachment.offline_id] = attachment;
|
|
1947
|
-
}
|
|
1948
|
-
},
|
|
2072
|
+
setIssueAttachments: setAttachments,
|
|
1949
2073
|
setActiveIssueId: (state, action) => {
|
|
1950
2074
|
state.activeIssueId = action.payload;
|
|
1951
2075
|
},
|
|
@@ -1956,17 +2080,8 @@ const issueSlice = createSlice({
|
|
|
1956
2080
|
state.issues[action.payload.offline_id] = action.payload;
|
|
1957
2081
|
},
|
|
1958
2082
|
// TODO: Reusable function
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
throw new Error(`Attachment ${action.payload.offline_id} already exists.`);
|
|
1962
|
-
}
|
|
1963
|
-
state.attachments[action.payload.offline_id] = action.payload;
|
|
1964
|
-
},
|
|
1965
|
-
addAttachments: (state, action) => {
|
|
1966
|
-
for (const attachment of action.payload) {
|
|
1967
|
-
state.attachments[attachment.offline_id] = attachment;
|
|
1968
|
-
}
|
|
1969
|
-
},
|
|
2083
|
+
addIssueAttachment: addAttachment,
|
|
2084
|
+
addIssueAttachments: addAttachments,
|
|
1970
2085
|
updateIssue: (state, action) => {
|
|
1971
2086
|
if (action.payload.offline_id in state.issues) {
|
|
1972
2087
|
state.issues[action.payload.offline_id] = {
|
|
@@ -1978,13 +2093,7 @@ const issueSlice = createSlice({
|
|
|
1978
2093
|
}
|
|
1979
2094
|
},
|
|
1980
2095
|
// TODO: Reusable function
|
|
1981
|
-
|
|
1982
|
-
if (action.payload.offline_id in state.attachments) {
|
|
1983
|
-
state.attachments[action.payload.offline_id] = action.payload;
|
|
1984
|
-
} else {
|
|
1985
|
-
throw new Error(`Attachment ${action.payload.offline_id} does not exist.`);
|
|
1986
|
-
}
|
|
1987
|
-
},
|
|
2096
|
+
updateIssueAttachment: updateAttachment,
|
|
1988
2097
|
removeIssue: (state, action) => {
|
|
1989
2098
|
if (action.payload in state.issues) {
|
|
1990
2099
|
delete state.issues[action.payload];
|
|
@@ -1992,15 +2101,9 @@ const issueSlice = createSlice({
|
|
|
1992
2101
|
throw new Error(`Failed to remove issue because ID doesn't exist: ${action.payload}`);
|
|
1993
2102
|
}
|
|
1994
2103
|
},
|
|
1995
|
-
|
|
1996
|
-
if (action.payload in state.attachments) {
|
|
1997
|
-
delete state.attachments[action.payload];
|
|
1998
|
-
} else {
|
|
1999
|
-
throw new Error(`Attachment ${action.payload} does not exist.`);
|
|
2000
|
-
}
|
|
2001
|
-
},
|
|
2104
|
+
removeIssueAttachment: removeAttachment,
|
|
2002
2105
|
removeAttachmentsOfIssue: (state, action) => {
|
|
2003
|
-
const attachments = Object.values(state.attachments).filter((a) => a.
|
|
2106
|
+
const attachments = Object.values(state.attachments).filter((a) => a.issue === action.payload);
|
|
2004
2107
|
for (const attachment of attachments) {
|
|
2005
2108
|
delete state.attachments[attachment.offline_id];
|
|
2006
2109
|
}
|
|
@@ -2052,25 +2155,25 @@ const issueSlice = createSlice({
|
|
|
2052
2155
|
}
|
|
2053
2156
|
});
|
|
2054
2157
|
const {
|
|
2055
|
-
|
|
2056
|
-
|
|
2158
|
+
addIssueAttachment,
|
|
2159
|
+
addIssueAttachments,
|
|
2057
2160
|
addIssue,
|
|
2058
2161
|
addOrReplaceIssueComment,
|
|
2059
2162
|
addToRecentIssues,
|
|
2060
2163
|
cleanRecentIssues,
|
|
2061
|
-
|
|
2164
|
+
removeIssueAttachment,
|
|
2062
2165
|
removeAttachmentsOfIssue,
|
|
2063
2166
|
removeIssue,
|
|
2064
2167
|
removeIssueComment,
|
|
2065
2168
|
removeRecentIssue,
|
|
2066
2169
|
resetRecentIssues,
|
|
2067
2170
|
setActiveIssueId,
|
|
2068
|
-
|
|
2171
|
+
setIssueAttachments,
|
|
2069
2172
|
setIssueComments,
|
|
2070
2173
|
setIssues,
|
|
2071
2174
|
setVisibleStatuses,
|
|
2072
2175
|
setVisibleUserIds,
|
|
2073
|
-
|
|
2176
|
+
updateIssueAttachment,
|
|
2074
2177
|
updateIssue
|
|
2075
2178
|
} = issueSlice.actions;
|
|
2076
2179
|
const selectIssueMapping = (state) => state.issueReducer.issues;
|
|
@@ -2130,10 +2233,8 @@ const selectPhotoAttachmentsOfIssue = restructureCreateSelectorWithArgs(
|
|
|
2130
2233
|
createSelector(
|
|
2131
2234
|
[selectIssueAttachmentMapping, (_state, issueId) => issueId],
|
|
2132
2235
|
(attachmentMapping, issueId) => {
|
|
2133
|
-
if (!issueId)
|
|
2134
|
-
return void 0;
|
|
2135
2236
|
return Object.values(attachmentMapping).filter(
|
|
2136
|
-
(attachment) => attachment.
|
|
2237
|
+
(attachment) => attachment.issue === issueId && attachment.file_type && attachment.file_type.startsWith("image/")
|
|
2137
2238
|
);
|
|
2138
2239
|
}
|
|
2139
2240
|
)
|
|
@@ -2144,6 +2245,31 @@ const selectCommentsOfIssue = restructureCreateSelectorWithArgs(
|
|
|
2144
2245
|
return Object.values(commentMapping).filter((comment) => comment.issue === issueId);
|
|
2145
2246
|
})
|
|
2146
2247
|
);
|
|
2248
|
+
const selectAttachmentsOfIssue = restructureCreateSelectorWithArgs(
|
|
2249
|
+
createSelector(
|
|
2250
|
+
[selectIssueAttachments, (_state, issueId) => issueId],
|
|
2251
|
+
(attachments, issueId) => {
|
|
2252
|
+
return attachments.filter(({ issue }) => issueId === issue);
|
|
2253
|
+
}
|
|
2254
|
+
)
|
|
2255
|
+
);
|
|
2256
|
+
const selectAttachmentsOfIssueByType = restructureCreateSelectorWithArgs(
|
|
2257
|
+
createSelector(
|
|
2258
|
+
[selectIssueAttachments, (_state, issueId) => issueId],
|
|
2259
|
+
(attachments, issueId) => {
|
|
2260
|
+
const attachmentsOfIssue = attachments.filter(({ issue }) => issue === issueId);
|
|
2261
|
+
const fileAttachments = attachmentsOfIssue.filter(
|
|
2262
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
2263
|
+
({ file_type }) => !file_type || !file_type.startsWith("image/")
|
|
2264
|
+
);
|
|
2265
|
+
const imageAttachments = attachmentsOfIssue.filter(
|
|
2266
|
+
// this null check here is necessary, there are cases where file_type is null or undefined
|
|
2267
|
+
({ file_type }) => file_type && file_type.startsWith("image/")
|
|
2268
|
+
);
|
|
2269
|
+
return { fileAttachments, imageAttachments };
|
|
2270
|
+
}
|
|
2271
|
+
)
|
|
2272
|
+
);
|
|
2147
2273
|
const selectFileAttachmentsOfIssue = restructureCreateSelectorWithArgs(
|
|
2148
2274
|
createSelector(
|
|
2149
2275
|
[selectIssueAttachmentMapping, (_state, issueId) => issueId],
|
|
@@ -2153,7 +2279,7 @@ const selectFileAttachmentsOfIssue = restructureCreateSelectorWithArgs(
|
|
|
2153
2279
|
return Object.values(attachmentMapping).filter(
|
|
2154
2280
|
(attachment) => (
|
|
2155
2281
|
// Files with file_type that is null or not an image file
|
|
2156
|
-
attachment.
|
|
2282
|
+
attachment.issue === issueId && (!attachment.file_type || !attachment.file_type.startsWith("image/"))
|
|
2157
2283
|
)
|
|
2158
2284
|
);
|
|
2159
2285
|
}
|
|
@@ -4082,12 +4208,16 @@ class AttachmentService extends BaseApiService {
|
|
|
4082
4208
|
blocks: [],
|
|
4083
4209
|
blockers: []
|
|
4084
4210
|
});
|
|
4085
|
-
const allAttachments =
|
|
4211
|
+
const allAttachments = {
|
|
4212
|
+
issue_attachments: Object.values(this.client.store.getState().issueReducer.attachments),
|
|
4213
|
+
component_attachments: Object.values(this.client.store.getState().componentReducer.attachments),
|
|
4214
|
+
component_type_attachments: Object.values(this.client.store.getState().componentTypeReducer.attachments)
|
|
4215
|
+
};
|
|
4086
4216
|
return [allAttachments, promise];
|
|
4087
4217
|
}
|
|
4088
4218
|
// Attachments aren't models, so we use the OptimisticGenericResult type instead
|
|
4089
|
-
async
|
|
4090
|
-
const { description: description2,
|
|
4219
|
+
async addIssueAttachment(attachmentPayload) {
|
|
4220
|
+
const { description: description2, issue, file_sha1, offline_id } = attachmentPayload;
|
|
4091
4221
|
if (!attachmentPayload.file.objectURL) {
|
|
4092
4222
|
throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
|
|
4093
4223
|
}
|
|
@@ -4098,45 +4228,157 @@ class AttachmentService extends BaseApiService {
|
|
|
4098
4228
|
file_type: attachmentPayload.file.type
|
|
4099
4229
|
};
|
|
4100
4230
|
await this.client.files.addCache(attachmentPayload.file, file_sha1);
|
|
4101
|
-
this.client.store.dispatch(
|
|
4231
|
+
this.client.store.dispatch(addIssueAttachment(offlineAttachment));
|
|
4102
4232
|
const [fileProps] = await this.client.files.uploadFileToS3(file_sha1);
|
|
4103
4233
|
const promise = this.enqueueRequest({
|
|
4104
4234
|
description: "Create attachment",
|
|
4105
4235
|
method: HttpMethod.POST,
|
|
4106
|
-
url: `/issues/${
|
|
4107
|
-
blocks: [offline_id,
|
|
4236
|
+
url: `/issues/${issue}/attach/`,
|
|
4237
|
+
blocks: [offline_id, issue],
|
|
4108
4238
|
blockers: [file_sha1],
|
|
4109
4239
|
payload: {
|
|
4110
4240
|
offline_id,
|
|
4111
|
-
issue
|
|
4241
|
+
issue,
|
|
4112
4242
|
description: description2 ?? "",
|
|
4113
4243
|
submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
|
|
4114
4244
|
...fileProps
|
|
4115
4245
|
}
|
|
4116
4246
|
});
|
|
4247
|
+
promise.catch((error2) => {
|
|
4248
|
+
this.client.store.dispatch(removeIssueAttachment(offlineAttachment.offline_id));
|
|
4249
|
+
throw error2;
|
|
4250
|
+
});
|
|
4117
4251
|
return [offlineAttachment, promise];
|
|
4118
4252
|
}
|
|
4119
|
-
async
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
}
|
|
4137
|
-
|
|
4253
|
+
async addComponentAttachment(attachmentPayload) {
|
|
4254
|
+
const { description: description2, component, file_sha1, offline_id } = attachmentPayload;
|
|
4255
|
+
if (!attachmentPayload.file.objectURL) {
|
|
4256
|
+
throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
|
|
4257
|
+
}
|
|
4258
|
+
const offlineAttachment = {
|
|
4259
|
+
...attachmentPayload,
|
|
4260
|
+
file: attachmentPayload.file.objectURL,
|
|
4261
|
+
file_name: attachmentPayload.file.name,
|
|
4262
|
+
file_type: attachmentPayload.file.type
|
|
4263
|
+
};
|
|
4264
|
+
await this.client.files.addCache(attachmentPayload.file, file_sha1);
|
|
4265
|
+
this.client.store.dispatch(addComponentAttachment(offlineAttachment));
|
|
4266
|
+
const [fileProps] = await this.client.files.uploadFileToS3(file_sha1);
|
|
4267
|
+
const promise = this.enqueueRequest({
|
|
4268
|
+
description: "Create attachment",
|
|
4269
|
+
method: HttpMethod.POST,
|
|
4270
|
+
url: `/components/${component}/attach/`,
|
|
4271
|
+
blocks: [offline_id, component],
|
|
4272
|
+
blockers: [file_sha1],
|
|
4273
|
+
payload: {
|
|
4274
|
+
offline_id,
|
|
4275
|
+
component,
|
|
4276
|
+
description: description2 ?? "",
|
|
4277
|
+
submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
|
|
4278
|
+
...fileProps
|
|
4279
|
+
}
|
|
4280
|
+
});
|
|
4281
|
+
promise.catch((error2) => {
|
|
4282
|
+
this.client.store.dispatch(removeComponentAttachment(offlineAttachment.offline_id));
|
|
4283
|
+
throw error2;
|
|
4284
|
+
});
|
|
4285
|
+
return [offlineAttachment, promise];
|
|
4138
4286
|
}
|
|
4139
|
-
async
|
|
4287
|
+
async addComponentTypeAttachment(attachmentPayload) {
|
|
4288
|
+
const { description: description2, component_type, file_sha1, offline_id } = attachmentPayload;
|
|
4289
|
+
if (!attachmentPayload.file.objectURL) {
|
|
4290
|
+
throw new Error("Expected attachmentPayload.file.objectURL to be defined.");
|
|
4291
|
+
}
|
|
4292
|
+
const offlineAttachment = {
|
|
4293
|
+
...attachmentPayload,
|
|
4294
|
+
file: attachmentPayload.file.objectURL,
|
|
4295
|
+
file_name: attachmentPayload.file.name,
|
|
4296
|
+
file_type: attachmentPayload.file.type
|
|
4297
|
+
};
|
|
4298
|
+
await this.client.files.addCache(attachmentPayload.file, file_sha1);
|
|
4299
|
+
this.client.store.dispatch(addComponentTypeAttachment(offlineAttachment));
|
|
4300
|
+
const [fileProps] = await this.client.files.uploadFileToS3(file_sha1);
|
|
4301
|
+
const promise = this.enqueueRequest({
|
|
4302
|
+
description: "Create attachment",
|
|
4303
|
+
method: HttpMethod.POST,
|
|
4304
|
+
url: `/components/types/${component_type}/attach/`,
|
|
4305
|
+
blocks: [offline_id, component_type],
|
|
4306
|
+
blockers: [file_sha1],
|
|
4307
|
+
payload: {
|
|
4308
|
+
offline_id,
|
|
4309
|
+
component_type,
|
|
4310
|
+
description: description2 ?? "",
|
|
4311
|
+
submitted_at: (/* @__PURE__ */ new Date()).getTime() / 1e3,
|
|
4312
|
+
...fileProps
|
|
4313
|
+
}
|
|
4314
|
+
});
|
|
4315
|
+
promise.catch((error2) => {
|
|
4316
|
+
this.client.store.dispatch(removeComponentTypeAttachment(offlineAttachment.offline_id));
|
|
4317
|
+
throw error2;
|
|
4318
|
+
});
|
|
4319
|
+
return [offlineAttachment, promise];
|
|
4320
|
+
}
|
|
4321
|
+
/** the outer Promise is needed to await the hashing of each file, which is required before offline use. If wanting to
|
|
4322
|
+
* attach promise handlers to the request to add the attachment in the backend, apply it on the promise returned from the
|
|
4323
|
+
* OptimisticModelResult. */
|
|
4324
|
+
attachFilesToIssue(filesToSubmit, issueId) {
|
|
4325
|
+
return filesToSubmit.map((file) => {
|
|
4326
|
+
if (!(file instanceof File)) {
|
|
4327
|
+
throw new Error("Expected a File instance.");
|
|
4328
|
+
}
|
|
4329
|
+
const photoAttachmentPromise = async (file2) => {
|
|
4330
|
+
const hash = await hashFile(file2);
|
|
4331
|
+
const attachment = offline({
|
|
4332
|
+
file: file2,
|
|
4333
|
+
file_name: file2.name,
|
|
4334
|
+
file_type: file2.type,
|
|
4335
|
+
issue: issueId,
|
|
4336
|
+
file_sha1: hash
|
|
4337
|
+
});
|
|
4338
|
+
return this.addIssueAttachment(attachment);
|
|
4339
|
+
};
|
|
4340
|
+
return photoAttachmentPromise(file);
|
|
4341
|
+
});
|
|
4342
|
+
}
|
|
4343
|
+
attachFilesToComponent(filesToSubmit, componentId) {
|
|
4344
|
+
return filesToSubmit.map((file) => {
|
|
4345
|
+
if (!(file instanceof File)) {
|
|
4346
|
+
throw new Error("Expected a File instance.");
|
|
4347
|
+
}
|
|
4348
|
+
const photoAttachmentPromise = async (file2) => {
|
|
4349
|
+
const hash = await hashFile(file2);
|
|
4350
|
+
const attachment = offline({
|
|
4351
|
+
file: file2,
|
|
4352
|
+
file_name: file2.name,
|
|
4353
|
+
file_type: file2.type,
|
|
4354
|
+
component: componentId,
|
|
4355
|
+
file_sha1: hash
|
|
4356
|
+
});
|
|
4357
|
+
return this.addComponentAttachment(attachment);
|
|
4358
|
+
};
|
|
4359
|
+
return photoAttachmentPromise(file);
|
|
4360
|
+
});
|
|
4361
|
+
}
|
|
4362
|
+
attachFilesToComponentType(filesToSubmit, componentTypeId) {
|
|
4363
|
+
return filesToSubmit.map((file) => {
|
|
4364
|
+
if (!(file instanceof File)) {
|
|
4365
|
+
throw new Error("Expected a File instance.");
|
|
4366
|
+
}
|
|
4367
|
+
const photoAttachmentPromise = async (file2) => {
|
|
4368
|
+
const hash = await hashFile(file2);
|
|
4369
|
+
const attachment = offline({
|
|
4370
|
+
file: file2,
|
|
4371
|
+
file_name: file2.name,
|
|
4372
|
+
file_type: file2.type,
|
|
4373
|
+
component_type: componentTypeId,
|
|
4374
|
+
file_sha1: hash
|
|
4375
|
+
});
|
|
4376
|
+
return this.addComponentTypeAttachment(attachment);
|
|
4377
|
+
};
|
|
4378
|
+
return photoAttachmentPromise(file);
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
async replaceIssueAttachmentFile(attachmentId, newFile) {
|
|
4140
4382
|
const { store } = this.client;
|
|
4141
4383
|
const attachment = store.getState().issueReducer.attachments[attachmentId];
|
|
4142
4384
|
if (!attachment)
|
|
@@ -4151,16 +4393,75 @@ class AttachmentService extends BaseApiService {
|
|
|
4151
4393
|
if (!newFile.objectURL) {
|
|
4152
4394
|
throw new Error(`newFile["objectURL"] is unexpectedly ${newFile.objectURL}`);
|
|
4153
4395
|
}
|
|
4154
|
-
store.dispatch(
|
|
4396
|
+
store.dispatch(
|
|
4397
|
+
updateIssueAttachment({ ...attachment, file_sha1: newSha1, file: URL.createObjectURL(newFile) })
|
|
4398
|
+
);
|
|
4399
|
+
await this.client.files.addCache(newFile, newSha1);
|
|
4400
|
+
const [fileProps] = await this.client.files.uploadFileToS3(newSha1).catch((e) => {
|
|
4401
|
+
store.dispatch(updateIssueAttachment(attachment));
|
|
4402
|
+
throw e;
|
|
4403
|
+
});
|
|
4404
|
+
const promise2 = this.enqueueRequest({
|
|
4405
|
+
description: "Edit attachment",
|
|
4406
|
+
method: HttpMethod.PATCH,
|
|
4407
|
+
url: `/attachments/issues/${attachment.offline_id}/`,
|
|
4408
|
+
isResponseBlob: false,
|
|
4409
|
+
payload: fileProps,
|
|
4410
|
+
blockers: [attachmentId, newSha1],
|
|
4411
|
+
blocks: [attachmentId, newSha1]
|
|
4412
|
+
});
|
|
4413
|
+
try {
|
|
4414
|
+
const result = await promise2;
|
|
4415
|
+
void this.client.files.removeCache(attachment.file_sha1);
|
|
4416
|
+
return result;
|
|
4417
|
+
} catch (e) {
|
|
4418
|
+
if (oldFile) {
|
|
4419
|
+
store.dispatch(
|
|
4420
|
+
updateIssueAttachment({
|
|
4421
|
+
...attachment,
|
|
4422
|
+
file_sha1: attachment.file_sha1,
|
|
4423
|
+
file: URL.createObjectURL(oldFile)
|
|
4424
|
+
})
|
|
4425
|
+
);
|
|
4426
|
+
}
|
|
4427
|
+
throw e;
|
|
4428
|
+
}
|
|
4429
|
+
};
|
|
4430
|
+
const offlineAttachment = {
|
|
4431
|
+
...attachment,
|
|
4432
|
+
file_sha1: newSha1,
|
|
4433
|
+
file: URL.createObjectURL(newFile)
|
|
4434
|
+
};
|
|
4435
|
+
const promise = performRequest2();
|
|
4436
|
+
return [offlineAttachment, promise];
|
|
4437
|
+
}
|
|
4438
|
+
async replaceComponentAttachmentFile(attachmentId, newFile) {
|
|
4439
|
+
const { store } = this.client;
|
|
4440
|
+
const attachment = store.getState().componentReducer.attachments[attachmentId];
|
|
4441
|
+
if (!attachment)
|
|
4442
|
+
throw new Error(`Attachment ${attachmentId} not found`);
|
|
4443
|
+
let oldFile = void 0;
|
|
4444
|
+
const newSha1 = await hashFile(newFile);
|
|
4445
|
+
const performRequest2 = async () => {
|
|
4446
|
+
oldFile = await this.client.files.fetchCache(attachment.file_sha1);
|
|
4447
|
+
if (!oldFile) {
|
|
4448
|
+
console.error(`Failed to fetch old file from cache for sha1 ${attachment.file_sha1}.`);
|
|
4449
|
+
}
|
|
4450
|
+
if (!newFile.objectURL) {
|
|
4451
|
+
throw new Error(`newFile["objectURL"] is unexpectedly ${newFile.objectURL}`);
|
|
4452
|
+
}
|
|
4453
|
+
store.dispatch(
|
|
4454
|
+
updateComponentAttachment({ ...attachment, file_sha1: newSha1, file: URL.createObjectURL(newFile) })
|
|
4455
|
+
);
|
|
4155
4456
|
await this.client.files.addCache(newFile, newSha1);
|
|
4156
4457
|
const [fileProps] = await this.client.files.uploadFileToS3(newSha1).catch((e) => {
|
|
4157
|
-
store.dispatch(
|
|
4458
|
+
store.dispatch(updateComponentAttachment(attachment));
|
|
4158
4459
|
throw e;
|
|
4159
4460
|
});
|
|
4160
4461
|
const promise2 = this.enqueueRequest({
|
|
4161
4462
|
description: "Edit attachment",
|
|
4162
4463
|
method: HttpMethod.PATCH,
|
|
4163
|
-
url: `/attachments/${attachment.offline_id}/`,
|
|
4464
|
+
url: `/attachments/components/${attachment.offline_id}/`,
|
|
4164
4465
|
isResponseBlob: false,
|
|
4165
4466
|
payload: fileProps,
|
|
4166
4467
|
blockers: [attachmentId, newSha1],
|
|
@@ -4173,7 +4474,68 @@ class AttachmentService extends BaseApiService {
|
|
|
4173
4474
|
} catch (e) {
|
|
4174
4475
|
if (oldFile) {
|
|
4175
4476
|
store.dispatch(
|
|
4176
|
-
|
|
4477
|
+
updateComponentAttachment({
|
|
4478
|
+
...attachment,
|
|
4479
|
+
file_sha1: attachment.file_sha1,
|
|
4480
|
+
file: URL.createObjectURL(oldFile)
|
|
4481
|
+
})
|
|
4482
|
+
);
|
|
4483
|
+
}
|
|
4484
|
+
throw e;
|
|
4485
|
+
}
|
|
4486
|
+
};
|
|
4487
|
+
const offlineAttachment = {
|
|
4488
|
+
...attachment,
|
|
4489
|
+
file_sha1: newSha1,
|
|
4490
|
+
file: URL.createObjectURL(newFile)
|
|
4491
|
+
};
|
|
4492
|
+
const promise = performRequest2();
|
|
4493
|
+
return [offlineAttachment, promise];
|
|
4494
|
+
}
|
|
4495
|
+
async replaceComponentTypeAttachmentFile(attachmentId, newFile) {
|
|
4496
|
+
const { store } = this.client;
|
|
4497
|
+
const attachment = store.getState().componentTypeReducer.attachments[attachmentId];
|
|
4498
|
+
if (!attachment)
|
|
4499
|
+
throw new Error(`Attachment ${attachmentId} not found`);
|
|
4500
|
+
let oldFile = void 0;
|
|
4501
|
+
const newSha1 = await hashFile(newFile);
|
|
4502
|
+
const performRequest2 = async () => {
|
|
4503
|
+
oldFile = await this.client.files.fetchCache(attachment.file_sha1);
|
|
4504
|
+
if (!oldFile) {
|
|
4505
|
+
console.error(`Failed to fetch old file from cache for sha1 ${attachment.file_sha1}.`);
|
|
4506
|
+
}
|
|
4507
|
+
if (!newFile.objectURL) {
|
|
4508
|
+
throw new Error(`newFile["objectURL"] is unexpectedly ${newFile.objectURL}`);
|
|
4509
|
+
}
|
|
4510
|
+
store.dispatch(
|
|
4511
|
+
updateComponentTypeAttachment({
|
|
4512
|
+
...attachment,
|
|
4513
|
+
file_sha1: newSha1,
|
|
4514
|
+
file: URL.createObjectURL(newFile)
|
|
4515
|
+
})
|
|
4516
|
+
);
|
|
4517
|
+
await this.client.files.addCache(newFile, newSha1);
|
|
4518
|
+
const [fileProps] = await this.client.files.uploadFileToS3(newSha1).catch((e) => {
|
|
4519
|
+
store.dispatch(updateComponentTypeAttachment(attachment));
|
|
4520
|
+
throw e;
|
|
4521
|
+
});
|
|
4522
|
+
const promise2 = this.enqueueRequest({
|
|
4523
|
+
description: "Edit attachment",
|
|
4524
|
+
method: HttpMethod.PATCH,
|
|
4525
|
+
url: `/attachments/component_types/${attachment.offline_id}/`,
|
|
4526
|
+
isResponseBlob: false,
|
|
4527
|
+
payload: fileProps,
|
|
4528
|
+
blockers: [attachmentId, newSha1],
|
|
4529
|
+
blocks: [attachmentId, newSha1]
|
|
4530
|
+
});
|
|
4531
|
+
try {
|
|
4532
|
+
const result = await promise2;
|
|
4533
|
+
void this.client.files.removeCache(attachment.file_sha1);
|
|
4534
|
+
return result;
|
|
4535
|
+
} catch (e) {
|
|
4536
|
+
if (oldFile) {
|
|
4537
|
+
store.dispatch(
|
|
4538
|
+
updateComponentTypeAttachment({
|
|
4177
4539
|
...attachment,
|
|
4178
4540
|
file_sha1: attachment.file_sha1,
|
|
4179
4541
|
file: URL.createObjectURL(oldFile)
|
|
@@ -4193,23 +4555,54 @@ class AttachmentService extends BaseApiService {
|
|
|
4193
4555
|
}
|
|
4194
4556
|
/**
|
|
4195
4557
|
* Deletes an attachment and associated data in the cloud, in the Redux store and the cache.
|
|
4196
|
-
* @param
|
|
4558
|
+
* @param issueAttachmentId
|
|
4197
4559
|
*/
|
|
4198
|
-
|
|
4560
|
+
deleteIssueAttachment(issueAttachmentId) {
|
|
4199
4561
|
const { store } = this.client;
|
|
4200
|
-
const
|
|
4201
|
-
const attachment = storeStateIssueReducer.attachments[attachmentId];
|
|
4562
|
+
const attachment = selectIssueAttachmentMapping(store.getState())[issueAttachmentId];
|
|
4202
4563
|
if (!attachment) {
|
|
4203
|
-
throw new Error(`Attachment ${
|
|
4564
|
+
throw new Error(`Attachment ${issueAttachmentId} not found`);
|
|
4204
4565
|
}
|
|
4205
|
-
store.dispatch(
|
|
4566
|
+
store.dispatch(removeIssueAttachment(issueAttachmentId));
|
|
4206
4567
|
void this.client.files.removeCache(attachment.file_sha1);
|
|
4207
4568
|
return this.enqueueRequest({
|
|
4208
4569
|
description: "Delete attachment",
|
|
4209
4570
|
method: HttpMethod.DELETE,
|
|
4210
|
-
url: `/attachments/${
|
|
4211
|
-
blockers: [
|
|
4212
|
-
blocks: [
|
|
4571
|
+
url: `/attachments/issues/${issueAttachmentId}/`,
|
|
4572
|
+
blockers: [issueAttachmentId],
|
|
4573
|
+
blocks: [issueAttachmentId]
|
|
4574
|
+
});
|
|
4575
|
+
}
|
|
4576
|
+
deleteComponentAttachment(componentAttachmentId) {
|
|
4577
|
+
const { store } = this.client;
|
|
4578
|
+
const attachment = selectComponentAttachmentMapping(store.getState())[componentAttachmentId];
|
|
4579
|
+
if (!attachment) {
|
|
4580
|
+
throw new Error(`Attachment ${componentAttachmentId} not found`);
|
|
4581
|
+
}
|
|
4582
|
+
store.dispatch(removeComponentAttachment(componentAttachmentId));
|
|
4583
|
+
void this.client.files.removeCache(attachment.file_sha1);
|
|
4584
|
+
return this.enqueueRequest({
|
|
4585
|
+
description: "Delete attachment",
|
|
4586
|
+
method: HttpMethod.DELETE,
|
|
4587
|
+
url: `/attachments/components/${componentAttachmentId}/`,
|
|
4588
|
+
blockers: [componentAttachmentId],
|
|
4589
|
+
blocks: [componentAttachmentId]
|
|
4590
|
+
});
|
|
4591
|
+
}
|
|
4592
|
+
deleteComponentTypeAttachment(componentTypeAttachmentId) {
|
|
4593
|
+
const { store } = this.client;
|
|
4594
|
+
const attachment = selectComponentTypeAttachmentMapping(store.getState())[componentTypeAttachmentId];
|
|
4595
|
+
if (!attachment) {
|
|
4596
|
+
throw new Error(`Attachment ${componentTypeAttachmentId} not found`);
|
|
4597
|
+
}
|
|
4598
|
+
store.dispatch(removeComponentTypeAttachment(componentTypeAttachmentId));
|
|
4599
|
+
void this.client.files.removeCache(attachment.file_sha1);
|
|
4600
|
+
return this.enqueueRequest({
|
|
4601
|
+
description: "Delete attachment",
|
|
4602
|
+
method: HttpMethod.DELETE,
|
|
4603
|
+
url: `/attachments/component_types/${componentTypeAttachmentId}/`,
|
|
4604
|
+
blockers: [componentTypeAttachmentId],
|
|
4605
|
+
blocks: [componentTypeAttachmentId]
|
|
4213
4606
|
});
|
|
4214
4607
|
}
|
|
4215
4608
|
}
|
|
@@ -4601,13 +4994,26 @@ class ComponentService extends BaseApiService {
|
|
|
4601
4994
|
return [component, promise];
|
|
4602
4995
|
}
|
|
4603
4996
|
async remove(id) {
|
|
4604
|
-
this.client
|
|
4997
|
+
const { store } = this.client;
|
|
4998
|
+
const backupComponent = selectComponent(id)(store.getState());
|
|
4999
|
+
if (!backupComponent)
|
|
5000
|
+
throw new Error(`No component with id ${id} found in the store`);
|
|
5001
|
+
const attachmentsOfComponent = selectAttachmentsOfComponent(id)(store.getState());
|
|
5002
|
+
store.dispatch(removeComponent(id));
|
|
5003
|
+
if (attachmentsOfComponent.length > 0) {
|
|
5004
|
+
const attachmentsOfComponentIds = attachmentsOfComponent.map(({ offline_id }) => offline_id);
|
|
5005
|
+
store.dispatch(removeComponentAttachments(attachmentsOfComponentIds));
|
|
5006
|
+
}
|
|
4605
5007
|
return this.enqueueRequest({
|
|
4606
5008
|
description: "Delete issue",
|
|
4607
5009
|
method: HttpMethod.DELETE,
|
|
4608
5010
|
url: `/components/${id}/`,
|
|
4609
5011
|
blockers: [id],
|
|
4610
5012
|
blocks: []
|
|
5013
|
+
}).catch((err) => {
|
|
5014
|
+
store.dispatch(addComponent(backupComponent));
|
|
5015
|
+
store.dispatch(addComponentAttachments(attachmentsOfComponent));
|
|
5016
|
+
throw err;
|
|
4611
5017
|
});
|
|
4612
5018
|
}
|
|
4613
5019
|
async deleteAllByComponentType(componentTypeId) {
|
|
@@ -4918,13 +5324,19 @@ class ComponentTypeService extends BaseApiService {
|
|
|
4918
5324
|
if (!componentType) {
|
|
4919
5325
|
throw new Error("Expected componentType to exist");
|
|
4920
5326
|
}
|
|
4921
|
-
const
|
|
4922
|
-
|
|
4923
|
-
removeStages(
|
|
4924
|
-
componentTypeStages.map((componentTypeStage) => componentTypeStage.offline_id)
|
|
4925
|
-
)
|
|
4926
|
-
);
|
|
5327
|
+
const stagesOfComponentType = selectStagesFromComponentType(componentTypeId)(state) ?? [];
|
|
5328
|
+
const attachmentsOfComponentType = selectAttachmentsOfComponentType(componentTypeId)(state);
|
|
4927
5329
|
store.dispatch(deleteComponentType(componentTypeId));
|
|
5330
|
+
if (stagesOfComponentType.length > 0) {
|
|
5331
|
+
const stagesOfComponentTypeIds = stagesOfComponentType.map(
|
|
5332
|
+
(componentTypeStage) => componentTypeStage.offline_id
|
|
5333
|
+
);
|
|
5334
|
+
store.dispatch(removeStages(stagesOfComponentTypeIds));
|
|
5335
|
+
}
|
|
5336
|
+
if (attachmentsOfComponentType.length > 0) {
|
|
5337
|
+
const attachmentsOfComponentTypeIds = attachmentsOfComponentType.map(({ offline_id }) => offline_id);
|
|
5338
|
+
store.dispatch(removeComponentTypeAttachments(attachmentsOfComponentTypeIds));
|
|
5339
|
+
}
|
|
4928
5340
|
return this.enqueueRequest({
|
|
4929
5341
|
description: "Delete ComponentType",
|
|
4930
5342
|
method: HttpMethod.DELETE,
|
|
@@ -4933,7 +5345,8 @@ class ComponentTypeService extends BaseApiService {
|
|
|
4933
5345
|
blocks: []
|
|
4934
5346
|
}).catch((e) => {
|
|
4935
5347
|
store.dispatch(addComponentType(componentType));
|
|
4936
|
-
store.dispatch(addStages(
|
|
5348
|
+
store.dispatch(addStages(stagesOfComponentType));
|
|
5349
|
+
store.dispatch(addComponentTypeAttachments(attachmentsOfComponentType));
|
|
4937
5350
|
throw e;
|
|
4938
5351
|
});
|
|
4939
5352
|
}
|
|
@@ -5111,12 +5524,12 @@ class IssueService extends BaseApiService {
|
|
|
5111
5524
|
if (!backup) {
|
|
5112
5525
|
throw new Error(`No issue with id ${id} found in the store`);
|
|
5113
5526
|
}
|
|
5114
|
-
const attachments = Object.values(state.issueReducer.attachments).filter((a) => a.
|
|
5115
|
-
const attachmentsOfIssue =
|
|
5116
|
-
store.dispatch(removeIssue(id));
|
|
5527
|
+
const attachments = Object.values(state.issueReducer.attachments).filter((a) => a.issue === id);
|
|
5528
|
+
const attachmentsOfIssue = selectAttachmentsOfIssue(id)(state);
|
|
5529
|
+
this.client.store.dispatch(removeIssue(id));
|
|
5117
5530
|
store.dispatch(addActiveProjectIssuesCount(-1));
|
|
5118
|
-
if (attachmentsOfIssue) {
|
|
5119
|
-
store.dispatch(removeAttachmentsOfIssue(id));
|
|
5531
|
+
if (attachmentsOfIssue.length > 0) {
|
|
5532
|
+
this.client.store.dispatch(removeAttachmentsOfIssue(id));
|
|
5120
5533
|
}
|
|
5121
5534
|
try {
|
|
5122
5535
|
return await this.enqueueRequest({
|
|
@@ -5127,8 +5540,8 @@ class IssueService extends BaseApiService {
|
|
|
5127
5540
|
blocks: []
|
|
5128
5541
|
});
|
|
5129
5542
|
} catch (e) {
|
|
5130
|
-
store.dispatch(addIssue(backup));
|
|
5131
|
-
store.dispatch(
|
|
5543
|
+
this.client.store.dispatch(addIssue(backup));
|
|
5544
|
+
this.client.store.dispatch(addIssueAttachments(attachments));
|
|
5132
5545
|
store.dispatch(addActiveProjectIssuesCount(1));
|
|
5133
5546
|
throw e;
|
|
5134
5547
|
}
|
|
@@ -5304,7 +5717,10 @@ class MainService extends BaseApiService {
|
|
|
5304
5717
|
if (currentProjectId) {
|
|
5305
5718
|
const [_offlineAttachments, promise] = this.client.attachments.fetchAll(currentProjectId);
|
|
5306
5719
|
void promise.then((result) => {
|
|
5307
|
-
|
|
5720
|
+
const { issue_attachments, component_type_attachments, component_attachments } = result;
|
|
5721
|
+
store.dispatch(setIssueAttachments(issue_attachments));
|
|
5722
|
+
store.dispatch(setComponentAttachments(component_attachments));
|
|
5723
|
+
store.dispatch(setComponentTypeAttachments(component_type_attachments));
|
|
5308
5724
|
});
|
|
5309
5725
|
}
|
|
5310
5726
|
store.dispatch(setIsFetchingInitialData(false));
|
|
@@ -6282,17 +6698,22 @@ class FileService extends BaseApiService {
|
|
|
6282
6698
|
let promise = cachedRequestPromises[requestCacheKey];
|
|
6283
6699
|
let isFirstRequest = true;
|
|
6284
6700
|
if (!promise) {
|
|
6285
|
-
promise =
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6701
|
+
promise = new Promise((resolve) => {
|
|
6702
|
+
void this.enqueueRequest({
|
|
6703
|
+
description: "Download file",
|
|
6704
|
+
method: HttpMethod.GET,
|
|
6705
|
+
url,
|
|
6706
|
+
// If in development, we should assume the files are saved at localhost by the Django development server.
|
|
6707
|
+
// Setting this to true will lead to localhost:8000 being prepended to the URL.
|
|
6708
|
+
isExternalUrl: true,
|
|
6709
|
+
isResponseBlob: true,
|
|
6710
|
+
isAuthNeeded: false,
|
|
6711
|
+
blockers: [expectedSha1],
|
|
6712
|
+
blocks: [expectedSha1]
|
|
6713
|
+
}).then((blob) => {
|
|
6714
|
+
const blobToFile = new File([blob], downloadedName ?? expectedSha1, { type: blob.type });
|
|
6715
|
+
resolve(blobToFile);
|
|
6716
|
+
});
|
|
6296
6717
|
});
|
|
6297
6718
|
cachedRequestPromises[requestCacheKey] = promise;
|
|
6298
6719
|
} else {
|
|
@@ -6698,8 +7119,8 @@ class BaseFormElement {
|
|
|
6698
7119
|
}
|
|
6699
7120
|
}
|
|
6700
7121
|
const emptyBaseField = {
|
|
6701
|
-
label: "
|
|
6702
|
-
description: "
|
|
7122
|
+
label: "",
|
|
7123
|
+
description: "",
|
|
6703
7124
|
required: false
|
|
6704
7125
|
};
|
|
6705
7126
|
class BaseField extends BaseFormElement {
|
|
@@ -12402,7 +12823,7 @@ const ImageCard = memo((props) => {
|
|
|
12402
12823
|
gap: "0",
|
|
12403
12824
|
...rest,
|
|
12404
12825
|
children: [
|
|
12405
|
-
!file && /* @__PURE__ */ jsx(Flex, { width: "100%", height: "100%", align: "center", justify: "center", position: "absolute", children: /* @__PURE__ */ jsx(Spinner, {}) }),
|
|
12826
|
+
!file && !error2 && /* @__PURE__ */ jsx(Flex, { width: "100%", height: "100%", align: "center", justify: "center", position: "absolute", children: /* @__PURE__ */ jsx(Spinner, {}) }),
|
|
12406
12827
|
/* @__PURE__ */ jsx(Inset, { className: styles$4.ImageInset, ref: imageInsetRef, clip: "padding-box", side: "y", pb: "0", children: file && !error2 && /* @__PURE__ */ jsx("img", { className: styles$4.Image, src: URL.createObjectURL(file), alt: alt ?? file.name }) }),
|
|
12407
12828
|
/* @__PURE__ */ jsx(
|
|
12408
12829
|
OvermapItem,
|
|
@@ -13495,14 +13916,16 @@ const FieldActions = memo((props) => {
|
|
|
13495
13916
|
key: "duplicate",
|
|
13496
13917
|
text: "Duplicate",
|
|
13497
13918
|
buttonProps: { onClick: duplicate }
|
|
13498
|
-
}
|
|
13499
|
-
|
|
13919
|
+
}
|
|
13920
|
+
];
|
|
13921
|
+
if (index2 === 0) {
|
|
13922
|
+
actions2.push({
|
|
13500
13923
|
Icon: TrashIcon,
|
|
13501
13924
|
key: "delete",
|
|
13502
13925
|
text: "Delete",
|
|
13503
13926
|
buttonProps: { onClick: remove2 }
|
|
13504
|
-
}
|
|
13505
|
-
|
|
13927
|
+
});
|
|
13928
|
+
}
|
|
13506
13929
|
if (type !== "section") {
|
|
13507
13930
|
actions2.unshift({
|
|
13508
13931
|
Icon: ImageIcon,
|
|
@@ -14676,15 +15099,19 @@ export {
|
|
|
14676
15099
|
acceptProjectInvite,
|
|
14677
15100
|
addActiveProjectFormSubmissionsCount,
|
|
14678
15101
|
addActiveProjectIssuesCount,
|
|
14679
|
-
addAttachment,
|
|
14680
|
-
addAttachments,
|
|
14681
15102
|
addCategory,
|
|
14682
15103
|
addComponent,
|
|
15104
|
+
addComponentAttachment,
|
|
15105
|
+
addComponentAttachments,
|
|
14683
15106
|
addComponentType,
|
|
15107
|
+
addComponentTypeAttachment,
|
|
15108
|
+
addComponentTypeAttachments,
|
|
14684
15109
|
addComponentsInBatches,
|
|
14685
15110
|
addEmailDomain,
|
|
14686
15111
|
addFavouriteProjectId,
|
|
14687
15112
|
addIssue,
|
|
15113
|
+
addIssueAttachment,
|
|
15114
|
+
addIssueAttachments,
|
|
14688
15115
|
addLicenses,
|
|
14689
15116
|
addOrReplaceCategories,
|
|
14690
15117
|
addOrReplaceIssueComment,
|
|
@@ -14825,14 +15252,18 @@ export {
|
|
|
14825
15252
|
rehydratedReducer,
|
|
14826
15253
|
rehydratedSlice,
|
|
14827
15254
|
removeAllComponentsOfType,
|
|
14828
|
-
removeAttachment,
|
|
14829
15255
|
removeAttachmentsOfIssue,
|
|
14830
15256
|
removeCategory,
|
|
14831
15257
|
removeColor,
|
|
14832
15258
|
removeComponent,
|
|
15259
|
+
removeComponentAttachment,
|
|
15260
|
+
removeComponentAttachments,
|
|
15261
|
+
removeComponentTypeAttachment,
|
|
15262
|
+
removeComponentTypeAttachments,
|
|
14833
15263
|
removeEmailDomain,
|
|
14834
15264
|
removeFavouriteProjectId,
|
|
14835
15265
|
removeIssue,
|
|
15266
|
+
removeIssueAttachment,
|
|
14836
15267
|
removeIssueComment,
|
|
14837
15268
|
removeOrganizationAccess,
|
|
14838
15269
|
removeProjectAccess,
|
|
@@ -14868,7 +15299,15 @@ export {
|
|
|
14868
15299
|
selectActiveWorkspace,
|
|
14869
15300
|
selectActiveWorkspaceId,
|
|
14870
15301
|
selectAllAttachments,
|
|
15302
|
+
selectAllComponentAttachments,
|
|
15303
|
+
selectAllComponentTypeAttachments,
|
|
14871
15304
|
selectAppearance,
|
|
15305
|
+
selectAttachmentsOfComponent,
|
|
15306
|
+
selectAttachmentsOfComponentByType,
|
|
15307
|
+
selectAttachmentsOfComponentType,
|
|
15308
|
+
selectAttachmentsOfComponentTypeByType,
|
|
15309
|
+
selectAttachmentsOfIssue,
|
|
15310
|
+
selectAttachmentsOfIssueByType,
|
|
14872
15311
|
selectCategories,
|
|
14873
15312
|
selectCategoriesOfWorkspace,
|
|
14874
15313
|
selectCategory,
|
|
@@ -14880,7 +15319,9 @@ export {
|
|
|
14880
15319
|
selectCompletedStageIdsForComponent,
|
|
14881
15320
|
selectCompletedStages,
|
|
14882
15321
|
selectComponent,
|
|
15322
|
+
selectComponentAttachmentMapping,
|
|
14883
15323
|
selectComponentType,
|
|
15324
|
+
selectComponentTypeAttachmentMapping,
|
|
14884
15325
|
selectComponentTypeForm,
|
|
14885
15326
|
selectComponentTypeFromComponent,
|
|
14886
15327
|
selectComponentTypeFromComponents,
|
|
@@ -14992,9 +15433,10 @@ export {
|
|
|
14992
15433
|
setActiveProjectId,
|
|
14993
15434
|
setActiveWorkspaceId,
|
|
14994
15435
|
setAppearance,
|
|
14995
|
-
setAttachments,
|
|
14996
15436
|
setCategories,
|
|
14997
15437
|
setCenterMapToProject,
|
|
15438
|
+
setComponentAttachments,
|
|
15439
|
+
setComponentTypeAttachments,
|
|
14998
15440
|
setComponentTypes,
|
|
14999
15441
|
setComponents,
|
|
15000
15442
|
setCreateProjectType,
|
|
@@ -15006,6 +15448,7 @@ export {
|
|
|
15006
15448
|
setIsFetchingInitialData,
|
|
15007
15449
|
setIsImportingProjectFile,
|
|
15008
15450
|
setIsLoading,
|
|
15451
|
+
setIssueAttachments,
|
|
15009
15452
|
setIssueComments,
|
|
15010
15453
|
setIssues,
|
|
15011
15454
|
setLicenses,
|
|
@@ -15045,9 +15488,11 @@ export {
|
|
|
15045
15488
|
unhideCategory,
|
|
15046
15489
|
unlinkStageToForm,
|
|
15047
15490
|
updateActiveOrganization,
|
|
15048
|
-
updateAttachment,
|
|
15049
15491
|
updateComponent,
|
|
15492
|
+
updateComponentAttachment,
|
|
15493
|
+
updateComponentTypeAttachment,
|
|
15050
15494
|
updateIssue,
|
|
15495
|
+
updateIssueAttachment,
|
|
15051
15496
|
updateLicense,
|
|
15052
15497
|
updateOrCreateProject,
|
|
15053
15498
|
updateOrCreateUserFormSubmission,
|