@overmap-ai/core 1.0.71-project-file-improvements.4 → 1.0.71-workspace-settings.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.
@@ -1138,9 +1138,9 @@ const selectAssetTypeStagesMapping = restructureCreateSelectorWithArgs(
1138
1138
  );
1139
1139
  const selectStagesOfAssetType = restructureCreateSelectorWithArgs(
1140
1140
  createSelector([selectAssetStages, (_state, assetTypeId) => assetTypeId], (stages, assetTypeId) => {
1141
- return fallbackToEmptyArray(
1142
- stages.filter((stage) => stage.asset_type === assetTypeId).sort((a, b) => a.priority - b.priority)
1143
- );
1141
+ const stagesOfAssetType = stages.filter((stage) => stage.asset_type === assetTypeId);
1142
+ const sortedStages = stagesOfAssetType.sort((a, b) => a.priority - b.priority);
1143
+ return fallbackToEmptyArray(sortedStages);
1144
1144
  })
1145
1145
  );
1146
1146
  const selectAssetStagesByIds = restructureCreateSelectorWithArgs(
@@ -1857,36 +1857,86 @@ const selectDeletedRequests = (state) => state.outboxReducer.deletedRequests;
1857
1857
  const selectLatestRetryTime = (state) => state.outboxReducer.latestRetryTime;
1858
1858
  const { enqueueRequest, markForDeletion, markAsDeleted, _setLatestRetryTime } = outboxSlice.actions;
1859
1859
  const outboxReducer = outboxSlice.reducer;
1860
- const projectFileAdapter = createModelAdapter((file) => file.offline_id);
1861
- const initialState$j = projectFileAdapter.getInitialState({});
1860
+ const initialState$j = {
1861
+ projectFiles: {},
1862
+ activeProjectFileId: null,
1863
+ isImportingProjectFile: false
1864
+ };
1862
1865
  const projectFileSlice = createSlice({
1863
1866
  name: "projectFiles",
1864
1867
  initialState: initialState$j,
1865
1868
  extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$j)),
1866
1869
  reducers: {
1867
- initializeProjectFiles: projectFileAdapter.initialize,
1868
- addProjectFile: projectFileAdapter.addOne,
1869
- addProjectFiles: projectFileAdapter.addMany,
1870
- setProjectFile: projectFileAdapter.setOne,
1871
- setProjectFiles: projectFileAdapter.setMany,
1872
- updateProjectFile: projectFileAdapter.updateOne,
1873
- updateProjectFiles: projectFileAdapter.updateMany,
1874
- deleteProjectFile: projectFileAdapter.deleteOne,
1875
- deleteProjectFiles: projectFileAdapter.deleteMany
1870
+ addOrReplaceProjectFiles: (state, action) => {
1871
+ for (let fileObj of action.payload) {
1872
+ let file = fileObj.file;
1873
+ if (file.includes("+")) {
1874
+ console.warn("Attempting to apply fix for image URL with '+' character:", file);
1875
+ const parts = file.split("/");
1876
+ if (parts.length < 2) {
1877
+ throw new Error("Invalid URL: " + file);
1878
+ }
1879
+ const lastPart = encodeURIComponent(parts[parts.length - 1]);
1880
+ file = parts.slice(0, -1).join("/") + "/" + lastPart;
1881
+ console.warn("Fixed URL:", file);
1882
+ fileObj = { ...fileObj, file };
1883
+ }
1884
+ state.projectFiles[fileObj.offline_id] = fileObj;
1885
+ }
1886
+ },
1887
+ addOrReplaceProjectFile: (state, action) => {
1888
+ if (!action.payload.project) {
1889
+ throw new Error("ProjectFile has no project. A project must be set before storing.");
1890
+ }
1891
+ state.projectFiles[action.payload.offline_id] = action.payload;
1892
+ },
1893
+ setIsImportingProjectFile: (state, action) => {
1894
+ state.isImportingProjectFile = action.payload;
1895
+ },
1896
+ saveActiveProjectFileBounds: (state, action) => {
1897
+ const activeProjectFileId = state.activeProjectFileId;
1898
+ if (!activeProjectFileId) {
1899
+ throw new Error("Tried to save bounds for active project file, but no active project file was set.");
1900
+ }
1901
+ if (!state.projectFiles[activeProjectFileId]) {
1902
+ throw new Error(
1903
+ `Tried to save bounds for active project file, but project file with ID ${activeProjectFileId}
1904
+ doesn't exist.`
1905
+ );
1906
+ }
1907
+ state.projectFiles[activeProjectFileId].bounds = action.payload;
1908
+ },
1909
+ // TODO: Move to MapContext. Should not be persisted.
1910
+ setActiveProjectFileId: (state, action) => {
1911
+ state.activeProjectFileId = action.payload;
1912
+ },
1913
+ removeProjectFile: (state, action) => {
1914
+ delete state.projectFiles[action.payload];
1915
+ },
1916
+ removeProjectFilesOfProject: (state, action) => {
1917
+ const filesToDelete = Object.values(state.projectFiles).filter((file) => file.project === action.payload);
1918
+ for (const file of filesToDelete) {
1919
+ delete state.projectFiles[file.offline_id];
1920
+ }
1921
+ },
1922
+ resetProjectFileObjectUrls: (state, ..._args) => {
1923
+ for (const key in state.projectFiles) {
1924
+ delete state.projectFiles[key].objectURL;
1925
+ }
1926
+ }
1876
1927
  }
1877
1928
  });
1878
1929
  const {
1879
- initializeProjectFiles,
1880
- addProjectFile,
1881
- addProjectFiles,
1882
- setProjectFile,
1883
- setProjectFiles,
1884
- updateProjectFile,
1885
- updateProjectFiles,
1886
- deleteProjectFile,
1887
- deleteProjectFiles
1930
+ addOrReplaceProjectFiles,
1931
+ addOrReplaceProjectFile,
1932
+ setIsImportingProjectFile,
1933
+ setActiveProjectFileId,
1934
+ saveActiveProjectFileBounds,
1935
+ removeProjectFile,
1936
+ removeProjectFilesOfProject,
1937
+ resetProjectFileObjectUrls
1888
1938
  } = projectFileSlice.actions;
1889
- const selectProjectFileMapping = (state) => state.projectFileReducer.instances;
1939
+ const selectProjectFileMapping = (state) => state.projectFileReducer.projectFiles;
1890
1940
  const selectProjectFiles = createSelector(
1891
1941
  [selectProjectFileMapping, selectActiveProjectId],
1892
1942
  (mapping, activeProjectId) => {
@@ -1895,8 +1945,10 @@ const selectProjectFiles = createSelector(
1895
1945
  );
1896
1946
  }
1897
1947
  );
1948
+ const selectActiveProjectFileId = (state) => state.projectFileReducer.activeProjectFileId;
1949
+ const selectIsImportingProjectFile = (state) => state.projectFileReducer.isImportingProjectFile;
1898
1950
  const selectProjectFileById = (id) => (state) => {
1899
- return state.projectFileReducer.instances[id];
1951
+ return state.projectFileReducer.projectFiles[id];
1900
1952
  };
1901
1953
  const projectFileReducer = projectFileSlice.reducer;
1902
1954
  const projectAttachmentAdapter = createModelAdapter((attachment) => attachment.offline_id);
@@ -5058,98 +5110,93 @@ class ProjectAccessService extends BaseApiService {
5058
5110
  this.dispatch(initializeProjectAccesses(result));
5059
5111
  }
5060
5112
  }
5061
- class ProjectFileService extends BaseUploadService {
5062
- async add(payload) {
5063
- var _a2;
5064
- const { store } = this.client;
5065
- const createdBy = (_a2 = store.getState().userReducer.currentUser) == null ? void 0 : _a2.id;
5066
- const { file, ...payloadWithoutFile } = payload;
5067
- const sha1 = await hashFile(file);
5068
- const filePayload = {
5069
- sha1,
5070
- file_type: file.type,
5071
- extension: file.name.split(".").pop(),
5072
- size: file.size
5073
- };
5074
- const offlineProjectFile = offline({
5075
- ...payloadWithoutFile,
5076
- submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
5077
- created_by: createdBy,
5078
- file_name: file.name,
5079
- file_sha1: sha1,
5080
- file: URL.createObjectURL(file)
5081
- });
5082
- this.dispatch(addProjectFile(offlineProjectFile));
5113
+ class ProjectFileService extends BaseApiService {
5114
+ async saveExisting(file) {
5115
+ if (!file.offline_id) {
5116
+ throw new Error(
5117
+ "You can only use this method to save existing project files. The one provided has no offline_id."
5118
+ );
5119
+ }
5120
+ const editableData = { ...file };
5121
+ delete editableData.file;
5083
5122
  const promise = this.enqueueRequest({
5084
- description: "Add project file",
5085
- method: HttpMethod.POST,
5086
- url: `/projects/${payload.project}/files/`,
5087
- payload: {
5088
- offline_id: offlineProjectFile.offline_id,
5089
- submitted_at: offlineProjectFile.submitted_at,
5090
- z_index: offlineProjectFile.z_index,
5091
- canvas_bounds: offlineProjectFile.canvas_bounds,
5092
- bounds: offlineProjectFile.bounds,
5093
- file_name: offlineProjectFile.file_name,
5094
- file_sha1: offlineProjectFile.file_sha1,
5095
- file: offlineProjectFile.file,
5096
- file_payload: filePayload
5097
- },
5098
- blockers: [],
5099
- blocks: [offlineProjectFile.offline_id]
5123
+ method: HttpMethod.PATCH,
5124
+ url: `/projects/files/${file.offline_id}/`,
5125
+ payload: editableData,
5126
+ blockers: [file.offline_id],
5127
+ blocks: [file.offline_id]
5100
5128
  });
5101
- promise.then((result) => {
5102
- this.processPresignedUrls(result.presigned_urls);
5103
- this.dispatch(updateProjectFile(result.project_file));
5104
- }).catch(() => {
5105
- this.dispatch(deleteProjectFile(offlineProjectFile.offline_id));
5129
+ void promise.then((result) => {
5130
+ this.dispatch(addOrReplaceProjectFile(result));
5106
5131
  });
5107
- return [offlineProjectFile, promise.then((result) => result.project_file)];
5132
+ return promise;
5108
5133
  }
5109
- update(payload) {
5134
+ // TODO: This needs to be seperated into a update and create method
5135
+ saveActive() {
5110
5136
  const { store } = this.client;
5111
- const projectFile = selectProjectFileById(payload.offline_id)(store.getState());
5112
- if (!projectFile) {
5113
- throw new Error(`Project file with id ${payload.offline_id} not found`);
5137
+ const state = store.getState();
5138
+ const activeProjectFileId = state.projectFileReducer.activeProjectFileId;
5139
+ const activeProjectId = state.projectReducer.activeProjectId;
5140
+ if (!activeProjectFileId) {
5141
+ throw new Error("No active project file");
5142
+ }
5143
+ if (!activeProjectId) {
5144
+ throw new Error("No active project");
5145
+ }
5146
+ const activeProjectFile = state.projectFileReducer.projectFiles[activeProjectFileId];
5147
+ if (!activeProjectFile) {
5148
+ throw new Error("No active project file");
5149
+ }
5150
+ if (!activeProjectFile.bounds && !activeProjectFile.canvas_bounds) {
5151
+ throw new Error("Project file must either have bounds or canvas_bounds set");
5152
+ }
5153
+ let requestDetails;
5154
+ const existing = typeof activeProjectFile.file === "string" && !activeProjectFile.file.startsWith("blob:");
5155
+ if (existing) {
5156
+ const editableData = { ...activeProjectFile };
5157
+ delete editableData.file;
5158
+ requestDetails = {
5159
+ method: HttpMethod.PATCH,
5160
+ url: `/projects/files/${activeProjectFileId}/`,
5161
+ payload: editableData,
5162
+ blockers: [activeProjectFileId],
5163
+ blocks: [activeProjectFileId]
5164
+ };
5165
+ } else {
5166
+ requestDetails = new Promise((resolve, reject) => {
5167
+ this.client.files.uploadFileToS3(activeProjectFile.file_sha1).then(([fileProps]) => {
5168
+ resolve({
5169
+ method: HttpMethod.POST,
5170
+ url: `/projects/${activeProjectId}/files/`,
5171
+ payload: {
5172
+ ...activeProjectFile,
5173
+ ...fileProps
5174
+ },
5175
+ blockers: [activeProjectFileId],
5176
+ blocks: [activeProjectFileId]
5177
+ });
5178
+ }).catch(reject);
5179
+ });
5114
5180
  }
5115
- const updatedProjectFile = {
5116
- ...projectFile,
5117
- ...payload
5118
- };
5119
- this.dispatch(updateProjectFile(updatedProjectFile));
5120
- const promise = this.enqueueRequest({
5121
- description: "Update project file",
5122
- method: HttpMethod.PATCH,
5123
- url: `/projects/files/${payload.offline_id}/`,
5124
- payload,
5125
- blockers: [payload.offline_id],
5126
- blocks: [payload.offline_id]
5181
+ const promise = Promise.resolve(requestDetails).then((requestDetails2) => {
5182
+ return this.enqueueRequest(requestDetails2);
5127
5183
  });
5128
- promise.then((result) => {
5129
- this.dispatch(updateProjectFile(result));
5130
- }).catch(() => {
5131
- this.dispatch(updateProjectFile(projectFile));
5184
+ void promise.then((result) => {
5185
+ this.dispatch(addOrReplaceProjectFile(result));
5132
5186
  });
5133
- return [updatedProjectFile, promise];
5187
+ this.dispatch(saveActiveProjectFileBounds);
5188
+ this.dispatch(setActiveProjectFileId(null));
5189
+ this.dispatch(setIsImportingProjectFile(false));
5190
+ return [activeProjectFile, promise];
5134
5191
  }
5135
- delete(id) {
5136
- const { store } = this.client;
5137
- const projectFileToDelete = selectProjectFileById(id)(store.getState());
5138
- if (!projectFileToDelete) {
5139
- throw new Error(`Project file with id ${id} not found`);
5140
- }
5141
- this.dispatch(deleteProjectFile(id));
5142
- const promise = this.enqueueRequest({
5143
- description: "Delete project file",
5192
+ delete(projectFileId) {
5193
+ this.dispatch(removeProjectFile(projectFileId));
5194
+ return this.enqueueRequest({
5144
5195
  method: HttpMethod.DELETE,
5145
- url: `/projects/files/${id}/`,
5146
- blockers: [id],
5196
+ url: `/projects/files/${projectFileId}`,
5197
+ blockers: [projectFileId],
5147
5198
  blocks: []
5148
5199
  });
5149
- promise.catch(() => {
5150
- this.dispatch(updateProjectFile(projectFileToDelete));
5151
- });
5152
- return promise;
5153
5200
  }
5154
5201
  async refreshStore(projectId) {
5155
5202
  const result = await this.enqueueRequest({
@@ -5159,7 +5206,8 @@ class ProjectFileService extends BaseUploadService {
5159
5206
  blockers: [],
5160
5207
  blocks: []
5161
5208
  });
5162
- this.dispatch(initializeProjectFiles(result));
5209
+ this.dispatch(addOrReplaceProjectFiles([]));
5210
+ this.dispatch(addOrReplaceProjectFiles(result));
5163
5211
  }
5164
5212
  }
5165
5213
  class ProjectAttachmentService extends BaseAttachmentService {
@@ -5236,8 +5284,8 @@ class ProjectService extends BaseApiService {
5236
5284
  if (!project) {
5237
5285
  throw new Error("Expected project to exist");
5238
5286
  }
5239
- const projectFiles = selectProjectFiles(state).filter((file) => file.project === projectId);
5240
- this.dispatch(deleteProjectFiles(projectFiles.map(({ offline_id }) => offline_id)));
5287
+ const filesToDelete = selectProjectFiles(state).filter((file) => file.project === projectId);
5288
+ this.dispatch(removeProjectFilesOfProject(project.id));
5241
5289
  const attachmentsOfProject = selectAttachmentsOfProject(project.id)(state);
5242
5290
  this.dispatch(deleteProjectAttachments(attachmentsOfProject.map(({ offline_id }) => offline_id)));
5243
5291
  const projectAccesses = selectProjectAccesses(state);
@@ -5260,7 +5308,7 @@ class ProjectService extends BaseApiService {
5260
5308
  } catch (e) {
5261
5309
  this.dispatch(setProjects(Object.values(projects)));
5262
5310
  this.dispatch(initializeProjectAccesses(Object.values(projectAccesses)));
5263
- this.dispatch(initializeProjectFiles(projectFiles));
5311
+ this.dispatch(addOrReplaceProjectFiles(filesToDelete));
5264
5312
  this.dispatch(setProjectAttachments(attachmentsOfProject));
5265
5313
  this.dispatch({ type: "rehydrated/setRehydrated", payload: true });
5266
5314
  if (license) {
@@ -7393,11 +7441,11 @@ export {
7393
7441
  addIssueUpdates,
7394
7442
  addIssues,
7395
7443
  addLicenses,
7444
+ addOrReplaceProjectFile,
7445
+ addOrReplaceProjectFiles,
7396
7446
  addOrReplaceProjects,
7397
7447
  addProjectAttachment,
7398
7448
  addProjectAttachments,
7399
- addProjectFile,
7400
- addProjectFiles,
7401
7449
  addTeam,
7402
7450
  addUsers,
7403
7451
  addWorkspace,
@@ -7475,8 +7523,6 @@ export {
7475
7523
  deleteProjectAccesses,
7476
7524
  deleteProjectAttachment,
7477
7525
  deleteProjectAttachments,
7478
- deleteProjectFile,
7479
- deleteProjectFiles,
7480
7526
  deleteTeam,
7481
7527
  deleteWorkspace,
7482
7528
  dequeue,
@@ -7545,7 +7591,6 @@ export {
7545
7591
  initializeOrganizationAccesses,
7546
7592
  initializeProjectAccesses,
7547
7593
  initializeProjectAttachments,
7548
- initializeProjectFiles,
7549
7594
  initializeTeams,
7550
7595
  initializeWorkspaces,
7551
7596
  isToday,
@@ -7598,13 +7643,18 @@ export {
7598
7643
  rehydratedSlice,
7599
7644
  removeDocuments,
7600
7645
  removeIssueType,
7646
+ removeProjectFile,
7647
+ removeProjectFilesOfProject,
7601
7648
  removeUser,
7649
+ resetProjectFileObjectUrls,
7602
7650
  resetStore,
7603
7651
  restructureCreateSelectorWithArgs,
7652
+ saveActiveProjectFileBounds,
7604
7653
  selectAccessToken,
7605
7654
  selectActiveOrganizationAccess,
7606
7655
  selectActiveProject,
7607
7656
  selectActiveProjectAccess,
7657
+ selectActiveProjectFileId,
7608
7658
  selectActiveProjectId,
7609
7659
  selectActiveStatusLicenses,
7610
7660
  selectAllDocumentAttachments,
@@ -7696,6 +7746,7 @@ export {
7696
7746
  selectGeoImageMapping,
7697
7747
  selectGeoImages,
7698
7748
  selectGeoImagesOfProject,
7749
+ selectIsImportingProjectFile,
7699
7750
  selectIsLoggedIn,
7700
7751
  selectIssueAssociationById,
7701
7752
  selectIssueAssociationMapping,
@@ -7778,6 +7829,7 @@ export {
7778
7829
  selectWorkspaceById,
7779
7830
  selectWorkspaceMapping,
7780
7831
  selectWorkspaces,
7832
+ setActiveProjectFileId,
7781
7833
  setActiveProjectId,
7782
7834
  setAsset,
7783
7835
  setAssetAttachment,
@@ -7804,6 +7856,7 @@ export {
7804
7856
  setFormSubmissions,
7805
7857
  setGeoImage,
7806
7858
  setGeoImages,
7859
+ setIsImportingProjectFile,
7807
7860
  setIssueAssociation,
7808
7861
  setIssueAssociations,
7809
7862
  setIssueAttachment,
@@ -7817,8 +7870,6 @@ export {
7817
7870
  setProfilePicture,
7818
7871
  setProjectAttachment,
7819
7872
  setProjectAttachments,
7820
- setProjectFile,
7821
- setProjectFiles,
7822
7873
  setProjects,
7823
7874
  setRehydrated,
7824
7875
  setTeam,
@@ -7873,8 +7924,6 @@ export {
7873
7924
  updateProjectAccess,
7874
7925
  updateProjectAttachment,
7875
7926
  updateProjectAttachments,
7876
- updateProjectFile,
7877
- updateProjectFiles,
7878
7927
  updateTeam,
7879
7928
  updateWorkspace,
7880
7929
  userReducer,