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