synapse-react-client 4.0.9 → 4.0.10

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.
Files changed (543) hide show
  1. package/dist/SWC.index.d.ts +1 -0
  2. package/dist/SWC.index.d.ts.map +1 -1
  3. package/dist/SWC.index.js +2 -1
  4. package/dist/SWC.index.js.map +1 -1
  5. package/dist/aridhia-queries/aridhiaTokenExchange.js.map +1 -1
  6. package/dist/aridhia-queries/useGetAridhiaRequests.js.map +1 -1
  7. package/dist/assets/icons/TasksIcon.d.ts.map +1 -1
  8. package/dist/assets/icons/TasksIcon.js +6 -10
  9. package/dist/assets/icons/TasksIcon.js.map +1 -1
  10. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.d.ts.map +1 -1
  11. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js +69 -63
  12. package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js.map +1 -1
  13. package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js.map +1 -1
  14. package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
  15. package/dist/components/AccessRequirementList/AccessRequirementListUtils.js.map +1 -1
  16. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsEditor.js.map +1 -1
  17. package/dist/components/AccessRequirementList/RequirementItem/SelfSignAccessRequirementItem.js.map +1 -1
  18. package/dist/components/AccessRequirementRelatedProjectsList/AccessRequirementRelatedProjectsList.js.map +1 -1
  19. package/dist/components/AccessTokenPage/AccessTokenCard/AccessTokenCard.js.map +1 -1
  20. package/dist/components/AcknowledgementsPage/StudyAcknowledgements.js.map +1 -1
  21. package/dist/components/AclEditor/PermissionLevelMenu.js.map +1 -1
  22. package/dist/components/AclEditor/ResourceAccessAndUserGroupHeader.js.map +1 -1
  23. package/dist/components/AclEditor/useSortResourceAccessList.js.map +1 -1
  24. package/dist/components/AclEditor/useUpdateAcl.js.map +1 -1
  25. package/dist/components/Aridhia/AridhiaAccessStatus.js.map +1 -1
  26. package/dist/components/Authentication/AuthenticationMethodSelection.d.ts.map +1 -1
  27. package/dist/components/Authentication/AuthenticationMethodSelection.js +38 -37
  28. package/dist/components/Authentication/AuthenticationMethodSelection.js.map +1 -1
  29. package/dist/components/Authentication/Constants.d.ts +1 -0
  30. package/dist/components/Authentication/Constants.d.ts.map +1 -1
  31. package/dist/components/Authentication/Constants.js +2 -2
  32. package/dist/components/Authentication/Constants.js.map +1 -1
  33. package/dist/components/Authentication/LastLoginInfo.js.map +1 -1
  34. package/dist/components/Authentication/RecoveryCodeForm.js.map +1 -1
  35. package/dist/components/Authentication/RecoveryCodeGrid.js.map +1 -1
  36. package/dist/components/Authentication/RegenerateBackupCodesWarning.js.map +1 -1
  37. package/dist/components/Authentication/Reset2FAWarning.js.map +1 -1
  38. package/dist/components/Authentication/StandaloneLoginForm.js +1 -1
  39. package/dist/components/Authentication/TwoFactorBackupCodes.js.map +1 -1
  40. package/dist/components/Authentication/TwoFactorEnrollmentForm.d.ts.map +1 -1
  41. package/dist/components/Authentication/TwoFactorEnrollmentForm.js +2 -1
  42. package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
  43. package/dist/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.js.map +1 -1
  44. package/dist/components/CardContainer/CardContainer.js.map +1 -1
  45. package/dist/components/CardDeck/CardDeck.Mobile.js.map +1 -1
  46. package/dist/components/CardDeck/TableQueryCardDeck.js.map +1 -1
  47. package/dist/components/CertificationQuiz/CertificationQuiz.js.map +1 -1
  48. package/dist/components/ChallengeDataDownload/ChallengeDataDownload.js.map +1 -1
  49. package/dist/components/ChallengeSubmission/ChallengeSubmission.js.map +1 -1
  50. package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js.map +1 -1
  51. package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js.map +1 -1
  52. package/dist/components/ChallengeSubmission/EvaluationQueueList.js.map +1 -1
  53. package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
  54. package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js.map +1 -1
  55. package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
  56. package/dist/components/ChangePassword/ChangePassword.js.map +1 -1
  57. package/dist/components/ChangePassword/ChangePasswordWithToken.js.map +1 -1
  58. package/dist/components/ChangePassword/useChangePasswordFormState.js +1 -1
  59. package/dist/components/ChangePassword/useChangePasswordFormState.js.map +1 -1
  60. package/dist/components/CitationPopover/CitationPopoverContent.js.map +1 -1
  61. package/dist/components/ColumnFilter/ColumnFilter.js.map +1 -1
  62. package/dist/components/ComponentCollapse.js.map +1 -1
  63. package/dist/components/CookiesNotification/CookiesNotification.js.map +1 -1
  64. package/dist/components/CreateProjectModal/CreateProjectModal.js.map +1 -1
  65. package/dist/components/CreateTableViewWizard/CreateTableViewWizardUtils.js.map +1 -1
  66. package/dist/components/DataGrid/DataGrid.d.ts +0 -1
  67. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
  68. package/dist/components/DataGrid/DataGrid.js +72 -72
  69. package/dist/components/DataGrid/DataGrid.js.map +1 -1
  70. package/dist/components/DataGrid/DataGridWebSocket.d.ts +4 -0
  71. package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
  72. package/dist/components/DataGrid/DataGridWebSocket.js +9 -8
  73. package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
  74. package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
  75. package/dist/components/DataGrid/SynapseGrid.js +326 -268
  76. package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
  77. package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts +2 -0
  78. package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts.map +1 -1
  79. package/dist/components/DataGrid/columns/AutocompleteColumn.js +113 -67
  80. package/dist/components/DataGrid/columns/AutocompleteColumn.js.map +1 -1
  81. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts +2 -1
  82. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts.map +1 -1
  83. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js +126 -122
  84. package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js.map +1 -1
  85. package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts +58 -0
  86. package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts.map +1 -0
  87. package/dist/components/DataGrid/columns/useGridAutocompleteState.js +52 -0
  88. package/dist/components/DataGrid/columns/useGridAutocompleteState.js.map +1 -0
  89. package/dist/components/DataGrid/components/ValidationAlert.d.ts +5 -2
  90. package/dist/components/DataGrid/components/ValidationAlert.d.ts.map +1 -1
  91. package/dist/components/DataGrid/components/ValidationAlert.js +429 -24
  92. package/dist/components/DataGrid/components/ValidationAlert.js.map +1 -1
  93. package/dist/components/DataGrid/hooks/useColumnResizeHandles.js.map +1 -1
  94. package/dist/components/DataGrid/hooks/useGetSchemaForGrid.js.map +1 -1
  95. package/dist/components/DataGrid/hooks/useGridUndoRedo.js.map +1 -1
  96. package/dist/components/DataGrid/hooks/useStack.js.map +1 -1
  97. package/dist/components/DataGrid/useCRDTModelView.js.map +1 -1
  98. package/dist/components/DataGrid/useDataGridWebsocket.d.ts +7 -0
  99. package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
  100. package/dist/components/DataGrid/useDataGridWebsocket.js +16 -2
  101. package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
  102. package/dist/components/DataGrid/useInitializeGridConnection.js.map +1 -1
  103. package/dist/components/DataGrid/useMergeGridWithRecordSet.js.map +1 -1
  104. package/dist/components/DataGrid/useMergeGridWithSource.js.map +1 -1
  105. package/dist/components/DataGrid/useMergeGridWithTable.js.map +1 -1
  106. package/dist/components/DataGrid/utils/DataGridUtils.js.map +1 -1
  107. package/dist/components/DataGrid/utils/applyModelChange.js.map +1 -1
  108. package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
  109. package/dist/components/DataGrid/utils/computeReplicaSelectionModel.js.map +1 -1
  110. package/dist/components/DataGrid/utils/extractColumnValidationMessages.js.map +1 -1
  111. package/dist/components/DataGrid/utils/getCellClassName.d.ts.map +1 -1
  112. package/dist/components/DataGrid/utils/getCellClassName.js +8 -8
  113. package/dist/components/DataGrid/utils/getCellClassName.js.map +1 -1
  114. package/dist/components/DataGrid/utils/json-rx/JsonRx.js.map +1 -1
  115. package/dist/components/DataGrid/utils/modelRowsToGrid.js.map +1 -1
  116. package/dist/components/DataGrid/utils/parseFreeTextUsingJsonSchemaType.js.map +1 -1
  117. package/dist/components/DataGrid/utils/splitPatch.js.map +1 -1
  118. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  119. package/dist/components/DirectDownload/DirectDownload.js.map +1 -1
  120. package/dist/components/DirectDownloadButton.js.map +1 -1
  121. package/dist/components/DownloadCart/CreatePackageV2.js.map +1 -1
  122. package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map +1 -1
  123. package/dist/components/DownloadCart/DownloadListActionsRequired.js.map +1 -1
  124. package/dist/components/DownloadCart/DownloadListTable.js.map +1 -1
  125. package/dist/components/DownloadCart/fileNameUtils.js.map +1 -1
  126. package/dist/components/DraggableDialog/DraggableDialog.js.map +1 -1
  127. package/dist/components/DynamicForm/DynamicFormModal.js.map +1 -1
  128. package/dist/components/Ecosystem/TableQueryEcosystem.js.map +1 -1
  129. package/dist/components/EntityAclEditor/EntityAclEditor.d.ts.map +1 -1
  130. package/dist/components/EntityAclEditor/EntityAclEditor.js +103 -103
  131. package/dist/components/EntityAclEditor/EntityAclEditor.js.map +1 -1
  132. package/dist/components/EntityAclEditor/useNotifyNewACLUsers.js.map +1 -1
  133. package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
  134. package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
  135. package/dist/components/EntityDownloadButton/EntityDownloadButton.js.map +1 -1
  136. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.d.ts.map +1 -1
  137. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js +36 -30
  138. package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js.map +1 -1
  139. package/dist/components/EntityFinder/EntityFinder.js.map +1 -1
  140. package/dist/components/EntityFinder/VersionSelectionType.js.map +1 -1
  141. package/dist/components/EntityFinder/details/configurations/EntityChildrenDetails.js.map +1 -1
  142. package/dist/components/EntityFinder/details/configurations/FavoritesDetails.js.map +1 -1
  143. package/dist/components/EntityFinder/details/configurations/ProjectListDetails.js.map +1 -1
  144. package/dist/components/EntityFinder/details/view/DetailsView.js.map +1 -1
  145. package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
  146. package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
  147. package/dist/components/EntityFinder/useEntitySelection.js.map +1 -1
  148. package/dist/components/EntityForm/EntityForm.js.map +1 -1
  149. package/dist/components/EntityHeaderTable/EntityHeaderTable.js.map +1 -1
  150. package/dist/components/EntityHeaderTable/Filter.js.map +1 -1
  151. package/dist/components/EntityHeaderTable/useEntityHeaderTableState.js.map +1 -1
  152. package/dist/components/EntitySubjectsSelector/EntitySubjectsSelector.js.map +1 -1
  153. package/dist/components/EntityTreeTable/components/IdColumnHeader.js.map +1 -1
  154. package/dist/components/EntityTreeTable/hooks/useEntityTreeState.js.map +1 -1
  155. package/dist/components/EntityTreeTable/hooks/useTableColumns.js.map +1 -1
  156. package/dist/components/EntityTreeTable/hooks/useTableData.js.map +1 -1
  157. package/dist/components/EntityTreeTable/hooks/useTreeOperationsWithDirectFetch.js.map +1 -1
  158. package/dist/components/EntityUpload/EntityUpload.js.map +1 -1
  159. package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
  160. package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js.map +1 -1
  161. package/dist/components/FeaturedDataTabs/FacetPlotsCard.js.map +1 -1
  162. package/dist/components/FeaturedDataTabs/QueryPerFacetPlotsCard.js.map +1 -1
  163. package/dist/components/FeaturedDataTabs/SingleQueryFacetPlotsCards.js.map +1 -1
  164. package/dist/components/FeaturedResearch/FeaturedResearch.js.map +1 -1
  165. package/dist/components/FeaturedToolsList/FeaturedToolsList.js.map +1 -1
  166. package/dist/components/FilePreview/FileHandleContentRenderer.js.map +1 -1
  167. package/dist/components/FilePreview/HtmlPreview/HtmlPreview.js.map +1 -1
  168. package/dist/components/FilePreview/PreviewRendererType.js.map +1 -1
  169. package/dist/components/Forum/DiscussionReply.js.map +1 -1
  170. package/dist/components/Forum/DiscussionSearchResult.js.map +1 -1
  171. package/dist/components/Forum/ForumTable.js.map +1 -1
  172. package/dist/components/Forum/ForumThreadEditor.js.map +1 -1
  173. package/dist/components/FullTextSearch/FullTextSearchUtils.js.map +1 -1
  174. package/dist/components/GenericCard/GenericCard.d.ts.map +1 -1
  175. package/dist/components/GenericCard/GenericCard.js +12 -7
  176. package/dist/components/GenericCard/GenericCard.js.map +1 -1
  177. package/dist/components/GenericCard/Linkify.js.map +1 -1
  178. package/dist/components/GenericCard/SynapseCardLabel.js.map +1 -1
  179. package/dist/components/GenericCard/TableRowGenericCard.js +105 -105
  180. package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
  181. package/dist/components/Goals/Goals.Mobile.js.map +1 -1
  182. package/dist/components/Goals/Goals.js.map +1 -1
  183. package/dist/components/GoalsV2/GoalsV2.Mobile.js.map +1 -1
  184. package/dist/components/GoalsV2/GoalsV2.js.map +1 -1
  185. package/dist/components/GoalsV3/GoalsV3.Mobile.js.map +1 -1
  186. package/dist/components/GoalsV3/GoalsV3.js.map +1 -1
  187. package/dist/components/GoogleMap/SynapseUserMarker.js.map +1 -1
  188. package/dist/components/HasAccess/AccessIcon.js.map +1 -1
  189. package/dist/components/HasAccess/useHasAccess.js.map +1 -1
  190. package/dist/components/HeaderCard/HeaderCardV2.js.map +1 -1
  191. package/dist/components/HeaderCard.d.ts +6 -1
  192. package/dist/components/HeaderCard.d.ts.map +1 -1
  193. package/dist/components/HeaderCard.js +107 -76
  194. package/dist/components/HeaderCard.js.map +1 -1
  195. package/dist/components/HexGrid/HexGrid.js.map +1 -1
  196. package/dist/components/IconList.js.map +1 -1
  197. package/dist/components/ImageCardGridWithLinks/ImageCardGridWithLinks.js.map +1 -1
  198. package/dist/components/ImageFromSynapseTable.js.map +1 -1
  199. package/dist/components/JSONArrayEditor/useParseCsv.js.map +1 -1
  200. package/dist/components/JsonSchemaForm/templates/ArrayFieldDescriptionTemplate.js.map +1 -1
  201. package/dist/components/JsonSchemaForm/templates/ArrayFieldItemTemplate.js.map +1 -1
  202. package/dist/components/JsonSchemaForm/templates/BaseInputTemplate.js.map +1 -1
  203. package/dist/components/JsonSchemaForm/templates/FieldTemplate.js.map +1 -1
  204. package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js.map +1 -1
  205. package/dist/components/Markdown/MarkdownGithub.js.map +1 -1
  206. package/dist/components/Markdown/MarkdownSynapse.js.map +1 -1
  207. package/dist/components/Markdown/MarkdownUtils.js.map +1 -1
  208. package/dist/components/Markdown/SynapseWikiContext.js.map +1 -1
  209. package/dist/components/Markdown/UserMentionModal.js.map +1 -1
  210. package/dist/components/Markdown/widget/MarkdownProvenanceGraph.js.map +1 -1
  211. package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js.map +1 -1
  212. package/dist/components/ModalDownload/ModalDownload.js.map +1 -1
  213. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.d.ts.map +1 -1
  214. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js +45 -39
  215. package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js.map +1 -1
  216. package/dist/components/OAuthClientManagement/OAuthManagement.js.map +1 -1
  217. package/dist/components/PageProgress/PageProgress.js.map +1 -1
  218. package/dist/components/Plot/DotPlot.js.map +1 -1
  219. package/dist/components/Plot/Plot.js.map +1 -1
  220. package/dist/components/Plot/SynapsePlot.js.map +1 -1
  221. package/dist/components/Plot/ThemesPlot.js.map +1 -1
  222. package/dist/components/Plot/UpsetPlot.js.map +1 -1
  223. package/dist/components/PortalAclEditor/PortalAclEditor.d.ts.map +1 -1
  224. package/dist/components/PortalAclEditor/PortalAclEditor.js +43 -41
  225. package/dist/components/PortalAclEditor/PortalAclEditor.js.map +1 -1
  226. package/dist/components/PortalFeaturedPartners/PortalFeaturedPartners.js.map +1 -1
  227. package/dist/components/PortalList/CreatePortalModal.js.map +1 -1
  228. package/dist/components/ProgrammaticInstructionsModal/ProgrammaticInstructionsModal.js.map +1 -1
  229. package/dist/components/ProgrammaticTableDownload/ProgrammaticTableDownload.js.map +1 -1
  230. package/dist/components/Programs/Programs.Mobile.js.map +1 -1
  231. package/dist/components/Programs/Programs.js.map +1 -1
  232. package/dist/components/ProvenanceGraph/ProvenanceExternalIcon.js.map +1 -1
  233. package/dist/components/ProvenanceGraph/ProvenanceGraph.js.map +1 -1
  234. package/dist/components/ProvenanceGraph/ProvenanceGraphUtils.js.map +1 -1
  235. package/dist/components/ProvenanceGraph/ProvenanceUtils.js.map +1 -1
  236. package/dist/components/QueryCount/QueryCount.js.map +1 -1
  237. package/dist/components/QueryCountButton/QueryCountButton.js.map +1 -1
  238. package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
  239. package/dist/components/QueryWrapper/QueryWrapper.js.map +1 -1
  240. package/dist/components/QueryWrapper/TableQueryUseQueryOptions.js.map +1 -1
  241. package/dist/components/QueryWrapper/TableRowSelectionState.js.map +1 -1
  242. package/dist/components/QueryWrapper/generateEncodedPathAndQueryForSelectedFacetURL.js.map +1 -1
  243. package/dist/components/QueryWrapper/useGetQueryMetadata.js.map +1 -1
  244. package/dist/components/QueryWrapperErrorBoundary.js.map +1 -1
  245. package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
  246. package/dist/components/QueryWrapperPlotNav/UseRowSet.js.map +1 -1
  247. package/dist/components/RecentPublicationsGrid/RecentPublicationsGrid.js.map +1 -1
  248. package/dist/components/ReleaseCard/ReleaseCardUtils.js.map +1 -1
  249. package/dist/components/ResizableContainer/hooks/useResizable.js.map +1 -1
  250. package/dist/components/Resources/Resources.Mobile.js.map +1 -1
  251. package/dist/components/Resources/Resources.js.map +1 -1
  252. package/dist/components/RowDataTable/RowDataTableWithQuery.js.map +1 -1
  253. package/dist/components/SageResourcesPopover/SageResourcesPopover.js.map +1 -1
  254. package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js.map +1 -1
  255. package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
  256. package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
  257. package/dist/components/SmartLink/SmartButton.js.map +1 -1
  258. package/dist/components/SmartLink/SmartLink.js.map +1 -1
  259. package/dist/components/SourceAppImage.js.map +1 -1
  260. package/dist/components/StandaloneQueryWrapper/StandaloneQueryWrapper.js.map +1 -1
  261. package/dist/components/StatisticsPlot.js.map +1 -1
  262. package/dist/components/StorybookComponentWrapper.js.map +1 -1
  263. package/dist/components/SubsectionRowRenderer/SubsectionRowRenderer.js.map +1 -1
  264. package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js.map +1 -1
  265. package/dist/components/SynapseChat/GridAgentChat.js.map +1 -1
  266. package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
  267. package/dist/components/SynapseChat/SynapseChatMessage.js.map +1 -1
  268. package/dist/components/SynapseChat/extractMessageFromTraceEvent.js.map +1 -1
  269. package/dist/components/SynapseForm/StepsSideNav.js.map +1 -1
  270. package/dist/components/SynapseForm/SummaryTable.js.map +1 -1
  271. package/dist/components/SynapseForm/SynapseForm.js +4 -2
  272. package/dist/components/SynapseForm/SynapseForm.js.map +1 -1
  273. package/dist/components/SynapseForm/SynapseFormWrapper.js.map +1 -1
  274. package/dist/components/SynapseHomepageV2/SynapseByTheNumbersItem.js.map +1 -1
  275. package/dist/components/SynapseHomepageV2/SynapseFeatureItem.js.map +1 -1
  276. package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js.map +1 -1
  277. package/dist/components/SynapseHomepageV2/SynapseHomepageSearch.js.map +1 -1
  278. package/dist/components/SynapseHomepageV2/SynapseInActionItem.js.map +1 -1
  279. package/dist/components/SynapseHomepageV2/SynapsePlans.js.map +1 -1
  280. package/dist/components/SynapseHomepageV2/SynapseTrendingProjects.js.map +1 -1
  281. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts +8 -7
  282. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts.map +1 -1
  283. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js +173 -164
  284. package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js.map +1 -1
  285. package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
  286. package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanel.js.map +1 -1
  287. package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanelUtils.js.map +1 -1
  288. package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js.map +1 -1
  289. package/dist/components/SynapseTable/EntityIDColumnCopyIcon.js.map +1 -1
  290. package/dist/components/SynapseTable/NoContentPlaceholderType.js.map +1 -1
  291. package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js.map +1 -1
  292. package/dist/components/SynapseTable/SynapseTableCell/SynapseTableCell.js.map +1 -1
  293. package/dist/components/SynapseTable/SynapseTableRenderers.js.map +1 -1
  294. package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
  295. package/dist/components/SynapseTable/table-top/ColumnSelection.js.map +1 -1
  296. package/dist/components/SynapseTable/table-top/DownloadOptions.js.map +1 -1
  297. package/dist/components/SynapseTable/usePrefetchTableData.js.map +1 -1
  298. package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
  299. package/dist/components/TableColumnSchemaEditor/ColumnModelFormFields/DefaultValueField.js.map +1 -1
  300. package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js.map +1 -1
  301. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts +1 -1
  302. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts.map +1 -1
  303. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.js.map +1 -1
  304. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
  305. package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js.map +1 -1
  306. package/dist/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.js.map +1 -1
  307. package/dist/components/TableColumnSchemaEditor/Validators/DatetimeSchema.js.map +1 -1
  308. package/dist/components/TanStackTable/ColumnHeader.d.ts +1 -0
  309. package/dist/components/TanStackTable/ColumnHeader.d.ts.map +1 -1
  310. package/dist/components/TanStackTable/ColumnHeader.js +8 -8
  311. package/dist/components/TanStackTable/ColumnHeader.js.map +1 -1
  312. package/dist/components/TanStackTable/ColumnHeaderEnumFilter.js.map +1 -1
  313. package/dist/components/TanStackTable/TableBody.js.map +1 -1
  314. package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js.map +1 -1
  315. package/dist/components/TextField/TextField.js.map +1 -1
  316. package/dist/components/TimelinePlot/TimelinePhase.js.map +1 -1
  317. package/dist/components/TimelinePlot/TimelinePlot.js.map +1 -1
  318. package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js.map +1 -1
  319. package/dist/components/UserCard/Avatar.js.map +1 -1
  320. package/dist/components/UserCardList/UserCardList.js.map +1 -1
  321. package/dist/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.js.map +1 -1
  322. package/dist/components/UserCardList/UserCardListRotate.js.map +1 -1
  323. package/dist/components/UserOrTeamBadge/useUserOrTeam.js.map +1 -1
  324. package/dist/components/UserProfileLinks/UserProjects.js.map +1 -1
  325. package/dist/components/UserSearchBox/UserSearchBox.js.map +1 -1
  326. package/dist/components/Webhook/WebhookDashboard.js.map +1 -1
  327. package/dist/components/WikiMarkdownEditor/WikiMarkdownEditor.js.map +1 -1
  328. package/dist/components/WikiMarkdownEditorButton/WikiMarkdownEditorButton.js.map +1 -1
  329. package/dist/components/dataaccess/AccessApprovalsTable.js.map +1 -1
  330. package/dist/components/dataaccess/AccessRequestSubmissionTable.js.map +1 -1
  331. package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
  332. package/dist/components/dataaccess/UseAccessRequirementTable.js.map +1 -1
  333. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js.map +1 -1
  334. package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
  335. package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
  336. package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
  337. package/dist/components/entity/page/title_bar/useDataCiteUsage.js.map +1 -1
  338. package/dist/components/entity/page/title_bar/useGetMentions.js.map +1 -1
  339. package/dist/components/error/ErrorPage.js.map +1 -1
  340. package/dist/components/favorites/FavoritesPage.js.map +1 -1
  341. package/dist/components/file/upload/BasicFileHandleUpload.js.map +1 -1
  342. package/dist/components/layout/SWCHeader.d.ts +9 -0
  343. package/dist/components/layout/SWCHeader.d.ts.map +1 -0
  344. package/dist/components/layout/SWCHeader.js +19 -0
  345. package/dist/components/layout/SWCHeader.js.map +1 -0
  346. package/dist/components/layout/SWCPageLayout.d.ts +9 -0
  347. package/dist/components/layout/SWCPageLayout.d.ts.map +1 -0
  348. package/dist/components/layout/SWCPageLayout.js +14 -0
  349. package/dist/components/layout/SWCPageLayout.js.map +1 -0
  350. package/dist/components/menu/ComplexMenu.js.map +1 -1
  351. package/dist/components/row_renderers/utils/ChipContainer.js.map +1 -1
  352. package/dist/components/styled/StyledPopover.js.map +1 -1
  353. package/dist/components/table/CsvPreview/CsvPreview.js +2 -1
  354. package/dist/components/table/CsvPreview/CsvPreview.js.map +1 -1
  355. package/dist/components/table/CsvPreview/CsvPreviewDialog.js.map +1 -1
  356. package/dist/components/trash/TrashCanList.js.map +1 -1
  357. package/dist/components/widgets/FileHandleLink.js.map +1 -1
  358. package/dist/components/widgets/RangeSlider/RangeSlider.js.map +1 -1
  359. package/dist/components/widgets/SynapseVideo.js.map +1 -1
  360. package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
  361. package/dist/components/widgets/facet-nav/PlotsContainer.js.map +1 -1
  362. package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js.map +1 -1
  363. package/dist/components/widgets/facet-nav/useFacetPlots.js.map +1 -1
  364. package/dist/components/widgets/query-filter/CombinedRangeFacetFilter.js.map +1 -1
  365. package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
  366. package/dist/components/widgets/query-filter/FacetFilterControls.js.map +1 -1
  367. package/dist/components/widgets/query-filter/RangeFacetFilter.js.map +1 -1
  368. package/dist/components/widgets/query-filter/RangeFacetFilterUI.js.map +1 -1
  369. package/dist/features/curator/GridPage/components/GridPageTitle.d.ts.map +1 -1
  370. package/dist/features/curator/GridPage/components/GridPageTitle.js +23 -30
  371. package/dist/features/curator/GridPage/components/GridPageTitle.js.map +1 -1
  372. package/dist/features/curator/dashboard/CuratorDashboard.d.ts +2 -0
  373. package/dist/features/curator/dashboard/CuratorDashboard.d.ts.map +1 -0
  374. package/dist/features/curator/dashboard/CuratorDashboard.js +45 -0
  375. package/dist/features/curator/dashboard/CuratorDashboard.js.map +1 -0
  376. package/dist/features/curator/dashboard/components/CurationTaskCard.css +1 -0
  377. package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts +9 -0
  378. package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts.map +1 -0
  379. package/dist/features/curator/dashboard/components/CurationTaskCard.js +106 -0
  380. package/dist/features/curator/dashboard/components/CurationTaskCard.js.map +1 -0
  381. package/dist/features/curator/dashboard/components/CurationTaskCard.module.js +12 -0
  382. package/dist/features/curator/dashboard/components/CurationTaskCard.module.js.map +1 -0
  383. package/dist/features/curator/dashboard/components/CurationTaskCard.module.scss +52 -0
  384. package/dist/features/curator/dashboard/components/NextStepButton.css +1 -0
  385. package/dist/features/curator/dashboard/components/NextStepButton.d.ts +14 -0
  386. package/dist/features/curator/dashboard/components/NextStepButton.d.ts.map +1 -0
  387. package/dist/features/curator/dashboard/components/NextStepButton.js +35 -0
  388. package/dist/features/curator/dashboard/components/NextStepButton.js.map +1 -0
  389. package/dist/features/curator/dashboard/components/NextStepButton.module.js +11 -0
  390. package/dist/features/curator/dashboard/components/NextStepButton.module.js.map +1 -0
  391. package/dist/features/curator/dashboard/components/NextStepButton.module.scss +57 -0
  392. package/dist/features/curator/dashboard/components/UserOrTeamChip.css +1 -1
  393. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js +1 -1
  394. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js.map +1 -1
  395. package/dist/features/curator/dashboard/components/UserOrTeamChip.module.scss +5 -5
  396. package/dist/features/curator/dashboard/components/shared.css +1 -0
  397. package/dist/features/curator/dashboard/components/shared.module.js +5 -0
  398. package/dist/features/curator/dashboard/components/shared.module.js.map +1 -0
  399. package/dist/features/curator/dashboard/components/shared.module.scss +8 -0
  400. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts +0 -2
  401. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts.map +1 -1
  402. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +16 -34
  403. package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
  404. package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js.map +1 -1
  405. package/dist/features/entity/metadata-task/hooks/useGetOrCreateGridSessionForSource.js.map +1 -1
  406. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js.map +1 -1
  407. package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask_legacy.js.map +1 -1
  408. package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js +1 -1
  409. package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js.map +1 -1
  410. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts +10 -0
  411. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts.map +1 -0
  412. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js +37 -0
  413. package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js.map +1 -0
  414. package/dist/features/entity/metadata-task/utils/constants.d.ts +5 -0
  415. package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -0
  416. package/dist/features/entity/metadata-task/utils/constants.js +6 -0
  417. package/dist/features/entity/metadata-task/utils/constants.js.map +1 -0
  418. package/dist/mocks/challenge/mockChallenge.js.map +1 -1
  419. package/dist/mocks/entity/mockDataset.js.map +1 -1
  420. package/dist/mocks/entity/mockDatasetCollection.js.map +1 -1
  421. package/dist/mocks/entity/mockFileEntity.js.map +1 -1
  422. package/dist/mocks/entity/mockFileView.js.map +1 -1
  423. package/dist/mocks/entity/mockGeneratedEntityData.js.map +1 -1
  424. package/dist/mocks/entity/mockProject.js.map +1 -1
  425. package/dist/mocks/entity/mockProjectView.js.map +1 -1
  426. package/dist/mocks/entity/mockRootEntity.js.map +1 -1
  427. package/dist/mocks/entity/mockTableEntity.js.map +1 -1
  428. package/dist/mocks/mockWiki.js.map +1 -1
  429. package/dist/mocks/msw/handlers/asyncJobHandlers.js.map +1 -1
  430. package/dist/mocks/msw/handlers/challengeHandlers.js.map +1 -1
  431. package/dist/mocks/msw/handlers/changePasswordHandlers.js.map +1 -1
  432. package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
  433. package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
  434. package/dist/mocks/msw/handlers/fileHandlers.js.map +1 -1
  435. package/dist/mocks/msw/handlers/gridHandlers.js.map +1 -1
  436. package/dist/mocks/msw/handlers/personalAccessTokenHandlers.js.map +1 -1
  437. package/dist/mocks/msw/handlers/subscriptionHandlers.js.map +1 -1
  438. package/dist/mocks/msw/handlers/teamHandlers.js.map +1 -1
  439. package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
  440. package/dist/mocks/msw/handlers/wikiHandlers.js.map +1 -1
  441. package/dist/mocks/provenance/mockActivity.js.map +1 -1
  442. package/dist/mocks/query/mockReleaseCardsTableQueryResultBundle.js.map +1 -1
  443. package/dist/ror-client/index.js.map +1 -1
  444. package/dist/style/components/_cards.scss +4 -0
  445. package/dist/style/components/_data-grid-extra.css +1 -1
  446. package/dist/style/components/_data-grid-extra.scss +2 -0
  447. package/dist/style/main.css +1 -1
  448. package/dist/synapse-client/HttpClient.js.map +1 -1
  449. package/dist/synapse-client/SynapseClient.js.map +1 -1
  450. package/dist/synapse-queries/QueryMatching.test-utils.js.map +1 -1
  451. package/dist/synapse-queries/auth/useTwoFactorEnrollment.js.map +1 -1
  452. package/dist/synapse-queries/curation/task/useCurationTask.d.ts +1 -1
  453. package/dist/synapse-queries/curation/task/useCurationTask.d.ts.map +1 -1
  454. package/dist/synapse-queries/curation/task/useCurationTask.js +1 -1
  455. package/dist/synapse-queries/curation/task/useCurationTask.js.map +1 -1
  456. package/dist/synapse-queries/dataaccess/useRestrictionInformation.js.map +1 -1
  457. package/dist/synapse-queries/doi/useDOI.js.map +1 -1
  458. package/dist/synapse-queries/download/useDownloadList.js.map +1 -1
  459. package/dist/synapse-queries/entity/useEntity.js.map +1 -1
  460. package/dist/synapse-queries/entity/useEntityBundle.js.map +1 -1
  461. package/dist/synapse-queries/entity/useExportTableQueryToAnalysisPlatform.js.map +1 -1
  462. package/dist/synapse-queries/entity/useExportToTerra.js.map +1 -1
  463. package/dist/synapse-queries/entity/useGetQueryResultBundle.js.map +1 -1
  464. package/dist/synapse-queries/entity/useSchema.js.map +1 -1
  465. package/dist/synapse-queries/file/UploadToS3.js.map +1 -1
  466. package/dist/synapse-queries/file/useDirectUploadToS3.js.map +1 -1
  467. package/dist/synapse-queries/file/useFiles.js.map +1 -1
  468. package/dist/synapse-queries/forum/useReply.js.map +1 -1
  469. package/dist/synapse-queries/forum/useThread.js.map +1 -1
  470. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
  471. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
  472. package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
  473. package/dist/synapse-queries/grid/useExportGrid.js.map +1 -1
  474. package/dist/synapse-queries/grid/useGridSession.js.map +1 -1
  475. package/dist/synapse-queries/grid/useImportCsvIntoGrid.js.map +1 -1
  476. package/dist/synapse-queries/subscription/useSubscription.js.map +1 -1
  477. package/dist/synapse-queries/table/useGetCsvPreview.js.map +1 -1
  478. package/dist/synapse-queries/table/useTableUpdateTransaction.js.map +1 -1
  479. package/dist/synapse-queries/team/useTeamMembers.js.map +1 -1
  480. package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
  481. package/dist/synapse-queries/user/useUserBundle.js.map +1 -1
  482. package/dist/synapse-queries/user/useUserGroupHeader.js.map +1 -1
  483. package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
  484. package/dist/theme/ThemeProvider.js.map +1 -1
  485. package/dist/tsconfig.build.tsbuildinfo +1 -1
  486. package/dist/utils/AppUtils/session/SynapseSessionManager.js.map +1 -1
  487. package/dist/utils/AppUtils/session/useSessionManager.js.map +1 -1
  488. package/dist/utils/PermissionLevelToAccessType.js.map +1 -1
  489. package/dist/utils/challenge/evaluation/EvaluationUtils.js.map +1 -1
  490. package/dist/utils/context/SynapseContext.js.map +1 -1
  491. package/dist/utils/functions/AccessControlListUtils.d.ts +4 -0
  492. package/dist/utils/functions/AccessControlListUtils.d.ts.map +1 -1
  493. package/dist/utils/functions/AccessControlListUtils.js +12 -1
  494. package/dist/utils/functions/AccessControlListUtils.js.map +1 -1
  495. package/dist/utils/functions/GridApiUtils.js.map +1 -1
  496. package/dist/utils/functions/QueryFilterUtils.js.map +1 -1
  497. package/dist/utils/functions/RealmUtils.d.ts +4 -0
  498. package/dist/utils/functions/RealmUtils.d.ts.map +1 -1
  499. package/dist/utils/functions/RealmUtils.js +9 -3
  500. package/dist/utils/functions/RealmUtils.js.map +1 -1
  501. package/dist/utils/functions/SanitizeHtmlUtils.js.map +1 -1
  502. package/dist/utils/functions/SanitizeHtmlUtils.test-utils.js.map +1 -1
  503. package/dist/utils/functions/SqlFunctions.js.map +1 -1
  504. package/dist/utils/functions/StringUtils.js.map +1 -1
  505. package/dist/utils/functions/deepLinkingUtils.js.map +1 -1
  506. package/dist/utils/functions/getDataFromFromStorage.js.map +1 -1
  507. package/dist/utils/functions/getEndpoint.js.map +1 -1
  508. package/dist/utils/functions/getUserData.js.map +1 -1
  509. package/dist/utils/functions/queryUtils.js.map +1 -1
  510. package/dist/utils/functions/testDownloadSpeed.js.map +1 -1
  511. package/dist/utils/hooks/useConfirmItems.js.map +1 -1
  512. package/dist/utils/hooks/useCookiePreferences.js.map +1 -1
  513. package/dist/utils/hooks/useCreateShortUrl.js.map +1 -1
  514. package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
  515. package/dist/utils/hooks/useDirectDownloadHandler.js.map +1 -1
  516. package/dist/utils/hooks/useGetGoalData.js.map +1 -1
  517. package/dist/utils/hooks/useGetInfoFromIds.js.map +1 -1
  518. package/dist/utils/hooks/useImageUrlUtils.js.map +1 -1
  519. package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
  520. package/dist/utils/hooks/useImmutableTableQuery/useTableQueryReducer.js.map +1 -1
  521. package/dist/utils/hooks/useIsBot.js.map +1 -1
  522. package/dist/utils/hooks/useListState.js.map +1 -1
  523. package/dist/utils/hooks/useLogin.d.ts.map +1 -1
  524. package/dist/utils/hooks/useLogin.js +53 -52
  525. package/dist/utils/hooks/useLogin.js.map +1 -1
  526. package/dist/utils/hooks/useMutuallyExclusiveState.js.map +1 -1
  527. package/dist/utils/hooks/useOverlay.js.map +1 -1
  528. package/dist/utils/hooks/usePreFetchResource.js.map +1 -1
  529. package/dist/utils/hooks/useQuerySearchParam.js.map +1 -1
  530. package/dist/utils/hooks/useScrollFadeTransition.js.map +1 -1
  531. package/dist/utils/hooks/useSet.js.map +1 -1
  532. package/dist/utils/hooks/useSourceAppConfigs.js.map +1 -1
  533. package/dist/utils/hooks/useTableImageUrl.js.map +1 -1
  534. package/dist/utils/hooks/useUploadFileEntity/useCreatePathsAndGetParentId.js.map +1 -1
  535. package/dist/utils/hooks/useUploadFileEntity/useLinkFileEntityToURL.js.map +1 -1
  536. package/dist/utils/hooks/useUploadFileEntity/usePrepareFileEntityUpload.js.map +1 -1
  537. package/dist/utils/hooks/useUploadFileEntity/useTrackFileUploads.js.map +1 -1
  538. package/dist/utils/hooks/useUploadFileEntity/useUploadFileEntities.js.map +1 -1
  539. package/dist/utils/hooks/useUploadFileEntity/useUploadFiles.js.map +1 -1
  540. package/dist/utils/hooks/useUploadFileEntity/willUploadsExceedStorageLimit.js.map +1 -1
  541. package/dist/utils/html/TargetEnum.js.map +1 -1
  542. package/dist/utils/jsonschema/SchemaAnnotationUtils.js.map +1 -1
  543. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"getEndpoint.js","names":[],"sources":["../../../src/utils/functions/getEndpoint.ts"],"sourcesContent":["import {\n SYNAPSE_BACKEND_DEV_URL,\n SYNAPSE_BACKEND_PRODUCTION_URL,\n SYNAPSE_BACKEND_STAGING_URL,\n} from '../SynapseConstants'\n\nexport enum BackendDestinationEnum {\n REPO_ENDPOINT,\n PORTAL_ENDPOINT,\n}\n\n// Encodes the portal and repo service destinations\nexport type EndpointObject = {\n REPO: string\n PORTAL: string\n}\n\nexport const KNOWN_SYNAPSE_ORG_URLS = [\n 'www.synapse.org',\n 'staging.synapse.org',\n 'tst.synapse.org',\n 'dev.synapse.org',\n '127.0.0.1',\n]\n\nexport const getSynapsePortalEndpoint = (hostname: string): string => {\n return KNOWN_SYNAPSE_ORG_URLS.includes(hostname.toLowerCase())\n ? '/'\n : 'https://www.synapse.org/'\n}\nfunction getDefaultSynapsePortal(): string {\n if (typeof window === 'undefined') return getSynapsePortalEndpoint('')\n return getSynapsePortalEndpoint(window.location.hostname)\n}\n\nexport const PRODUCTION_ENDPOINT_CONFIG: EndpointObject = {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n // Computed lazily via a getter so this object can be imported in Node.js (SSR/pre-render)\n // without crashing on the `window.location.hostname` access.\n get PORTAL() {\n return getDefaultSynapsePortal()\n },\n}\n\n// Given an endpoint will return the specific stack object\nexport const getEndpoint = (endpoint: BackendDestinationEnum): string => {\n let endpoint_config = PRODUCTION_ENDPOINT_CONFIG\n if (typeof window !== 'undefined') {\n // @ts-expect-error if overriding endpoint config\n if (window.SRC_OVERRIDE_ENDPOINT_CONFIG) {\n // @ts-expect-error\n endpoint_config = window.SRC_OVERRIDE_ENDPOINT_CONFIG\n }\n }\n const { PORTAL, REPO } = endpoint_config\n if (!PORTAL || !REPO) {\n throw Error('Error failed to specify endpoints, cannot make call')\n }\n if (endpoint === BackendDestinationEnum.PORTAL_ENDPOINT) {\n return PORTAL\n }\n return REPO\n}\n\nexport type SynapseStack =\n | 'production'\n | 'staging'\n | 'development'\n | 'mock'\n | 'local'\n\nexport const MOCK_REPO_ORIGIN =\n 'https://mock-repo.sagebase.org' satisfies string\nconst MOCK_PORTAL_ORIGIN = 'https://mock-portal.sagebase.org/' satisfies string\n\nexport const STACK_MAP: Record<SynapseStack, EndpointObject> = {\n production: {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n PORTAL: 'https://www.synapse.org/',\n },\n staging: {\n REPO: SYNAPSE_BACKEND_STAGING_URL,\n PORTAL: 'https://staging.synapse.org/',\n },\n development: {\n REPO: SYNAPSE_BACKEND_DEV_URL,\n PORTAL: 'https://dev.synapse.org/',\n },\n mock: {\n REPO: MOCK_REPO_ORIGIN,\n PORTAL: MOCK_PORTAL_ORIGIN,\n },\n local: {\n REPO: 'http://localhost:8080/services-repository-develop-SNAPSHOT',\n PORTAL: 'http://localhost:8888/',\n },\n}\n"],"mappings":";;AAMA,IAAY,IAAL,yBAAA,GAAA;QACL,EAAA,EAAA,gBAAA,KAAA,iBACA,EAAA,EAAA,kBAAA,KAAA;KACD,EAQY,IAAyB;CACpC;CACA;CACA;CACA;CACA;CACD,EAEY,KAA4B,MAChC,EAAuB,SAAS,EAAS,aAAa,CAAC,GAC1D,MACA;AAEN,SAAS,IAAkC;AAEzC,QAD0C,EAAtC,OAAO,SAAW,MAA6C,KACnC,OAAO,SAAS,SADsB;;AAIxE,IAAa,IAA6C;CACxD,MAAM;CAGN,IAAI,SAAS;AACX,SAAO,GAAyB;;CAEnC,EAGY,KAAe,MAA6C;CACvE,IAAI,IAAkB;AACtB,CAAI,OAAO,SAAW,OAEhB,OAAO,iCAET,IAAkB,OAAO;CAG7B,IAAM,EAAE,WAAQ,YAAS;AACzB,KAAI,CAAC,KAAU,CAAC,EACd,OAAM,MAAM,sDAAsD;AAKpE,QAHI,MAAa,EAAuB,kBAC/B,IAEF;GAUI,IACX,kCAGW,IAAkD;CAC7D,YAAY;EACV,MAAM;EACN,QAAQ;EACT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACT;CACD,aAAa;EACX,MAAM;EACN,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,QAjBuB;EAkBxB;CACD,OAAO;EACL,MAAM;EACN,QAAQ;EACT;CACF"}
1
+ {"version":3,"file":"getEndpoint.js","names":[],"sources":["../../../src/utils/functions/getEndpoint.ts"],"sourcesContent":["import {\n SYNAPSE_BACKEND_DEV_URL,\n SYNAPSE_BACKEND_PRODUCTION_URL,\n SYNAPSE_BACKEND_STAGING_URL,\n} from '../SynapseConstants'\n\nexport enum BackendDestinationEnum {\n REPO_ENDPOINT,\n PORTAL_ENDPOINT,\n}\n\n// Encodes the portal and repo service destinations\nexport type EndpointObject = {\n REPO: string\n PORTAL: string\n}\n\nexport const KNOWN_SYNAPSE_ORG_URLS = [\n 'www.synapse.org',\n 'staging.synapse.org',\n 'tst.synapse.org',\n 'dev.synapse.org',\n '127.0.0.1',\n]\n\nexport const getSynapsePortalEndpoint = (hostname: string): string => {\n return KNOWN_SYNAPSE_ORG_URLS.includes(hostname.toLowerCase())\n ? '/'\n : 'https://www.synapse.org/'\n}\nfunction getDefaultSynapsePortal(): string {\n if (typeof window === 'undefined') return getSynapsePortalEndpoint('')\n return getSynapsePortalEndpoint(window.location.hostname)\n}\n\nexport const PRODUCTION_ENDPOINT_CONFIG: EndpointObject = {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n // Computed lazily via a getter so this object can be imported in Node.js (SSR/pre-render)\n // without crashing on the `window.location.hostname` access.\n get PORTAL() {\n return getDefaultSynapsePortal()\n },\n}\n\n// Given an endpoint will return the specific stack object\nexport const getEndpoint = (endpoint: BackendDestinationEnum): string => {\n let endpoint_config = PRODUCTION_ENDPOINT_CONFIG\n if (typeof window !== 'undefined') {\n // @ts-expect-error if overriding endpoint config\n if (window.SRC_OVERRIDE_ENDPOINT_CONFIG) {\n // @ts-expect-error\n endpoint_config = window.SRC_OVERRIDE_ENDPOINT_CONFIG\n }\n }\n const { PORTAL, REPO } = endpoint_config\n if (!PORTAL || !REPO) {\n throw Error('Error failed to specify endpoints, cannot make call')\n }\n if (endpoint === BackendDestinationEnum.PORTAL_ENDPOINT) {\n return PORTAL\n }\n return REPO\n}\n\nexport type SynapseStack =\n | 'production'\n | 'staging'\n | 'development'\n | 'mock'\n | 'local'\n\nexport const MOCK_REPO_ORIGIN =\n 'https://mock-repo.sagebase.org' satisfies string\nconst MOCK_PORTAL_ORIGIN = 'https://mock-portal.sagebase.org/' satisfies string\n\nexport const STACK_MAP: Record<SynapseStack, EndpointObject> = {\n production: {\n REPO: SYNAPSE_BACKEND_PRODUCTION_URL,\n PORTAL: 'https://www.synapse.org/',\n },\n staging: {\n REPO: SYNAPSE_BACKEND_STAGING_URL,\n PORTAL: 'https://staging.synapse.org/',\n },\n development: {\n REPO: SYNAPSE_BACKEND_DEV_URL,\n PORTAL: 'https://dev.synapse.org/',\n },\n mock: {\n REPO: MOCK_REPO_ORIGIN,\n PORTAL: MOCK_PORTAL_ORIGIN,\n },\n local: {\n REPO: 'http://localhost:8080/services-repository-develop-SNAPSHOT',\n PORTAL: 'http://localhost:8888/',\n },\n}\n"],"mappings":";;AAMA,IAAY,IAAL,yBAAA,GAAA;QACL,EAAA,EAAA,gBAAA,KAAA,iBACA,EAAA,EAAA,kBAAA,KAAA;KACD,EAQY,IAAyB;CACpC;CACA;CACA;CACA;CACA;CACD,EAEY,KAA4B,MAChC,EAAuB,SAAS,EAAS,aAAa,CAAC,GAC1D,MACA;AAEN,SAAS,IAAkC;AAEzC,QAD0C,EAAtC,OAAO,SAAW,MAA6C,KACnC,OAAO,SAAS,SADsB;;AAIxE,IAAa,IAA6C;CACxD,MAAM;CAGN,IAAI,SAAS;AACX,SAAO,GAAyB;;CAEnC,EAGY,KAAe,MAA6C;CACvE,IAAI,IAAkB;AACtB,CAAI,OAAO,SAAW,OAEhB,OAAO,iCAET,IAAkB,OAAO;CAG7B,IAAM,EAAE,WAAQ,YAAS;AACzB,KAAI,CAAC,KAAU,CAAC,EACd,OAAM,MAAM,sDAAsD;AAKpE,QAHI,MAAa,EAAuB,kBAC/B,IAEF;GAUI,IACX,kCAGW,IAAkD;CAC7D,YAAY;EACV,MAAM;EACN,QAAQ;EACT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACT;CACD,aAAa;EACX,MAAM;EACN,QAAQ;EACT;CACD,MAAM;EACJ,MAAM;EACN,QAAQ;EACT;CACD,OAAO;EACL,MAAM;EACN,QAAQ;EACT;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"getUserData.js","names":[],"sources":["../../../src/utils/functions/getUserData.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { BackendDestinationEnum, getEndpoint } from './getEndpoint'\n\n/*\n Utility functions for UserCards\n*/\n\nexport async function getUserProfileWithProfilePicAttached(\n principalIds: string[],\n) {\n const userProfiles = await SynapseClient.getUserProfiles(principalIds)\n const profilesWithPictures = userProfiles.list.map(profile => {\n if (profile.profilePicureFileHandleId) {\n return {\n ...profile,\n clientPreSignedURL: `${getEndpoint(\n BackendDestinationEnum.REPO_ENDPOINT,\n )}/repo/v1/userProfile/${profile.ownerId}/image/preview?redirect=true`,\n }\n } else {\n return profile\n }\n })\n return { list: profilesWithPictures }\n}\n\nconst COLORS: string[] = [\n 'chocolate',\n 'black',\n 'firebrick',\n 'maroon',\n 'olive',\n 'green',\n 'forestgreen',\n 'darkturquoise',\n 'teal',\n 'blue',\n 'navy',\n 'darkmagenta',\n 'purple',\n 'blue',\n 'orangered',\n 'blueviolet',\n]\n\nconst hash = (userName: string) => {\n const val = userName\n .split('')\n .reduce(\n (prevHash, currVal) =>\n ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0,\n 0,\n )\n return Math.abs(val)\n}\n\nexport const getColor = (userName: string) => {\n const hashedUserName = hash(userName)\n return COLORS[hashedUserName % COLORS.length]\n}\n"],"mappings":";;;AAOA,eAAsB,EACpB,GACA;AAcA,QAAO,EAAE,OAbY,MAAM,EAAc,gBAAgB,EAAa,EAC5B,KAAK,KAAI,MAC7C,EAAQ,4BACH;EACL,GAAG;EACH,oBAAoB,GAAG,EACrB,EAAuB,cACxB,CAAC,uBAAuB,EAAQ,QAAQ;EAC1C,GAEM,EAET,EACmC;;AAGvC,IAAM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,KAAQ,MAAqB;CACjC,IAAM,IAAM,EACT,MAAM,GAAG,CACT,QACE,GAAU,OACP,KAAY,KAAK,IAAW,EAAQ,WAAW,EAAE,GAAI,GACzD,EACD;AACH,QAAO,KAAK,IAAI,EAAI;GAGT,KAAY,MAEhB,EADgB,EAAK,EAAS,GACN,EAAO"}
1
+ {"version":3,"file":"getUserData.js","names":[],"sources":["../../../src/utils/functions/getUserData.ts"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { BackendDestinationEnum, getEndpoint } from './getEndpoint'\n\n/*\n Utility functions for UserCards\n*/\n\nexport async function getUserProfileWithProfilePicAttached(\n principalIds: string[],\n) {\n const userProfiles = await SynapseClient.getUserProfiles(principalIds)\n const profilesWithPictures = userProfiles.list.map(profile => {\n if (profile.profilePicureFileHandleId) {\n return {\n ...profile,\n clientPreSignedURL: `${getEndpoint(\n BackendDestinationEnum.REPO_ENDPOINT,\n )}/repo/v1/userProfile/${profile.ownerId}/image/preview?redirect=true`,\n }\n } else {\n return profile\n }\n })\n return { list: profilesWithPictures }\n}\n\nconst COLORS: string[] = [\n 'chocolate',\n 'black',\n 'firebrick',\n 'maroon',\n 'olive',\n 'green',\n 'forestgreen',\n 'darkturquoise',\n 'teal',\n 'blue',\n 'navy',\n 'darkmagenta',\n 'purple',\n 'blue',\n 'orangered',\n 'blueviolet',\n]\n\nconst hash = (userName: string) => {\n const val = userName\n .split('')\n .reduce(\n (prevHash, currVal) =>\n ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0,\n 0,\n )\n return Math.abs(val)\n}\n\nexport const getColor = (userName: string) => {\n const hashedUserName = hash(userName)\n return COLORS[hashedUserName % COLORS.length]\n}\n"],"mappings":";;;AAOA,eAAsB,EACpB,GACA;AAcA,QAAO,EAAE,OAZoB,MADF,EAAc,gBAAgB,EAAa,EAC5B,KAAK,KAAI,MAC7C,EAAQ,4BACH;EACL,GAAG;EACH,oBAAoB,GAAG,EACrB,EAAuB,cACxB,CAAC,uBAAuB,EAAQ,QAAQ;EAC1C,GAEM,EAGI,EAAsB;;AAGvC,IAAM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,KAAQ,MAAqB;CACjC,IAAM,IAAM,EACT,MAAM,GAAG,CACT,QACE,GAAU,OACP,KAAY,KAAK,IAAW,EAAQ,WAAW,EAAE,GAAI,GACzD,EACD;AACH,QAAO,KAAK,IAAI,EAAI;GAGT,KAAY,MAEhB,EADgB,EAAK,EACd,GAAiB,EAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"queryUtils.js","names":[],"sources":["../../../src/utils/functions/queryUtils.ts"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n LockedColumn,\n UniqueFacetIdentifier,\n} from '@/utils/types'\nimport {\n ColumnModel,\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResult,\n JsonSubColumnModel,\n Query,\n QueryBundleRequest,\n QueryResultBundle,\n SelectColumn,\n Table,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual, isEqualWith, isMatch, isNil } from 'lodash-es'\nimport * as SynapseConstants from '../SynapseConstants'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '../SynapseConstants'\nimport {\n hasFilesInView,\n isDataset,\n isDatasetCollection,\n isEntityView,\n isFileView,\n} from './EntityTypeUtils'\n\n/**\n * Retrieve the index of a column using the column name\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getFieldIndex = (name?: string, result?: QueryResultBundle) => {\n if (name == undefined) {\n return -1\n }\n return (\n result?.selectColumns?.findIndex(el => {\n return el.name === name\n }) ?? -1\n )\n}\n\n/**\n * Retrieve the index of a column using the header column name found inthe query results.\n * Ignores case.\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getHeaderIndex = (\n name: string,\n result: QueryResultBundle | undefined,\n) => {\n const nameLowercase = name.toLowerCase()\n return (\n result?.queryResult?.queryResults.headers.findIndex(el => {\n return el.name.toLowerCase() === nameLowercase\n }) ?? -1\n )\n}\n\n/**\n * Returns the indices of the selectColumns with the specified type\n * @param columnType\n * @param selectColumns\n */\nexport function getTypeIndices(\n columnType: ColumnTypeEnum,\n selectColumns: SelectColumn[] = [],\n): number[] {\n return selectColumns.reduce((prev: number[], curr, index) => {\n if (curr.columnType === columnType) {\n return [...prev, index]\n }\n return prev\n }, [])\n}\n\nexport const hasFacetedSelectColumn = (\n facets?: FacetColumnResult[],\n selectColumns?: SelectColumn[],\n): boolean => {\n /**\n * Facets are available iff\n * * there is at least one facet AND\n * * each facet has a corresponding columnModel in the selectColumns AND\n * * each facets has a valid value other than the null/not set value\n */\n if (facets == null || selectColumns == null) {\n return false\n }\n\n if (facets.length === 0 || selectColumns.length === 0) {\n return false\n }\n\n const facetsWithValuesAndColumnModels = facets.filter(facet => {\n return (\n !isSingleNotSetValue(facet) &&\n selectColumns.find(model => model.name === facet.columnName)\n )\n })\n\n return facetsWithValuesAndColumnModels.length > 0\n}\n\nexport const isSingleNotSetValue = (facet: FacetColumnResult): boolean => {\n return (\n facet.facetType === 'enumeration' &&\n facet.facetValues.length == 1 &&\n facet.facetValues[0].value == SynapseConstants.VALUE_NOT_SET\n )\n}\n\n// TODO Instead of removing the facet from the data, the facet renderers should just be aware of what to hide\nexport function removeLockedColumnFromFacetData(\n data?: QueryResultBundle,\n lockedColumn?: LockedColumn,\n): QueryResultBundle | undefined {\n const lockedColumnName = lockedColumn?.columnName\n if (lockedColumnName && data) {\n // for details page, return data without the \"locked\" facet\n const dataCopy: QueryResultBundle = cloneDeep(data)\n const facets = dataCopy.facets?.filter(\n item => item.columnName.toLowerCase() !== lockedColumnName.toLowerCase(),\n )\n dataCopy.facets = facets\n return dataCopy\n } else {\n // for other pages, just return the data\n return data\n }\n}\n\n/**\n * Returns true iff the query has filters applied that can be reset.\n * This includes facet filters and additional filters that are not applied to a locked column.\n */\nexport function hasResettableFilters(\n query: Query,\n lockedColumn?: LockedColumn,\n): boolean {\n const hasFacetFilters =\n Array.isArray(query.selectedFacets) &&\n query.selectedFacets.filter(\n facet =>\n facet.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase(),\n ).length > 0\n const hasAdditionalFilters =\n Array.isArray(query.additionalFilters) &&\n query.additionalFilters.filter(queryFilter =>\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ? queryFilter.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase()\n : true,\n ).length > 0\n\n return hasFacetFilters || hasAdditionalFilters\n}\n\n/**\n * Returns true iff a table query can be added to the download list.\n * @param entity\n * @param entityColumnId\n */\nexport function canTableQueryBeAddedToDownloadList<T extends Table = Table>(\n entity?: T,\n entityColumnId?: string,\n) {\n const viewCannotIncludeFiles =\n // EntityViews without the file bit mask cannot contain files\n (entity && isEntityView(entity) && !hasFilesInView(entity)) ||\n // DatasetCollections cannot contain files\n (entity && isDatasetCollection(entity))\n\n if (viewCannotIncludeFiles) {\n return false\n }\n\n return Boolean(\n entityColumnId ||\n (entity &&\n ((isEntityView(entity) && isFileView(entity)) || isDataset(entity))),\n )\n}\n\n/**\n * Returns true if the queries necessarily return the same result set, i.e. if they have identical parameters other than\n * limit, offset, sort.\n *\n * Note that this does not query the actual tables, so this only captures the semantic equivalence of the queries.\n */\nexport function queryRequestsHaveSameTotalResults(\n request1: Query,\n request2: Query,\n) {\n const clone1 = cloneDeep(request1)\n delete clone1.limit\n delete clone1.offset\n delete clone1.sort\n const clone2 = cloneDeep(request2)\n delete clone2.limit\n delete clone2.offset\n delete clone2.sort\n return isEqualWith(clone1, clone2, (value1, value2) => {\n if (isNil(value1) && isNil(value2)) {\n // Consider all nil (e.g. undefined/null) values equal\n return true\n }\n return isEqual(value1, value2)\n })\n}\n\n/**\n * Remove null/empty values from the query parameters where an undefined value is equivalent.\n *\n * This will ensure a query object is as simple as possible for URL search parameters and also increases the\n * likelihood of a cache hit.\n * @param q\n */\nexport function removeEmptyQueryParams(q: Query) {\n const query = cloneDeep(q)\n\n if (query.limit == null) {\n delete query.limit\n }\n if (query.offset == null || query.offset == 0) {\n delete query.offset\n }\n\n if (query.sort == null || query.sort.length == 0) {\n delete query.sort\n }\n\n if (query.selectedFacets == null || query.selectedFacets.length == 0) {\n delete query.selectedFacets\n }\n\n if (query.additionalFilters == null || query.additionalFilters.length == 0) {\n delete query.additionalFilters\n }\n\n return query\n}\n\n/**\n * Given a FacetColumnResult and a set of ColumnModels, return the ColumnModel that\n * matches the FacetColumnResult.\n * @param facet\n * @param columnModels\n */\nexport function getCorrespondingColumnForFacet(\n facet: FacetColumnResult,\n columnModels: ColumnModel[],\n): ColumnModel | JsonSubColumnModel | undefined {\n let columnModel: ColumnModel | JsonSubColumnModel | undefined =\n columnModels.find(model => model.name === facet.columnName)\n if (facet.jsonPath && columnModel && columnModel.jsonSubColumns) {\n columnModel = columnModel.jsonSubColumns.find(\n cm => cm.jsonPath === facet.jsonPath,\n )\n }\n return columnModel\n}\n\n/**\n * Given a set of FacetColumnRequests, return the FacetColumnRequest that matches the given UniqueFacetIdentifier.\n * @param facetDefinition\n * @param selectedFacets\n */\nexport function getCorrespondingSelectedFacet(\n facetDefinition: UniqueFacetIdentifier,\n selectedFacets?: FacetColumnRequest[],\n): FacetColumnRequest | undefined {\n return selectedFacets?.find(selectedFacet =>\n facetObjectMatchesDefinition(facetDefinition, selectedFacet),\n )\n}\n\nexport function facetObjectMatchesDefinition(\n facetDefinition:\n | UniqueFacetIdentifier\n | FacetColumnRequest\n | FacetColumnResult,\n facetObject: UniqueFacetIdentifier | FacetColumnRequest | FacetColumnResult,\n) {\n return isMatch(\n {\n columnName: facetDefinition.columnName,\n jsonPath: facetDefinition.jsonPath,\n },\n { columnName: facetObject.columnName, jsonPath: facetObject.jsonPath },\n )\n}\n\nexport function partitionQueryBundleRequestIntoRowsAndMetadata(\n queryBundleRequest: QueryBundleRequest,\n): {\n rowDataRequest: QueryBundleRequest\n queryMetadataRequest: QueryBundleRequest\n} {\n const rowDataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n // Get just the rows (if they were originally requested)\n partMask: queryBundleRequest.partMask & BUNDLE_MASK_QUERY_RESULTS,\n }\n\n const queryMetadataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n query: {\n ...queryBundleRequest.query,\n // Remove query fields that don't affect the results.\n offset: undefined,\n limit: undefined,\n sort: undefined,\n },\n // Bitwise remove the query result flag from the mask\n partMask: queryBundleRequest.partMask & ~BUNDLE_MASK_QUERY_RESULTS,\n }\n\n return {\n rowDataRequest,\n queryMetadataRequest,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"queryUtils.js","names":[],"sources":["../../../src/utils/functions/queryUtils.ts"],"sourcesContent":["import {\n isColumnMultiValueFunctionQueryFilter,\n isColumnSingleValueQueryFilter,\n LockedColumn,\n UniqueFacetIdentifier,\n} from '@/utils/types'\nimport {\n ColumnModel,\n ColumnTypeEnum,\n FacetColumnRequest,\n FacetColumnResult,\n JsonSubColumnModel,\n Query,\n QueryBundleRequest,\n QueryResultBundle,\n SelectColumn,\n Table,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual, isEqualWith, isMatch, isNil } from 'lodash-es'\nimport * as SynapseConstants from '../SynapseConstants'\nimport { BUNDLE_MASK_QUERY_RESULTS } from '../SynapseConstants'\nimport {\n hasFilesInView,\n isDataset,\n isDatasetCollection,\n isEntityView,\n isFileView,\n} from './EntityTypeUtils'\n\n/**\n * Retrieve the index of a column using the column name\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getFieldIndex = (name?: string, result?: QueryResultBundle) => {\n if (name == undefined) {\n return -1\n }\n return (\n result?.selectColumns?.findIndex(el => {\n return el.name === name\n }) ?? -1\n )\n}\n\n/**\n * Retrieve the index of a column using the header column name found inthe query results.\n * Ignores case.\n * @param name the column name\n * @param result the QueryResultBundle containing the columns\n * @returns The index of the column, or -1 if the column doesn't exist in the result\n */\nexport const getHeaderIndex = (\n name: string,\n result: QueryResultBundle | undefined,\n) => {\n const nameLowercase = name.toLowerCase()\n return (\n result?.queryResult?.queryResults.headers.findIndex(el => {\n return el.name.toLowerCase() === nameLowercase\n }) ?? -1\n )\n}\n\n/**\n * Returns the indices of the selectColumns with the specified type\n * @param columnType\n * @param selectColumns\n */\nexport function getTypeIndices(\n columnType: ColumnTypeEnum,\n selectColumns: SelectColumn[] = [],\n): number[] {\n return selectColumns.reduce((prev: number[], curr, index) => {\n if (curr.columnType === columnType) {\n return [...prev, index]\n }\n return prev\n }, [])\n}\n\nexport const hasFacetedSelectColumn = (\n facets?: FacetColumnResult[],\n selectColumns?: SelectColumn[],\n): boolean => {\n /**\n * Facets are available iff\n * * there is at least one facet AND\n * * each facet has a corresponding columnModel in the selectColumns AND\n * * each facets has a valid value other than the null/not set value\n */\n if (facets == null || selectColumns == null) {\n return false\n }\n\n if (facets.length === 0 || selectColumns.length === 0) {\n return false\n }\n\n const facetsWithValuesAndColumnModels = facets.filter(facet => {\n return (\n !isSingleNotSetValue(facet) &&\n selectColumns.find(model => model.name === facet.columnName)\n )\n })\n\n return facetsWithValuesAndColumnModels.length > 0\n}\n\nexport const isSingleNotSetValue = (facet: FacetColumnResult): boolean => {\n return (\n facet.facetType === 'enumeration' &&\n facet.facetValues.length == 1 &&\n facet.facetValues[0].value == SynapseConstants.VALUE_NOT_SET\n )\n}\n\n// TODO Instead of removing the facet from the data, the facet renderers should just be aware of what to hide\nexport function removeLockedColumnFromFacetData(\n data?: QueryResultBundle,\n lockedColumn?: LockedColumn,\n): QueryResultBundle | undefined {\n const lockedColumnName = lockedColumn?.columnName\n if (lockedColumnName && data) {\n // for details page, return data without the \"locked\" facet\n const dataCopy: QueryResultBundle = cloneDeep(data)\n const facets = dataCopy.facets?.filter(\n item => item.columnName.toLowerCase() !== lockedColumnName.toLowerCase(),\n )\n dataCopy.facets = facets\n return dataCopy\n } else {\n // for other pages, just return the data\n return data\n }\n}\n\n/**\n * Returns true iff the query has filters applied that can be reset.\n * This includes facet filters and additional filters that are not applied to a locked column.\n */\nexport function hasResettableFilters(\n query: Query,\n lockedColumn?: LockedColumn,\n): boolean {\n const hasFacetFilters =\n Array.isArray(query.selectedFacets) &&\n query.selectedFacets.filter(\n facet =>\n facet.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase(),\n ).length > 0\n const hasAdditionalFilters =\n Array.isArray(query.additionalFilters) &&\n query.additionalFilters.filter(queryFilter =>\n isColumnSingleValueQueryFilter(queryFilter) ||\n isColumnMultiValueFunctionQueryFilter(queryFilter)\n ? queryFilter.columnName.toLowerCase() !==\n lockedColumn?.columnName?.toLowerCase()\n : true,\n ).length > 0\n\n return hasFacetFilters || hasAdditionalFilters\n}\n\n/**\n * Returns true iff a table query can be added to the download list.\n * @param entity\n * @param entityColumnId\n */\nexport function canTableQueryBeAddedToDownloadList<T extends Table = Table>(\n entity?: T,\n entityColumnId?: string,\n) {\n const viewCannotIncludeFiles =\n // EntityViews without the file bit mask cannot contain files\n (entity && isEntityView(entity) && !hasFilesInView(entity)) ||\n // DatasetCollections cannot contain files\n (entity && isDatasetCollection(entity))\n\n if (viewCannotIncludeFiles) {\n return false\n }\n\n return Boolean(\n entityColumnId ||\n (entity &&\n ((isEntityView(entity) && isFileView(entity)) || isDataset(entity))),\n )\n}\n\n/**\n * Returns true if the queries necessarily return the same result set, i.e. if they have identical parameters other than\n * limit, offset, sort.\n *\n * Note that this does not query the actual tables, so this only captures the semantic equivalence of the queries.\n */\nexport function queryRequestsHaveSameTotalResults(\n request1: Query,\n request2: Query,\n) {\n const clone1 = cloneDeep(request1)\n delete clone1.limit\n delete clone1.offset\n delete clone1.sort\n const clone2 = cloneDeep(request2)\n delete clone2.limit\n delete clone2.offset\n delete clone2.sort\n return isEqualWith(clone1, clone2, (value1, value2) => {\n if (isNil(value1) && isNil(value2)) {\n // Consider all nil (e.g. undefined/null) values equal\n return true\n }\n return isEqual(value1, value2)\n })\n}\n\n/**\n * Remove null/empty values from the query parameters where an undefined value is equivalent.\n *\n * This will ensure a query object is as simple as possible for URL search parameters and also increases the\n * likelihood of a cache hit.\n * @param q\n */\nexport function removeEmptyQueryParams(q: Query) {\n const query = cloneDeep(q)\n\n if (query.limit == null) {\n delete query.limit\n }\n if (query.offset == null || query.offset == 0) {\n delete query.offset\n }\n\n if (query.sort == null || query.sort.length == 0) {\n delete query.sort\n }\n\n if (query.selectedFacets == null || query.selectedFacets.length == 0) {\n delete query.selectedFacets\n }\n\n if (query.additionalFilters == null || query.additionalFilters.length == 0) {\n delete query.additionalFilters\n }\n\n return query\n}\n\n/**\n * Given a FacetColumnResult and a set of ColumnModels, return the ColumnModel that\n * matches the FacetColumnResult.\n * @param facet\n * @param columnModels\n */\nexport function getCorrespondingColumnForFacet(\n facet: FacetColumnResult,\n columnModels: ColumnModel[],\n): ColumnModel | JsonSubColumnModel | undefined {\n let columnModel: ColumnModel | JsonSubColumnModel | undefined =\n columnModels.find(model => model.name === facet.columnName)\n if (facet.jsonPath && columnModel && columnModel.jsonSubColumns) {\n columnModel = columnModel.jsonSubColumns.find(\n cm => cm.jsonPath === facet.jsonPath,\n )\n }\n return columnModel\n}\n\n/**\n * Given a set of FacetColumnRequests, return the FacetColumnRequest that matches the given UniqueFacetIdentifier.\n * @param facetDefinition\n * @param selectedFacets\n */\nexport function getCorrespondingSelectedFacet(\n facetDefinition: UniqueFacetIdentifier,\n selectedFacets?: FacetColumnRequest[],\n): FacetColumnRequest | undefined {\n return selectedFacets?.find(selectedFacet =>\n facetObjectMatchesDefinition(facetDefinition, selectedFacet),\n )\n}\n\nexport function facetObjectMatchesDefinition(\n facetDefinition:\n | UniqueFacetIdentifier\n | FacetColumnRequest\n | FacetColumnResult,\n facetObject: UniqueFacetIdentifier | FacetColumnRequest | FacetColumnResult,\n) {\n return isMatch(\n {\n columnName: facetDefinition.columnName,\n jsonPath: facetDefinition.jsonPath,\n },\n { columnName: facetObject.columnName, jsonPath: facetObject.jsonPath },\n )\n}\n\nexport function partitionQueryBundleRequestIntoRowsAndMetadata(\n queryBundleRequest: QueryBundleRequest,\n): {\n rowDataRequest: QueryBundleRequest\n queryMetadataRequest: QueryBundleRequest\n} {\n const rowDataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n // Get just the rows (if they were originally requested)\n partMask: queryBundleRequest.partMask & BUNDLE_MASK_QUERY_RESULTS,\n }\n\n const queryMetadataRequest: QueryBundleRequest = {\n ...queryBundleRequest,\n query: {\n ...queryBundleRequest.query,\n // Remove query fields that don't affect the results.\n offset: undefined,\n limit: undefined,\n sort: undefined,\n },\n // Bitwise remove the query result flag from the mask\n partMask: queryBundleRequest.partMask & ~BUNDLE_MASK_QUERY_RESULTS,\n }\n\n return {\n rowDataRequest,\n queryMetadataRequest,\n }\n}\n"],"mappings":";;;;;;AAmCA,IAAa,KAAiB,GAAe,MACvC,KAAQ,OACH,KAGP,GAAQ,eAAe,WAAU,MACxB,EAAG,SAAS,EACnB,IAAI,IAWG,KACX,GACA,MACG;CACH,IAAM,IAAgB,EAAK,aAAa;AACxC,QACE,GAAQ,aAAa,aAAa,QAAQ,WAAU,MAC3C,EAAG,KAAK,aAAa,KAAK,EACjC,IAAI;;AASV,SAAgB,EACd,GACA,IAAgC,EAAE,EACxB;AACV,QAAO,EAAc,QAAQ,GAAgB,GAAM,MAC7C,EAAK,eAAe,IACf,CAAC,GAAG,GAAM,EAAM,GAElB,GACN,EAAE,CAAC;;AAGR,IAAa,KACX,GACA,MAQI,KAAU,QAAQ,KAAiB,QAInC,EAAO,WAAW,KAAK,EAAc,WAAW,IAC3C,KAG+B,EAAO,QAAO,MAElD,CAAC,EAAoB,EAAM,IAC3B,EAAc,MAAK,MAAS,EAAM,SAAS,EAAM,WAAW,CAIzD,CAAgC,SAAS,GAGrC,KAAuB,MAEhC,EAAM,cAAc,iBACpB,EAAM,YAAY,UAAU,KAC5B,EAAM,YAAY,GAAG,SAAS;AAKlC,SAAgB,EACd,GACA,GAC+B;CAC/B,IAAM,IAAmB,GAAc;AACvC,KAAI,KAAoB,GAAM;EAE5B,IAAM,IAA8B,EAAU,EAAK;AAKnD,SADA,EAAS,SAHM,EAAS,QAAQ,QAC9B,MAAQ,EAAK,WAAW,aAAa,KAAK,EAAiB,aAAa,CACzE,EAEM;OAGP,QAAO;;AAQX,SAAgB,EACd,GACA,GACS;CACT,IAAM,IACJ,MAAM,QAAQ,EAAM,eAAe,IACnC,EAAM,eAAe,QACnB,MACE,EAAM,WAAW,aAAa,KAC9B,GAAc,YAAY,aAAa,CAC1C,CAAC,SAAS,GACP,IACJ,MAAM,QAAQ,EAAM,kBAAkB,IACtC,EAAM,kBAAkB,QAAO,MAC7B,EAA+B,EAAY,IAC3C,EAAsC,EAAY,GAC9C,EAAY,WAAW,aAAa,KACpC,GAAc,YAAY,aAAa,GACvC,GACL,CAAC,SAAS;AAEb,QAAO,KAAmB;;AAQ5B,SAAgB,EACd,GACA,GACA;AAWA,QARG,KAAU,EAAa,EAAO,IAAI,CAAC,EAAe,EAAO,IAEzD,KAAU,EAAoB,EAAO,GAG/B,KAGF,GACL,KACG,MACG,EAAa,EAAO,IAAI,EAAW,EAAO,IAAK,EAAU,EAAO;;AAU1E,SAAgB,EACd,GACA,GACA;CACA,IAAM,IAAS,EAAU,EAAS;AAGlC,CAFA,OAAO,EAAO,OACd,OAAO,EAAO,QACd,OAAO,EAAO;CACd,IAAM,IAAS,EAAU,EAAS;AAIlC,QAHA,OAAO,EAAO,OACd,OAAO,EAAO,QACd,OAAO,EAAO,MACP,EAAY,GAAQ,IAAS,GAAQ,MACtC,EAAM,EAAO,IAAI,EAAM,EAAO,GAEzB,KAEF,EAAQ,GAAQ,EAAO,CAC9B;;AAUJ,SAAgB,EAAuB,GAAU;CAC/C,IAAM,IAAQ,EAAU,EAAE;AAqB1B,QAnBI,EAAM,SACR,OAAO,EAAM,QAEX,EAAM,UAAU,QAAQ,EAAM,UAAU,MAC1C,OAAO,EAAM,SAGX,EAAM,QAAQ,QAAQ,EAAM,KAAK,UAAU,MAC7C,OAAO,EAAM,OAGX,EAAM,kBAAkB,QAAQ,EAAM,eAAe,UAAU,MACjE,OAAO,EAAM,iBAGX,EAAM,qBAAqB,QAAQ,EAAM,kBAAkB,UAAU,MACvE,OAAO,EAAM,mBAGR;;AAST,SAAgB,EACd,GACA,GAC8C;CAC9C,IAAI,IACF,EAAa,MAAK,MAAS,EAAM,SAAS,EAAM,WAAW;AAM7D,QALI,EAAM,YAAY,KAAe,EAAY,mBAC/C,IAAc,EAAY,eAAe,MACvC,MAAM,EAAG,aAAa,EAAM,SAC7B,GAEI;;AAQT,SAAgB,EACd,GACA,GACgC;AAChC,QAAO,GAAgB,MAAK,MAC1B,EAA6B,GAAiB,EAAc,CAC7D;;AAGH,SAAgB,EACd,GAIA,GACA;AACA,QAAO,EACL;EACE,YAAY,EAAgB;EAC5B,UAAU,EAAgB;EAC3B,EACD;EAAE,YAAY,EAAY;EAAY,UAAU,EAAY;EAAU,CACvE;;AAGH,SAAgB,EACd,GAIA;AAoBA,QAAO;EACL,gBAAA;GAnBA,GAAG;GAEH,UAAU,EAAmB,WAAA;GAiB7B;EACA,sBAAA;GAdA,GAAG;GACH,OAAO;IACL,GAAG,EAAmB;IAEtB,QAAQ,KAAA;IACR,OAAO,KAAA;IACP,MAAM,KAAA;IACP;GAED,UAAU,EAAmB,WAAW;GAKxC;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"testDownloadSpeed.js","names":[],"sources":["../../../src/utils/functions/testDownloadSpeed.ts"],"sourcesContent":["import {\n getEntity,\n getFileHandleContent,\n getFiles,\n} from '@/synapse-client/SynapseClient'\nimport {\n BatchFileRequest,\n BatchFileResult,\n Entity,\n FileEntity,\n FileHandle,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nconst ESTIMATED_CORS_TIME_MS: number = 200\nconst ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY: string =\n 'ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME'\nconst ESTIMATED_DOWNLOAD_SPEED_KEY: string = 'ESTIMATED_DOWNLOAD_SPEED'\nconst TEST_FILE_ENTITY_ID: string = 'syn12600511'\n/**\n * Return the estimated download speed (bytes/second). Result is cached.\n * Result is crude estimate since it's a single test file (small sample, only ~2MB), but is a valid test (since it's a Synapse file on s3).\n * The intent is to let the user know if the package download will take many hours to download.\n * @param accessToken\n */\nexport const testDownloadSpeed = (accessToken: string): Promise<number> => {\n return new Promise((resolve, reject) => {\n // check cache\n const cachedSpeedExpireTime = localStorage.getItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n )\n const cachedSpeed = localStorage.getItem(ESTIMATED_DOWNLOAD_SPEED_KEY)\n if (cachedSpeedExpireTime && cachedSpeed) {\n // is this value expired?\n const now: number = new Date().getTime()\n if (now < Number(cachedSpeedExpireTime)) {\n resolve(Number(cachedSpeed))\n return\n }\n }\n\n /**\n * 1. Get the test File Entity\n * 2. Get the file handle and presigned URL associated to the latest version of the test File Entity\n * 3. Start the timer and fetch the file content using that presigned URL\n */\n getEntity(accessToken, TEST_FILE_ENTITY_ID)\n .then((entity: Entity) => {\n const fileEntity: FileEntity = entity as FileEntity\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: fileEntity.id!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: fileEntity.dataFileHandleId,\n },\n ]\n const request: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n getFiles(request, accessToken).then((data: BatchFileResult) => {\n const presignedUrl: string = data.requestedFiles[0].preSignedURL!\n // we know this file exists\n const fileHandle: FileHandle = data.requestedFiles[0].fileHandle!\n // start test!\n const startMs = new Date().getTime()\n return getFileHandleContent(fileHandle, presignedUrl).then(() => {\n // console.log(`Transferred ${fileHandle.contentSize/1000000} MB in ${((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS)/1000)} seconds`)\n // bytes/second\n const result =\n fileHandle.contentSize /\n ((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS) / 1000)\n // save result in cache (for 5 minutes)\n const now: number = new Date().getTime()\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n (now + 1000 * 60 * 5).toString(),\n )\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_KEY,\n result.toString(),\n )\n resolve(result)\n })\n })\n })\n .catch(err => reject(err as Error))\n })\n}\n"],"mappings":";;;AAeA,IAAM,IAAiC,KACjC,IACJ,wCACI,IAAuC,4BACvC,IAA8B,eAOvB,KAAqB,MACzB,IAAI,SAAS,GAAS,MAAW;CAEtC,IAAM,IAAwB,aAAa,QACzC,EACD,EACK,IAAc,aAAa,QAAQ,EAA6B;AACtE,KAAI,KAAyB,sBAEP,IAAI,MAAM,EAAC,SAAS,GAC9B,OAAO,EAAsB,EAAE;AACvC,IAAQ,OAAO,EAAY,CAAC;AAC5B;;AASJ,GAAU,GAAa,EAAoB,CACxC,MAAM,MAAmB;EACxB,IAAM,IAAyB;AAc/B,IANkC;GAChC,oBAAoB;GACpB,sBAAsB;GACtB,6BAA6B;GAC7B,gBAXyD,CACzD;IACE,mBAAmB,EAAW;IAC9B,qBAAqB,EAAwB;IAC7C,cAAc,EAAW;IAC1B,CACF;GAMA,EACiB,EAAY,CAAC,MAAM,MAA0B;GAC7D,IAAM,IAAuB,EAAK,eAAe,GAAG,cAE9C,IAAyB,EAAK,eAAe,GAAG,YAEhD,qBAAU,IAAI,MAAM,EAAC,SAAS;AACpC,UAAO,EAAqB,GAAY,EAAa,CAAC,WAAW;IAG/D,IAAM,IACJ,EAAW,iCACT,IAAI,MAAM,EAAC,SAAS,GAAG,IAAU,KAA0B,MAEzD,qBAAc,IAAI,MAAM,EAAC,SAAS;AASxC,IARA,aAAa,QACX,IACC,IAAM,MAAO,KAAK,GAAG,UAAU,CACjC,EACD,aAAa,QACX,GACA,EAAO,UAAU,CAClB,EACD,EAAQ,EAAO;KACf;IACF;GACF,CACD,OAAM,MAAO,EAAO,EAAa,CAAC;EACrC"}
1
+ {"version":3,"file":"testDownloadSpeed.js","names":[],"sources":["../../../src/utils/functions/testDownloadSpeed.ts"],"sourcesContent":["import {\n getEntity,\n getFileHandleContent,\n getFiles,\n} from '@/synapse-client/SynapseClient'\nimport {\n BatchFileRequest,\n BatchFileResult,\n Entity,\n FileEntity,\n FileHandle,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nconst ESTIMATED_CORS_TIME_MS: number = 200\nconst ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY: string =\n 'ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME'\nconst ESTIMATED_DOWNLOAD_SPEED_KEY: string = 'ESTIMATED_DOWNLOAD_SPEED'\nconst TEST_FILE_ENTITY_ID: string = 'syn12600511'\n/**\n * Return the estimated download speed (bytes/second). Result is cached.\n * Result is crude estimate since it's a single test file (small sample, only ~2MB), but is a valid test (since it's a Synapse file on s3).\n * The intent is to let the user know if the package download will take many hours to download.\n * @param accessToken\n */\nexport const testDownloadSpeed = (accessToken: string): Promise<number> => {\n return new Promise((resolve, reject) => {\n // check cache\n const cachedSpeedExpireTime = localStorage.getItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n )\n const cachedSpeed = localStorage.getItem(ESTIMATED_DOWNLOAD_SPEED_KEY)\n if (cachedSpeedExpireTime && cachedSpeed) {\n // is this value expired?\n const now: number = new Date().getTime()\n if (now < Number(cachedSpeedExpireTime)) {\n resolve(Number(cachedSpeed))\n return\n }\n }\n\n /**\n * 1. Get the test File Entity\n * 2. Get the file handle and presigned URL associated to the latest version of the test File Entity\n * 3. Start the timer and fetch the file content using that presigned URL\n */\n getEntity(accessToken, TEST_FILE_ENTITY_ID)\n .then((entity: Entity) => {\n const fileEntity: FileEntity = entity as FileEntity\n const fileHandleAssociationList: FileHandleAssociation[] = [\n {\n associateObjectId: fileEntity.id!,\n associateObjectType: FileHandleAssociateType.FileEntity,\n fileHandleId: fileEntity.dataFileHandleId,\n },\n ]\n const request: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n getFiles(request, accessToken).then((data: BatchFileResult) => {\n const presignedUrl: string = data.requestedFiles[0].preSignedURL!\n // we know this file exists\n const fileHandle: FileHandle = data.requestedFiles[0].fileHandle!\n // start test!\n const startMs = new Date().getTime()\n return getFileHandleContent(fileHandle, presignedUrl).then(() => {\n // console.log(`Transferred ${fileHandle.contentSize/1000000} MB in ${((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS)/1000)} seconds`)\n // bytes/second\n const result =\n fileHandle.contentSize /\n ((new Date().getTime() - startMs - ESTIMATED_CORS_TIME_MS) / 1000)\n // save result in cache (for 5 minutes)\n const now: number = new Date().getTime()\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_EXPIRE_TIME_KEY,\n (now + 1000 * 60 * 5).toString(),\n )\n localStorage.setItem(\n ESTIMATED_DOWNLOAD_SPEED_KEY,\n result.toString(),\n )\n resolve(result)\n })\n })\n })\n .catch(err => reject(err as Error))\n })\n}\n"],"mappings":";;;AAeA,IAAM,IAAiC,KACjC,IACJ,wCACI,IAAuC,4BACvC,IAA8B,eAOvB,KAAqB,MACzB,IAAI,SAAS,GAAS,MAAW;CAEtC,IAAM,IAAwB,aAAa,QACzC,EACD,EACK,IAAc,aAAa,QAAQ,EAA6B;AACtE,KAAI,KAAyB,sBAEP,IAAI,MAAM,EAAC,SAC3B,GAAM,OAAO,EAAsB,EAAE;AACvC,IAAQ,OAAO,EAAY,CAAC;AAC5B;;AASJ,GAAU,GAAa,EAAoB,CACxC,MAAM,MAAmB;EACxB,IAAM,IAAyB;AAc/B,IAAS;GALP,oBAAoB;GACpB,sBAAsB;GACtB,6BAA6B;GAC7B,gBAAgB,CAVhB;IACE,mBAAmB,EAAW;IAC9B,qBAAqB,EAAwB;IAC7C,cAAc,EAAW;IAC1B,CAMe;GAET,EAAS,EAAY,CAAC,MAAM,MAA0B;GAC7D,IAAM,IAAuB,EAAK,eAAe,GAAG,cAE9C,IAAyB,EAAK,eAAe,GAAG,YAEhD,qBAAU,IAAI,MAAM,EAAC,SAAS;AACpC,UAAO,EAAqB,GAAY,EAAa,CAAC,WAAW;IAG/D,IAAM,IACJ,EAAW,iCACT,IAAI,MAAM,EAAC,SAAS,GAAG,IAAU,KAA0B,MAEzD,qBAAc,IAAI,MAAM,EAAC,SAAS;AASxC,IARA,aAAa,QACX,IACC,IAAM,MAAO,KAAK,GAAG,UAAU,CACjC,EACD,aAAa,QACX,GACA,EAAO,UAAU,CAClB,EACD,EAAQ,EAAO;KACf;IACF;GACF,CACD,OAAM,MAAO,EAAO,EAAa,CAAC;EACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useConfirmItems.js","names":[],"sources":["../../../src/utils/hooks/useConfirmItems.ts"],"sourcesContent":["import { useReducer } from 'react'\n\ntype UseConfirmItemsReturn<T = unknown> = {\n /** The set of items that are awaiting confirmation */\n pendingItems: T[]\n /** The set of items that have been confirmed */\n confirmedItems: T[]\n /** Adds one or more items to the pending list */\n addItemsPendingConfirmation: (...items: T[]) => void\n /** Confirms one or more items, adding them to the confirmed list and removing them from the pending list\n * @returns the confirmed items */\n confirmItem: (...items: T[]) => { confirmedItems: T[]; pendingItems: T[] }\n /** Remove one or more items that are pending from the list\n * @returns the resulting state of confirmed items and pending items */\n removePendingItems: (...items: T[]) => {\n confirmedItems: T[]\n pendingItems: T[]\n }\n /** Clear all items from the confirmed and pending lists */\n clear: () => void\n}\n\nfunction reducer<T = unknown>(\n state: { pendingItems: T[]; confirmedItems: T[] },\n action:\n | { type: 'addItemsPendingConfirmation'; newItems: T[] }\n | { type: 'confirmItem'; itemsToConfirm: T[] }\n | { type: 'removePendingItems'; itemsToRemove: T[] }\n | { type: 'clear' },\n) {\n switch (action.type) {\n case 'addItemsPendingConfirmation':\n return {\n ...state,\n pendingItems: [...state.pendingItems, ...action.newItems],\n }\n case 'confirmItem': {\n const newConfirmedItems = [\n ...state.confirmedItems,\n ...action.itemsToConfirm,\n ]\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToConfirm.includes(i),\n )\n return {\n confirmedItems: newConfirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'removePendingItems': {\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToRemove.includes(i),\n )\n return {\n confirmedItems: state.confirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'clear':\n return { confirmedItems: [], pendingItems: [] }\n default:\n return state\n }\n}\n\n/**\n * Stateful hook used to track items that need to be confirmed by the user. Methods are provided to support confirming\n * individual items or all items, as well as skipping confirmation for individual items or all items.\n */\nfunction useConfirmItems<T = unknown>(): UseConfirmItemsReturn<T> {\n const [state, dispatch] = useReducer(reducer<T>, {\n pendingItems: [],\n confirmedItems: [],\n })\n\n const addItemsPendingConfirmation = (...items: T[]) => {\n dispatch({ type: 'addItemsPendingConfirmation', newItems: items })\n }\n\n const confirmItem = (...items: T[]) => {\n dispatch({ type: 'confirmItem', itemsToConfirm: items })\n return reducer(state, { type: 'confirmItem', itemsToConfirm: items })\n }\n\n const removePendingItems = (...items: T[]) => {\n dispatch({ type: 'removePendingItems', itemsToRemove: items })\n return reducer(state, { type: 'removePendingItems', itemsToRemove: items })\n }\n\n const clear = () => {\n dispatch({ type: 'clear' })\n }\n\n return {\n pendingItems: state.pendingItems,\n confirmedItems: state.confirmedItems,\n addItemsPendingConfirmation,\n confirmItem,\n removePendingItems,\n clear,\n }\n}\n\nexport default useConfirmItems\n"],"mappings":";;AAsBA,SAAS,EACP,GACA,GAKA;AACA,SAAQ,EAAO,MAAf;EACE,KAAK,8BACH,QAAO;GACL,GAAG;GACH,cAAc,CAAC,GAAG,EAAM,cAAc,GAAG,EAAO,SAAS;GAC1D;EACH,KAAK,cAQH,QAAO;GACL,gBARwB,CACxB,GAAG,EAAM,gBACT,GAAG,EAAO,eACX;GAMC,cALsB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,eAAe,SAAS,EAAE,CACxC;GAIA;EAEH,KAAK,sBAAsB;GACzB,IAAM,IAAkB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,cAAc,SAAS,EAAE,CACvC;AACD,UAAO;IACL,gBAAgB,EAAM;IACtB,cAAc;IACf;;EAEH,KAAK,QACH,QAAO;GAAE,gBAAgB,EAAE;GAAE,cAAc,EAAE;GAAE;EACjD,QACE,QAAO;;;AAQb,SAAS,IAAyD;CAChE,IAAM,CAAC,GAAO,KAAY,EAAW,GAAY;EAC/C,cAAc,EAAE;EAChB,gBAAgB,EAAE;EACnB,CAAC;AAoBF,QAAO;EACL,cAAc,EAAM;EACpB,gBAAgB,EAAM;EACtB,8BArBmC,GAAG,MAAe;AACrD,KAAS;IAAE,MAAM;IAA+B,UAAU;IAAO,CAAC;;EAqBlE,cAlBmB,GAAG,OACtB,EAAS;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC,EACjD,EAAQ,GAAO;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC;EAiBrE,qBAd0B,GAAG,OAC7B,EAAS;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC,EACvD,EAAQ,GAAO;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC;EAa3E,aAVkB;AAClB,KAAS,EAAE,MAAM,SAAS,CAAC;;EAU5B"}
1
+ {"version":3,"file":"useConfirmItems.js","names":[],"sources":["../../../src/utils/hooks/useConfirmItems.ts"],"sourcesContent":["import { useReducer } from 'react'\n\ntype UseConfirmItemsReturn<T = unknown> = {\n /** The set of items that are awaiting confirmation */\n pendingItems: T[]\n /** The set of items that have been confirmed */\n confirmedItems: T[]\n /** Adds one or more items to the pending list */\n addItemsPendingConfirmation: (...items: T[]) => void\n /** Confirms one or more items, adding them to the confirmed list and removing them from the pending list\n * @returns the confirmed items */\n confirmItem: (...items: T[]) => { confirmedItems: T[]; pendingItems: T[] }\n /** Remove one or more items that are pending from the list\n * @returns the resulting state of confirmed items and pending items */\n removePendingItems: (...items: T[]) => {\n confirmedItems: T[]\n pendingItems: T[]\n }\n /** Clear all items from the confirmed and pending lists */\n clear: () => void\n}\n\nfunction reducer<T = unknown>(\n state: { pendingItems: T[]; confirmedItems: T[] },\n action:\n | { type: 'addItemsPendingConfirmation'; newItems: T[] }\n | { type: 'confirmItem'; itemsToConfirm: T[] }\n | { type: 'removePendingItems'; itemsToRemove: T[] }\n | { type: 'clear' },\n) {\n switch (action.type) {\n case 'addItemsPendingConfirmation':\n return {\n ...state,\n pendingItems: [...state.pendingItems, ...action.newItems],\n }\n case 'confirmItem': {\n const newConfirmedItems = [\n ...state.confirmedItems,\n ...action.itemsToConfirm,\n ]\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToConfirm.includes(i),\n )\n return {\n confirmedItems: newConfirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'removePendingItems': {\n const newPendingItems = state.pendingItems.filter(\n i => !action.itemsToRemove.includes(i),\n )\n return {\n confirmedItems: state.confirmedItems,\n pendingItems: newPendingItems,\n }\n }\n case 'clear':\n return { confirmedItems: [], pendingItems: [] }\n default:\n return state\n }\n}\n\n/**\n * Stateful hook used to track items that need to be confirmed by the user. Methods are provided to support confirming\n * individual items or all items, as well as skipping confirmation for individual items or all items.\n */\nfunction useConfirmItems<T = unknown>(): UseConfirmItemsReturn<T> {\n const [state, dispatch] = useReducer(reducer<T>, {\n pendingItems: [],\n confirmedItems: [],\n })\n\n const addItemsPendingConfirmation = (...items: T[]) => {\n dispatch({ type: 'addItemsPendingConfirmation', newItems: items })\n }\n\n const confirmItem = (...items: T[]) => {\n dispatch({ type: 'confirmItem', itemsToConfirm: items })\n return reducer(state, { type: 'confirmItem', itemsToConfirm: items })\n }\n\n const removePendingItems = (...items: T[]) => {\n dispatch({ type: 'removePendingItems', itemsToRemove: items })\n return reducer(state, { type: 'removePendingItems', itemsToRemove: items })\n }\n\n const clear = () => {\n dispatch({ type: 'clear' })\n }\n\n return {\n pendingItems: state.pendingItems,\n confirmedItems: state.confirmedItems,\n addItemsPendingConfirmation,\n confirmItem,\n removePendingItems,\n clear,\n }\n}\n\nexport default useConfirmItems\n"],"mappings":";;AAsBA,SAAS,EACP,GACA,GAKA;AACA,SAAQ,EAAO,MAAf;EACE,KAAK,8BACH,QAAO;GACL,GAAG;GACH,cAAc,CAAC,GAAG,EAAM,cAAc,GAAG,EAAO,SAAS;GAC1D;EACH,KAAK,cAQH,QAAO;GACL,gBAAgB,CAPhB,GAAG,EAAM,gBACT,GAAG,EAAO,eAMM;GAChB,cALsB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,eAAe,SAAS,EAAE,CAIzB;GACf;EAEH,KAAK,sBAAsB;GACzB,IAAM,IAAkB,EAAM,aAAa,QACzC,MAAK,CAAC,EAAO,cAAc,SAAS,EAAE,CACvC;AACD,UAAO;IACL,gBAAgB,EAAM;IACtB,cAAc;IACf;;EAEH,KAAK,QACH,QAAO;GAAE,gBAAgB,EAAE;GAAE,cAAc,EAAE;GAAE;EACjD,QACE,QAAO;;;AAQb,SAAS,IAAyD;CAChE,IAAM,CAAC,GAAO,KAAY,EAAW,GAAY;EAC/C,cAAc,EAAE;EAChB,gBAAgB,EAAE;EACnB,CAAC;AAoBF,QAAO;EACL,cAAc,EAAM;EACpB,gBAAgB,EAAM;EACtB,8BArBmC,GAAG,MAAe;AACrD,KAAS;IAAE,MAAM;IAA+B,UAAU;IAAO,CAAC;;EAqBlE,cAlBmB,GAAG,OACtB,EAAS;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC,EACjD,EAAQ,GAAO;GAAE,MAAM;GAAe,gBAAgB;GAAO,CAAC;EAiBrE,qBAd0B,GAAG,OAC7B,EAAS;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC,EACvD,EAAQ,GAAO;GAAE,MAAM;GAAsB,eAAe;GAAO,CAAC;EAa3E,aAVkB;AAClB,KAAS,EAAE,MAAM,SAAS,CAAC;;EAU5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCookiePreferences.js","names":[],"sources":["../../../src/utils/hooks/useCookiePreferences.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport { atom, useAtom } from 'jotai'\nimport UniversalCookies from 'universal-cookie'\n\nexport type CookiePreference = {\n functionalAllowed: boolean\n analyticsAllowed: boolean\n}\n\nexport const allowAll: CookiePreference = {\n functionalAllowed: true,\n analyticsAllowed: true,\n}\nexport const allowNone: CookiePreference = {\n functionalAllowed: false,\n analyticsAllowed: false,\n}\n\nexport const COOKIES_AGREEMENT_COOKIE_KEY =\n 'org.sagebionetworks.security.cookies.portal.preference'\n\n/**\n * Read the current cookie preferences from the browser cookie jar.\n * This must only be called in a browser environment (not during SSR).\n */\nexport const getCurrentCookiePreferences = (): CookiePreference => {\n const cookies = new UniversalCookies()\n const prefs = cookies.get(COOKIES_AGREEMENT_COOKIE_KEY, {\n doNotParse: true,\n }) as string | undefined\n let cookiePreference = allowNone\n try {\n if (prefs != undefined) {\n cookiePreference = JSON.parse(prefs) as CookiePreference\n }\n } catch (err) {\n console.error(\n `Failed to parse CookiePreference from value, falling back to allow none. value=${prefs}`,\n )\n }\n return cookiePreference\n}\n\n// Initialize with allowNone so the atom is SSR-safe. The actual cookie value\n// is synced on the client via useEffect inside useCookiePreferences.\nconst cookiePreferencesAtom = atom<CookiePreference>(allowNone)\n\nexport const useCookiePreferences = (): [\n CookiePreference,\n (pref: CookiePreference) => void,\n] => {\n const [cookiePreferences, setCookiePreferencesAtomValue] = useAtom(\n cookiePreferencesAtom,\n )\n\n // On first client mount, sync the atom with the actual cookie value.\n const hasSyncedRef = useRef(false)\n useEffect(() => {\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n const current = getCurrentCookiePreferences()\n setCookiePreferencesAtomValue(current)\n }\n }, [setCookiePreferencesAtomValue])\n\n const setCookiePreferences = useCallback(\n (prefs: CookiePreference) => {\n if (!prefs.functionalAllowed) {\n localStorage.clear()\n sessionStorage.clear()\n }\n\n const current = new Date()\n const nextYear = new Date()\n nextYear.setFullYear(current.getFullYear() + 1)\n const hostname = window.location.hostname.toLowerCase()\n const cookies = new UniversalCookies()\n cookies.set(COOKIES_AGREEMENT_COOKIE_KEY, prefs, {\n path: '/',\n expires: nextYear,\n domain: hostname.endsWith('.synapse.org') ? 'synapse.org' : undefined,\n })\n\n setCookiePreferencesAtomValue(prefs)\n },\n [setCookiePreferencesAtomValue],\n )\n return [cookiePreferences, setCookiePreferences]\n}\n"],"mappings":";;;;AASA,IAAa,IAA6B;CACxC,mBAAmB;CACnB,kBAAkB;CACnB,EACY,IAA8B;CACzC,mBAAmB;CACnB,kBAAkB;CACnB,EAEY,IACX,0DAMW,UAAsD;CAEjE,IAAM,IADU,IAAI,GAAkB,CAChB,IAAI,GAA8B,EACtD,YAAY,IACb,CAAC,EACE,IAAmB;AACvB,KAAI;AACF,EAAI,KAAS,SACX,IAAmB,KAAK,MAAM,EAAM;SAE1B;AACZ,UAAQ,MACN,kFAAkF,IACnF;;AAEH,QAAO;GAKH,IAAwB,EAAuB,EAAU,EAElD,UAGR;CACH,IAAM,CAAC,GAAmB,KAAiC,EACzD,EACD,EAGK,IAAe,EAAO,GAAM;AA+BlC,QA9BA,QAAgB;AACd,EAAK,EAAa,YAChB,EAAa,UAAU,IAEvB,EADgB,GAA6B,CACP;IAEvC,CAAC,EAA8B,CAAC,EAwB5B,CAAC,GAtBqB,GAC1B,MAA4B;AAC3B,EAAK,EAAM,sBACT,aAAa,OAAO,EACpB,eAAe,OAAO;EAGxB,IAAM,oBAAU,IAAI,MAAM,EACpB,oBAAW,IAAI,MAAM;AAC3B,IAAS,YAAY,EAAQ,aAAa,GAAG,EAAE;EAC/C,IAAM,IAAW,OAAO,SAAS,SAAS,aAAa;AAQvD,EAPgB,IAAI,GAAkB,CAC9B,IAAI,GAA8B,GAAO;GAC/C,MAAM;GACN,SAAS;GACT,QAAQ,EAAS,SAAS,eAAe,GAAG,gBAAgB,KAAA;GAC7D,CAAC,EAEF,EAA8B,EAAM;IAEtC,CAAC,EAA8B,CAChC,CAC+C"}
1
+ {"version":3,"file":"useCookiePreferences.js","names":[],"sources":["../../../src/utils/hooks/useCookiePreferences.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport { atom, useAtom } from 'jotai'\nimport UniversalCookies from 'universal-cookie'\n\nexport type CookiePreference = {\n functionalAllowed: boolean\n analyticsAllowed: boolean\n}\n\nexport const allowAll: CookiePreference = {\n functionalAllowed: true,\n analyticsAllowed: true,\n}\nexport const allowNone: CookiePreference = {\n functionalAllowed: false,\n analyticsAllowed: false,\n}\n\nexport const COOKIES_AGREEMENT_COOKIE_KEY =\n 'org.sagebionetworks.security.cookies.portal.preference'\n\n/**\n * Read the current cookie preferences from the browser cookie jar.\n * This must only be called in a browser environment (not during SSR).\n */\nexport const getCurrentCookiePreferences = (): CookiePreference => {\n const cookies = new UniversalCookies()\n const prefs = cookies.get(COOKIES_AGREEMENT_COOKIE_KEY, {\n doNotParse: true,\n }) as string | undefined\n let cookiePreference = allowNone\n try {\n if (prefs != undefined) {\n cookiePreference = JSON.parse(prefs) as CookiePreference\n }\n } catch (err) {\n console.error(\n `Failed to parse CookiePreference from value, falling back to allow none. value=${prefs}`,\n )\n }\n return cookiePreference\n}\n\n// Initialize with allowNone so the atom is SSR-safe. The actual cookie value\n// is synced on the client via useEffect inside useCookiePreferences.\nconst cookiePreferencesAtom = atom<CookiePreference>(allowNone)\n\nexport const useCookiePreferences = (): [\n CookiePreference,\n (pref: CookiePreference) => void,\n] => {\n const [cookiePreferences, setCookiePreferencesAtomValue] = useAtom(\n cookiePreferencesAtom,\n )\n\n // On first client mount, sync the atom with the actual cookie value.\n const hasSyncedRef = useRef(false)\n useEffect(() => {\n if (!hasSyncedRef.current) {\n hasSyncedRef.current = true\n const current = getCurrentCookiePreferences()\n setCookiePreferencesAtomValue(current)\n }\n }, [setCookiePreferencesAtomValue])\n\n const setCookiePreferences = useCallback(\n (prefs: CookiePreference) => {\n if (!prefs.functionalAllowed) {\n localStorage.clear()\n sessionStorage.clear()\n }\n\n const current = new Date()\n const nextYear = new Date()\n nextYear.setFullYear(current.getFullYear() + 1)\n const hostname = window.location.hostname.toLowerCase()\n const cookies = new UniversalCookies()\n cookies.set(COOKIES_AGREEMENT_COOKIE_KEY, prefs, {\n path: '/',\n expires: nextYear,\n domain: hostname.endsWith('.synapse.org') ? 'synapse.org' : undefined,\n })\n\n setCookiePreferencesAtomValue(prefs)\n },\n [setCookiePreferencesAtomValue],\n )\n return [cookiePreferences, setCookiePreferences]\n}\n"],"mappings":";;;;AASA,IAAa,IAA6B;CACxC,mBAAmB;CACnB,kBAAkB;CACnB,EACY,IAA8B;CACzC,mBAAmB;CACnB,kBAAkB;CACnB,EAEY,IACX,0DAMW,UAAsD;CAEjE,IAAM,IAAQ,IADM,GACN,CAAQ,IAAI,GAA8B,EACtD,YAAY,IACb,CAAC,EACE,IAAmB;AACvB,KAAI;AACF,EAAI,KAAS,SACX,IAAmB,KAAK,MAAM,EAAM;SAE1B;AACZ,UAAQ,MACN,kFAAkF,IACnF;;AAEH,QAAO;GAKH,IAAwB,EAAuB,EAAU,EAElD,UAGR;CACH,IAAM,CAAC,GAAmB,KAAiC,EACzD,EACD,EAGK,IAAe,EAAO,GAAM;AA+BlC,QA9BA,QAAgB;AACd,EAAK,EAAa,YAChB,EAAa,UAAU,IAEvB,EADgB,GACc,CAAQ;IAEvC,CAAC,EAA8B,CAAC,EAwB5B,CAAC,GAtBqB,GAC1B,MAA4B;AAC3B,EAAK,EAAM,sBACT,aAAa,OAAO,EACpB,eAAe,OAAO;EAGxB,IAAM,oBAAU,IAAI,MAAM,EACpB,oBAAW,IAAI,MAAM;AAC3B,IAAS,YAAY,EAAQ,aAAa,GAAG,EAAE;EAC/C,IAAM,IAAW,OAAO,SAAS,SAAS,aAAa;AAQvD,EANA,IADoB,GACpB,CAAQ,IAAI,GAA8B,GAAO;GAC/C,MAAM;GACN,SAAS;GACT,QAAQ,EAAS,SAAS,eAAe,GAAG,gBAAgB,KAAA;GAC7D,CAAC,EAEF,EAA8B,EAAM;IAEtC,CAAC,EAA8B,CAEN,CAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCreateShortUrl.js","names":[],"sources":["../../../src/utils/hooks/useCreateShortUrl.tsx"],"sourcesContent":["import { useMutation, useQueryClient } from '@tanstack/react-query'\n\ninterface UseCreateShortUrlOptions {\n shortIoPublicApiKey?: string\n domain: string\n onSuccess?: (shortUrl: string) => void\n onError?: (error: Error) => void\n}\n\nexport function useCreateShortUrl({\n shortIoPublicApiKey,\n domain,\n onSuccess,\n onError,\n}: UseCreateShortUrlOptions) {\n const queryClient = useQueryClient()\n const currentUrl = typeof window !== 'undefined' ? window.location.href : ''\n // Create a unique cache key based on the current URL to store the short URL\n const cacheKey = ['shortUrl', currentUrl]\n\n // Check if we have a cached short URL\n // This prevents regenerating the same short URL multiple times\n const cachedUrl = queryClient.getQueryData<string>(cacheKey)\n\n return useMutation({\n mutationFn: async () => {\n if (cachedUrl) {\n return cachedUrl\n }\n\n if (!shortIoPublicApiKey) {\n return window.location.href\n } else {\n const response = await fetch('https://api.short.io/links/public', {\n method: 'POST',\n headers: {\n Authorization: shortIoPublicApiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n originalURL: window.location.href,\n domain: domain,\n }),\n })\n if (!response.ok) {\n const responseText = await response.text()\n throw new Error(responseText)\n }\n const jsonResponse = await response.json()\n const shortUrl = jsonResponse.shortURL\n // Store the short URL in the React Query cache so we can reuse it\n queryClient.setQueryData(cacheKey, shortUrl)\n return shortUrl\n }\n },\n onSuccess,\n onError,\n })\n}\n"],"mappings":";;AASA,SAAgB,EAAkB,EAChC,wBACA,WACA,cACA,cAC2B;CAC3B,IAAM,IAAc,GAAgB,EAG9B,IAAW,CAAC,YAFC,OAAO,SAAW,MAAc,OAAO,SAAS,OAAO,GAEjC,EAInC,IAAY,EAAY,aAAqB,EAAS;AAE5D,QAAO,EAAY;EACjB,YAAY,YAAY;AACtB,OAAI,EACF,QAAO;AAGT,OAAK,GAEE;IACL,IAAM,IAAW,MAAM,MAAM,qCAAqC;KAChE,QAAQ;KACR,SAAS;MACP,eAAe;MACf,gBAAgB;MAChB,QAAQ;MACT;KACD,MAAM,KAAK,UAAU;MACnB,aAAa,OAAO,SAAS;MACrB;MACT,CAAC;KACH,CAAC;AACF,QAAI,CAAC,EAAS,IAAI;KAChB,IAAM,IAAe,MAAM,EAAS,MAAM;AAC1C,WAAU,MAAM,EAAa;;IAG/B,IAAM,KADe,MAAM,EAAS,MAAM,EACZ;AAG9B,WADA,EAAY,aAAa,GAAU,EAAS,EACrC;SAtBP,QAAO,OAAO,SAAS;;EAyB3B;EACA;EACD,CAAC"}
1
+ {"version":3,"file":"useCreateShortUrl.js","names":[],"sources":["../../../src/utils/hooks/useCreateShortUrl.tsx"],"sourcesContent":["import { useMutation, useQueryClient } from '@tanstack/react-query'\n\ninterface UseCreateShortUrlOptions {\n shortIoPublicApiKey?: string\n domain: string\n onSuccess?: (shortUrl: string) => void\n onError?: (error: Error) => void\n}\n\nexport function useCreateShortUrl({\n shortIoPublicApiKey,\n domain,\n onSuccess,\n onError,\n}: UseCreateShortUrlOptions) {\n const queryClient = useQueryClient()\n const currentUrl = typeof window !== 'undefined' ? window.location.href : ''\n // Create a unique cache key based on the current URL to store the short URL\n const cacheKey = ['shortUrl', currentUrl]\n\n // Check if we have a cached short URL\n // This prevents regenerating the same short URL multiple times\n const cachedUrl = queryClient.getQueryData<string>(cacheKey)\n\n return useMutation({\n mutationFn: async () => {\n if (cachedUrl) {\n return cachedUrl\n }\n\n if (!shortIoPublicApiKey) {\n return window.location.href\n } else {\n const response = await fetch('https://api.short.io/links/public', {\n method: 'POST',\n headers: {\n Authorization: shortIoPublicApiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n originalURL: window.location.href,\n domain: domain,\n }),\n })\n if (!response.ok) {\n const responseText = await response.text()\n throw new Error(responseText)\n }\n const jsonResponse = await response.json()\n const shortUrl = jsonResponse.shortURL\n // Store the short URL in the React Query cache so we can reuse it\n queryClient.setQueryData(cacheKey, shortUrl)\n return shortUrl\n }\n },\n onSuccess,\n onError,\n })\n}\n"],"mappings":";;AASA,SAAgB,EAAkB,EAChC,wBACA,WACA,cACA,cAC2B;CAC3B,IAAM,IAAc,GAAgB,EAG9B,IAAW,CAAC,YAFC,OAAO,SAAW,MAAc,OAAO,SAAS,OAAO,GAEjC,EAInC,IAAY,EAAY,aAAqB,EAAS;AAE5D,QAAO,EAAY;EACjB,YAAY,YAAY;AACtB,OAAI,EACF,QAAO;AAGT,OAAK,GAEE;IACL,IAAM,IAAW,MAAM,MAAM,qCAAqC;KAChE,QAAQ;KACR,SAAS;MACP,eAAe;MACf,gBAAgB;MAChB,QAAQ;MACT;KACD,MAAM,KAAK,UAAU;MACnB,aAAa,OAAO,SAAS;MACrB;MACT,CAAC;KACH,CAAC;AACF,QAAI,CAAC,EAAS,IAAI;KAChB,IAAM,IAAe,MAAM,EAAS,MAAM;AAC1C,WAAU,MAAM,EAAa;;IAG/B,IAAM,KAAW,MADU,EAAS,MAAM,EACZ;AAG9B,WADA,EAAY,aAAa,GAAU,EAAS,EACrC;SAtBP,QAAO,OAAO,SAAS;;EAyB3B;EACA;EACD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDetectSSOCode.js","names":[],"sources":["../../../src/utils/hooks/useDetectSSOCode.ts"],"sourcesContent":["import {\n bindOAuthProviderToAccount,\n getRootURL,\n oAuthRegisterAccountStep2,\n oAuthSessionRequest,\n setAccessTokenCookie,\n} from '@/synapse-client'\nimport { OAuth2State } from '@/utils/types'\nimport { TwoFactorAuthErrorResponse } from '@sage-bionetworks/synapse-client/generated/models/TwoFactorAuthErrorResponse'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { LoginResponse } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useMemo, useState } from 'react'\nimport { BackendDestinationEnum } from '../functions'\nimport { CSRF_TOKEN_STORAGE_KEY, OAUTH2_PROVIDERS } from '../SynapseConstants'\nimport { useOneSageURL } from './useOneSageURL'\n\nfunction safeLocalStorageGetItem(key: string): string | null {\n try {\n return localStorage.getItem(key)\n } catch (err) {\n console.warn(`Unable to read from localStorage: ${key}`, err)\n return null\n }\n}\n\nfunction safeLocalStorageRemoveItem(key: string): void {\n try {\n localStorage.removeItem(key)\n } catch (err) {\n console.warn(`Unable to remove from localStorage: ${key}`, err)\n }\n}\n\nexport type UseDetectSSOCodeReturnType = {\n /* true iff SSO login has occurred and the completion of the OAuth flow in Synapse is pending */\n isLoading: boolean\n}\n\nexport type UseDetectSSOCodeOptions = {\n onSignInComplete?: () => void\n registerAccountUrl?: string\n onError?: (err: unknown) => void\n onTwoFactorAuthRequired?: (resp: TwoFactorAuthErrorResponse) => void\n onTwoFactorAuthResetTokenPresent?: (\n resp: TwoFactorAuthErrorResponse,\n encodedTwoFaResetToken: string,\n ) => void\n isInitializingSession: boolean\n isAuthenticated: boolean\n}\n\n/*\n * During SSO login, the authorization provider (Google, ORCiD, ArcusBio Okta, ...) will send the user back to the portal with an authorization code,\n * which can be exchanged for a Synapse user session. This function should be called whenever the root App is initialized\n * (to look for this code parameter and complete the round-trip). If state is included, then we assume that this is being\n * used for account creation, where we pass the username through the process.\n */\nexport default function useDetectSSOCode(\n opts: UseDetectSSOCodeOptions = {\n isInitializingSession: true,\n isAuthenticated: false,\n },\n): UseDetectSSOCodeReturnType {\n const defaultRegisterAccountURL = useOneSageURL('/register1')\n\n const {\n onSignInComplete,\n registerAccountUrl = defaultRegisterAccountURL.toString(),\n onError,\n onTwoFactorAuthRequired,\n onTwoFactorAuthResetTokenPresent,\n isInitializingSession,\n isAuthenticated,\n } = opts\n const redirectURL = getRootURL()\n // 'code' handling (from SSO) should be preformed on the root page, and then redirect to original route.\n // Use 'http://localhost/' as a placeholder during SSR (no browser URL available).\n // Since there is no 'code' or 'provider' in this placeholder URL, the rest of the hook is a no-op.\n const fullUrl: URL = new URL(\n typeof window !== 'undefined' ? window.location.href : 'http://localhost/',\n )\n // in test environment the searchParams isn't defined\n const { searchParams } = fullUrl\n const code = searchParams?.get('code')\n const provider = searchParams?.get('provider')\n\n // If the URL contains a client_id and redirect_uri, then we are acting as an identity provider for an external OAuth client\n const isHandlingSynapseOAuthSignIn = Boolean(\n searchParams?.get('client_id') && searchParams?.get('redirect_uri'),\n )\n\n // If the Synapse user signed in with an external IdP, we may have passed data in the 'state' param\n // Parse it (if appropriate)\n const state: OAuth2State | null = useMemo(() => {\n // If we are acting as an OIDC identity provider, then we should not parse the state param -- it was sent to us, and we should return it untouched\n if (!isHandlingSynapseOAuthSignIn) {\n const encodedState = searchParams?.get('state')\n try {\n return encodedState\n ? (JSON.parse(decodeURIComponent(encodedState)) as OAuth2State)\n : null\n } catch (e) {\n console.error(\n 'Error parsing state param:\\n',\n e,\n '\\nEncoded value:\\n',\n encodedState,\n )\n }\n }\n return null\n }, [isHandlingSynapseOAuthSignIn, searchParams])\n\n const [isLoading, setIsLoading] = useState(!!(code && provider))\n\n useEffect(() => {\n if (!isInitializingSession) {\n if (code && provider) {\n if (!isHandlingSynapseOAuthSignIn) {\n const storedCsrfToken = safeLocalStorageGetItem(\n CSRF_TOKEN_STORAGE_KEY,\n )\n\n const expectedCsrfToken = state?.csrfToken ?? null\n const tokensMatch =\n typeof expectedCsrfToken === 'string' &&\n typeof storedCsrfToken === 'string' &&\n expectedCsrfToken === storedCsrfToken\n\n if (!tokensMatch) {\n safeLocalStorageRemoveItem(CSRF_TOKEN_STORAGE_KEY)\n console.error(\n 'Invalid or missing OAuth CSRF token detected. Aborting OAuth flow.',\n )\n if (onError) {\n onError('Invalid OAuth state. Please try signing in again.')\n }\n setIsLoading(false)\n return\n }\n\n safeLocalStorageRemoveItem(CSRF_TOKEN_STORAGE_KEY)\n }\n\n const redirectUrl = `${redirectURL}?provider=${provider}`\n\n //If user is already logged in, and the provider is ORCID, then try to bind this OAuth provider to the account.\n if (OAUTH2_PROVIDERS.ORCID == provider && isAuthenticated) {\n // now bind this to the user account\n const onFailure = (err: SynapseClientError) => {\n console.error('Error binding ORCiD to account: ', err)\n if (onError) {\n onError(err.reason)\n }\n }\n bindOAuthProviderToAccount(\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSignInComplete)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n } else if (\n OAUTH2_PROVIDERS.GOOGLE == provider ||\n OAUTH2_PROVIDERS.ORCID == provider ||\n OAUTH2_PROVIDERS.ARCUS == provider ||\n OAUTH2_PROVIDERS.SAGE_BIONETWORKS == provider\n ) {\n const onSuccess = (\n response: LoginResponse | TwoFactorAuthErrorResponse | null,\n ) => {\n if (response) {\n if ('accessToken' in response) {\n setAccessTokenCookie(response.accessToken).then(\n onSignInComplete,\n )\n } else {\n // The app will redirect or open a modal to handle a standard 2FA sign in\n if (onTwoFactorAuthRequired) {\n onTwoFactorAuthRequired(response)\n }\n if (\n // The user logged in with OAuth while attempting to disable 2FA using an emailed signed token\n state &&\n state.twoFaResetToken &&\n onTwoFactorAuthResetTokenPresent\n ) {\n // Let the app handle redirecting to the 2FA reset page\n onTwoFactorAuthResetTokenPresent(\n response,\n state.twoFaResetToken,\n )\n }\n }\n }\n }\n const onFailure = (err: SynapseClientError) => {\n if (err.status === 404) {\n // Synapse account not found, send to registration page\n window.location.replace(registerAccountUrl)\n }\n console.error('Error with account login: ', err)\n if (onError) {\n onError(err.reason)\n }\n }\n\n if (\n (OAUTH2_PROVIDERS.GOOGLE == provider ||\n OAUTH2_PROVIDERS.ARCUS == provider ||\n OAUTH2_PROVIDERS.SAGE_BIONETWORKS == provider) &&\n state?.registrationUsername\n ) {\n oAuthRegisterAccountStep2(\n state.registrationUsername,\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSuccess)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n } else {\n oAuthSessionRequest(\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSuccess)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n }\n } else {\n console.warn('Unknown SSO Provider: ', provider)\n setIsLoading(false)\n }\n }\n }\n // Intentionally only monitoring initialization of the session -- only running on mount after the session detection has completed since this uses URL params that come from a redirect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isInitializingSession])\n\n return { isLoading }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useDetectSSOCode.js","names":[],"sources":["../../../src/utils/hooks/useDetectSSOCode.ts"],"sourcesContent":["import {\n bindOAuthProviderToAccount,\n getRootURL,\n oAuthRegisterAccountStep2,\n oAuthSessionRequest,\n setAccessTokenCookie,\n} from '@/synapse-client'\nimport { OAuth2State } from '@/utils/types'\nimport { TwoFactorAuthErrorResponse } from '@sage-bionetworks/synapse-client/generated/models/TwoFactorAuthErrorResponse'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { LoginResponse } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useMemo, useState } from 'react'\nimport { BackendDestinationEnum } from '../functions'\nimport { CSRF_TOKEN_STORAGE_KEY, OAUTH2_PROVIDERS } from '../SynapseConstants'\nimport { useOneSageURL } from './useOneSageURL'\n\nfunction safeLocalStorageGetItem(key: string): string | null {\n try {\n return localStorage.getItem(key)\n } catch (err) {\n console.warn(`Unable to read from localStorage: ${key}`, err)\n return null\n }\n}\n\nfunction safeLocalStorageRemoveItem(key: string): void {\n try {\n localStorage.removeItem(key)\n } catch (err) {\n console.warn(`Unable to remove from localStorage: ${key}`, err)\n }\n}\n\nexport type UseDetectSSOCodeReturnType = {\n /* true iff SSO login has occurred and the completion of the OAuth flow in Synapse is pending */\n isLoading: boolean\n}\n\nexport type UseDetectSSOCodeOptions = {\n onSignInComplete?: () => void\n registerAccountUrl?: string\n onError?: (err: unknown) => void\n onTwoFactorAuthRequired?: (resp: TwoFactorAuthErrorResponse) => void\n onTwoFactorAuthResetTokenPresent?: (\n resp: TwoFactorAuthErrorResponse,\n encodedTwoFaResetToken: string,\n ) => void\n isInitializingSession: boolean\n isAuthenticated: boolean\n}\n\n/*\n * During SSO login, the authorization provider (Google, ORCiD, ArcusBio Okta, ...) will send the user back to the portal with an authorization code,\n * which can be exchanged for a Synapse user session. This function should be called whenever the root App is initialized\n * (to look for this code parameter and complete the round-trip). If state is included, then we assume that this is being\n * used for account creation, where we pass the username through the process.\n */\nexport default function useDetectSSOCode(\n opts: UseDetectSSOCodeOptions = {\n isInitializingSession: true,\n isAuthenticated: false,\n },\n): UseDetectSSOCodeReturnType {\n const defaultRegisterAccountURL = useOneSageURL('/register1')\n\n const {\n onSignInComplete,\n registerAccountUrl = defaultRegisterAccountURL.toString(),\n onError,\n onTwoFactorAuthRequired,\n onTwoFactorAuthResetTokenPresent,\n isInitializingSession,\n isAuthenticated,\n } = opts\n const redirectURL = getRootURL()\n // 'code' handling (from SSO) should be preformed on the root page, and then redirect to original route.\n // Use 'http://localhost/' as a placeholder during SSR (no browser URL available).\n // Since there is no 'code' or 'provider' in this placeholder URL, the rest of the hook is a no-op.\n const fullUrl: URL = new URL(\n typeof window !== 'undefined' ? window.location.href : 'http://localhost/',\n )\n // in test environment the searchParams isn't defined\n const { searchParams } = fullUrl\n const code = searchParams?.get('code')\n const provider = searchParams?.get('provider')\n\n // If the URL contains a client_id and redirect_uri, then we are acting as an identity provider for an external OAuth client\n const isHandlingSynapseOAuthSignIn = Boolean(\n searchParams?.get('client_id') && searchParams?.get('redirect_uri'),\n )\n\n // If the Synapse user signed in with an external IdP, we may have passed data in the 'state' param\n // Parse it (if appropriate)\n const state: OAuth2State | null = useMemo(() => {\n // If we are acting as an OIDC identity provider, then we should not parse the state param -- it was sent to us, and we should return it untouched\n if (!isHandlingSynapseOAuthSignIn) {\n const encodedState = searchParams?.get('state')\n try {\n return encodedState\n ? (JSON.parse(decodeURIComponent(encodedState)) as OAuth2State)\n : null\n } catch (e) {\n console.error(\n 'Error parsing state param:\\n',\n e,\n '\\nEncoded value:\\n',\n encodedState,\n )\n }\n }\n return null\n }, [isHandlingSynapseOAuthSignIn, searchParams])\n\n const [isLoading, setIsLoading] = useState(!!(code && provider))\n\n useEffect(() => {\n if (!isInitializingSession) {\n if (code && provider) {\n if (!isHandlingSynapseOAuthSignIn) {\n const storedCsrfToken = safeLocalStorageGetItem(\n CSRF_TOKEN_STORAGE_KEY,\n )\n\n const expectedCsrfToken = state?.csrfToken ?? null\n const tokensMatch =\n typeof expectedCsrfToken === 'string' &&\n typeof storedCsrfToken === 'string' &&\n expectedCsrfToken === storedCsrfToken\n\n if (!tokensMatch) {\n safeLocalStorageRemoveItem(CSRF_TOKEN_STORAGE_KEY)\n console.error(\n 'Invalid or missing OAuth CSRF token detected. Aborting OAuth flow.',\n )\n if (onError) {\n onError('Invalid OAuth state. Please try signing in again.')\n }\n setIsLoading(false)\n return\n }\n\n safeLocalStorageRemoveItem(CSRF_TOKEN_STORAGE_KEY)\n }\n\n const redirectUrl = `${redirectURL}?provider=${provider}`\n\n //If user is already logged in, and the provider is ORCID, then try to bind this OAuth provider to the account.\n if (OAUTH2_PROVIDERS.ORCID == provider && isAuthenticated) {\n // now bind this to the user account\n const onFailure = (err: SynapseClientError) => {\n console.error('Error binding ORCiD to account: ', err)\n if (onError) {\n onError(err.reason)\n }\n }\n bindOAuthProviderToAccount(\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSignInComplete)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n } else if (\n OAUTH2_PROVIDERS.GOOGLE == provider ||\n OAUTH2_PROVIDERS.ORCID == provider ||\n OAUTH2_PROVIDERS.ARCUS == provider ||\n OAUTH2_PROVIDERS.SAGE_BIONETWORKS == provider\n ) {\n const onSuccess = (\n response: LoginResponse | TwoFactorAuthErrorResponse | null,\n ) => {\n if (response) {\n if ('accessToken' in response) {\n setAccessTokenCookie(response.accessToken).then(\n onSignInComplete,\n )\n } else {\n // The app will redirect or open a modal to handle a standard 2FA sign in\n if (onTwoFactorAuthRequired) {\n onTwoFactorAuthRequired(response)\n }\n if (\n // The user logged in with OAuth while attempting to disable 2FA using an emailed signed token\n state &&\n state.twoFaResetToken &&\n onTwoFactorAuthResetTokenPresent\n ) {\n // Let the app handle redirecting to the 2FA reset page\n onTwoFactorAuthResetTokenPresent(\n response,\n state.twoFaResetToken,\n )\n }\n }\n }\n }\n const onFailure = (err: SynapseClientError) => {\n if (err.status === 404) {\n // Synapse account not found, send to registration page\n window.location.replace(registerAccountUrl)\n }\n console.error('Error with account login: ', err)\n if (onError) {\n onError(err.reason)\n }\n }\n\n if (\n (OAUTH2_PROVIDERS.GOOGLE == provider ||\n OAUTH2_PROVIDERS.ARCUS == provider ||\n OAUTH2_PROVIDERS.SAGE_BIONETWORKS == provider) &&\n state?.registrationUsername\n ) {\n oAuthRegisterAccountStep2(\n state.registrationUsername,\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSuccess)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n } else {\n oAuthSessionRequest(\n provider,\n code,\n redirectUrl,\n BackendDestinationEnum.REPO_ENDPOINT,\n )\n .then(onSuccess)\n .catch(onFailure)\n .finally(() => setIsLoading(false))\n }\n } else {\n console.warn('Unknown SSO Provider: ', provider)\n setIsLoading(false)\n }\n }\n }\n // Intentionally only monitoring initialization of the session -- only running on mount after the session detection has completed since this uses URL params that come from a redirect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isInitializingSession])\n\n return { isLoading }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDirectDownloadHandler.js","names":[],"sources":["../../../src/utils/hooks/useDirectDownloadHandler.tsx"],"sourcesContent":["import { getFiles } from '@/synapse-client'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport UAParser from 'ua-parser-js'\n\ntype DirectDownloadHandlerProps = {\n fileHandleId: string\n associatedObjectId: string\n associatedObjectType: FileHandleAssociateType\n}\n\n/**\n * Returns a 'downloadFile' function that fetches a presigned URL for a file\n * handle and opens it in the browser to trigger a direct download.\n *\n * To bypass Safari's popup blocker, this function must be invoked\n * within a user-triggered event handler (ex, inside an onClick callback).\n */\nexport function useDirectDownloadHandler() {\n const { accessToken } = useSynapseContext()\n\n const downloadFile = async ({\n fileHandleId,\n associatedObjectId,\n associatedObjectType,\n }: DirectDownloadHandlerProps) => {\n // SWC-5907: opening in the file must be strictly done in the same click event process (Safari only).\n // https://stackoverflow.com/questions/6628949/window-open-popup-getting-blocked-during-click-event\n const parser = new UAParser()\n const isSafari = parser.getBrowser().name == 'Safari'\n let safariDownloadWindow: Window | null = null\n\n if (isSafari) {\n safariDownloadWindow = window.open(\n '',\n 'Safari Download',\n 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,popup,width=600,height=200',\n )\n safariDownloadWindow!.document.body.innerHTML =\n 'Downloading file on Safari...'\n }\n\n try {\n const fileHandleAssociation: FileHandleAssociation = {\n fileHandleId,\n associateObjectId: associatedObjectId,\n associateObjectType: associatedObjectType,\n }\n\n const batchFileRequest: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: [fileHandleAssociation],\n }\n\n const response = await getFiles(batchFileRequest, accessToken)\n const preSignedURL = response.requestedFiles[0].preSignedURL!\n\n if (!preSignedURL) {\n safariDownloadWindow?.close()\n console.log('Failed to get file download link')\n } else {\n if (isSafari && safariDownloadWindow) {\n safariDownloadWindow.location = preSignedURL\n setTimeout(() => {\n if (safariDownloadWindow) {\n safariDownloadWindow.close()\n }\n }, 10000)\n } else {\n window.open(preSignedURL)\n }\n }\n } catch (e) {\n safariDownloadWindow?.close()\n console.log('Failed to get file download link: ', e)\n }\n }\n return { downloadFile }\n}\n"],"mappings":";;;;;AAsBA,SAAgB,IAA2B;CACzC,IAAM,EAAE,mBAAgB,GAAmB;AA4D3C,QAAO,EAAE,cA1DY,OAAO,EAC1B,iBACA,uBACA,8BACgC;EAIhC,IAAM,IADS,IAAI,GAAU,CACL,YAAY,CAAC,QAAQ,UACzC,IAAsC;AAE1C,EAAI,MACF,IAAuB,OAAO,KAC5B,IACA,mBACA,qHACD,EACD,EAAsB,SAAS,KAAK,YAClC;AAGJ,MAAI;GAeF,IAAM,KADW,MAAM,EAPoB;IACzC,oBAAoB;IACpB,sBAAsB;IACtB,6BAA6B;IAC7B,gBAAgB,CAVmC;KACnD;KACA,mBAAmB;KACnB,qBAAqB;KACtB,CAMwC;IACxC,EAEiD,EAAY,EAChC,eAAe,GAAG;AAEhD,GAAK,IAIC,KAAY,KACd,EAAqB,WAAW,GAChC,iBAAiB;AACf,IAAI,KACF,EAAqB,OAAO;MAE7B,IAAM,IAET,OAAO,KAAK,EAAa,IAX3B,GAAsB,OAAO,EAC7B,QAAQ,IAAI,mCAAmC;WAa1C,GAAG;AAEV,GADA,GAAsB,OAAO,EAC7B,QAAQ,IAAI,sCAAsC,EAAE;;IAGjC"}
1
+ {"version":3,"file":"useDirectDownloadHandler.js","names":[],"sources":["../../../src/utils/hooks/useDirectDownloadHandler.tsx"],"sourcesContent":["import { getFiles } from '@/synapse-client'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport {\n BatchFileRequest,\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\nimport UAParser from 'ua-parser-js'\n\ntype DirectDownloadHandlerProps = {\n fileHandleId: string\n associatedObjectId: string\n associatedObjectType: FileHandleAssociateType\n}\n\n/**\n * Returns a 'downloadFile' function that fetches a presigned URL for a file\n * handle and opens it in the browser to trigger a direct download.\n *\n * To bypass Safari's popup blocker, this function must be invoked\n * within a user-triggered event handler (ex, inside an onClick callback).\n */\nexport function useDirectDownloadHandler() {\n const { accessToken } = useSynapseContext()\n\n const downloadFile = async ({\n fileHandleId,\n associatedObjectId,\n associatedObjectType,\n }: DirectDownloadHandlerProps) => {\n // SWC-5907: opening in the file must be strictly done in the same click event process (Safari only).\n // https://stackoverflow.com/questions/6628949/window-open-popup-getting-blocked-during-click-event\n const parser = new UAParser()\n const isSafari = parser.getBrowser().name == 'Safari'\n let safariDownloadWindow: Window | null = null\n\n if (isSafari) {\n safariDownloadWindow = window.open(\n '',\n 'Safari Download',\n 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,popup,width=600,height=200',\n )\n safariDownloadWindow!.document.body.innerHTML =\n 'Downloading file on Safari...'\n }\n\n try {\n const fileHandleAssociation: FileHandleAssociation = {\n fileHandleId,\n associateObjectId: associatedObjectId,\n associateObjectType: associatedObjectType,\n }\n\n const batchFileRequest: BatchFileRequest = {\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: [fileHandleAssociation],\n }\n\n const response = await getFiles(batchFileRequest, accessToken)\n const preSignedURL = response.requestedFiles[0].preSignedURL!\n\n if (!preSignedURL) {\n safariDownloadWindow?.close()\n console.log('Failed to get file download link')\n } else {\n if (isSafari && safariDownloadWindow) {\n safariDownloadWindow.location = preSignedURL\n setTimeout(() => {\n if (safariDownloadWindow) {\n safariDownloadWindow.close()\n }\n }, 10000)\n } else {\n window.open(preSignedURL)\n }\n }\n } catch (e) {\n safariDownloadWindow?.close()\n console.log('Failed to get file download link: ', e)\n }\n }\n return { downloadFile }\n}\n"],"mappings":";;;;;AAsBA,SAAgB,IAA2B;CACzC,IAAM,EAAE,mBAAgB,GAAmB;AA4D3C,QAAO,EAAE,qBA1DmB,EAC1B,iBACA,uBACA,8BACgC;EAIhC,IAAM,IAAW,IADE,GACF,CAAO,YAAY,CAAC,QAAQ,UACzC,IAAsC;AAE1C,EAAI,MACF,IAAuB,OAAO,KAC5B,IACA,mBACA,qHACD,EACD,EAAsB,SAAS,KAAK,YAClC;AAGJ,MAAI;GAeF,IAAM,KAAe,MADE,EAAS;IAN9B,oBAAoB;IACpB,sBAAsB;IACtB,6BAA6B;IAC7B,gBAAgB,CAAC;KATjB;KACA,mBAAmB;KACnB,qBAAqB;KAOJ,CAAsB;IAGT,EAAkB,EAAY,EAChC,eAAe,GAAG;AAEhD,GAAK,IAIC,KAAY,KACd,EAAqB,WAAW,GAChC,iBAAiB;AACf,IAAI,KACF,EAAqB,OAAO;MAE7B,IAAM,IAET,OAAO,KAAK,EAAa,IAX3B,GAAsB,OAAO,EAC7B,QAAQ,IAAI,mCAAmC;WAa1C,GAAG;AAEV,GADA,GAAsB,OAAO,EAC7B,QAAQ,IAAI,sCAAsC,EAAE;;IAGjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGetGoalData.js","names":[],"sources":["../../../src/utils/hooks/useGetGoalData.ts"],"sourcesContent":["import { getFiles } from '@/synapse-client/SynapseClient'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { getFieldIndex } from '@/utils/functions/queryUtils'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport {\n BatchFileRequest,\n FileHandleAssociateType,\n FileHandleAssociation,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\n\nexport default function useGetGoalData(\n entityId: string,\n queryResultBundle: QueryResultBundle | undefined,\n) {\n const [assets, setAssets] = useState<string[] | undefined>()\n const [error, setError] = useState<string | SynapseClientError | undefined>()\n const { accessToken } = useSynapseContext()\n\n enum ExpectedColumns {\n TABLEID = 'TableId',\n COUNT_SQL = 'CountSql',\n TITLE = 'Title',\n SUMMARY = 'Summary',\n LINK = 'Link',\n ASSET = 'Asset',\n }\n\n useEffect(() => {\n const getData = async () => {\n try {\n const assetColumnIndex = getFieldIndex(\n ExpectedColumns.ASSET,\n queryResultBundle,\n )\n const fileHandle =\n (queryResultBundle?.queryResult!.queryResults.rows.map(\n row => row.values[assetColumnIndex],\n ) ?? []) as string[]\n const assetFileHandleIds = fileHandle.filter(\n v => v != null && v !== undefined,\n )\n if (assetFileHandleIds.length === 0) {\n setAssets(undefined)\n setError(undefined)\n return\n }\n const fileHandleAssociationList: FileHandleAssociation[] =\n assetFileHandleIds.map(fileId => {\n return {\n associateObjectId: entityId,\n associateObjectType: FileHandleAssociateType.TableEntity,\n fileHandleId: fileId,\n }\n })\n const batchFileRequest: BatchFileRequest = {\n includeFileHandles: false,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n const files = await getFiles(batchFileRequest, accessToken)\n setError(undefined)\n const outFiles = files.requestedFiles\n .filter(row => row.preSignedURL !== undefined)\n .map(row => row.preSignedURL!)\n setAssets(outFiles)\n } catch (e) {\n setError(e)\n setAssets(undefined)\n }\n }\n getData()\n }, [entityId, queryResultBundle, accessToken])\n\n return { assets, error }\n}\n"],"mappings":";;;;;;AAYA,SAAwB,EACtB,GACA,GACA;CACA,IAAM,CAAC,GAAQ,KAAa,GAAgC,EACtD,CAAC,GAAO,KAAY,GAAmD,EACvE,EAAE,mBAAgB,GAAmB,EAEtC,IAAL,yBAAA,GAAA;SACE,EAAA,UAAA,WACA,EAAA,YAAA,YACA,EAAA,QAAA,SACA,EAAA,UAAA,WACA,EAAA,OAAA,QACA,EAAA,QAAA;MACD;AAiDD,QA/CA,QAAgB;AA4Cd,GA3CgB,YAAY;AAC1B,OAAI;IACF,IAAM,IAAmB,EACvB,EAAgB,OAChB,EACD,EAKK,KAHH,GAAmB,YAAa,aAAa,KAAK,KACjD,MAAO,EAAI,OAAO,GACnB,IAAI,EAAE,EAC6B,QACpC,MAAK,KAAK,QAAQ,MAAM,KAAA,EACzB;AACD,QAAI,EAAmB,WAAW,GAAG;AAEnC,KADA,EAAU,KAAA,EAAU,EACpB,EAAS,KAAA,EAAU;AACnB;;IAgBF,IAAM,IAAQ,MAAM,EANuB;KACzC,oBAAoB;KACpB,sBAAsB;KACtB,6BAA6B;KAC7B,gBAXA,EAAmB,KAAI,OACd;MACL,mBAAmB;MACnB,qBAAqB,EAAwB;MAC7C,cAAc;MACf,EACD;KAMH,EAC8C,EAAY;AAK3D,IAJA,EAAS,KAAA,EAAU,EAInB,EAHiB,EAAM,eACpB,QAAO,MAAO,EAAI,iBAAiB,KAAA,EAAU,CAC7C,KAAI,MAAO,EAAI,aAAc,CACb;YACZ,GAAG;AAEV,IADA,EAAS,EAAE,EACX,EAAU,KAAA,EAAU;;MAGf;IACR;EAAC;EAAU;EAAmB;EAAY,CAAC,EAEvC;EAAE;EAAQ;EAAO"}
1
+ {"version":3,"file":"useGetGoalData.js","names":[],"sources":["../../../src/utils/hooks/useGetGoalData.ts"],"sourcesContent":["import { getFiles } from '@/synapse-client/SynapseClient'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { getFieldIndex } from '@/utils/functions/queryUtils'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport {\n BatchFileRequest,\n FileHandleAssociateType,\n FileHandleAssociation,\n QueryResultBundle,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\n\nexport default function useGetGoalData(\n entityId: string,\n queryResultBundle: QueryResultBundle | undefined,\n) {\n const [assets, setAssets] = useState<string[] | undefined>()\n const [error, setError] = useState<string | SynapseClientError | undefined>()\n const { accessToken } = useSynapseContext()\n\n enum ExpectedColumns {\n TABLEID = 'TableId',\n COUNT_SQL = 'CountSql',\n TITLE = 'Title',\n SUMMARY = 'Summary',\n LINK = 'Link',\n ASSET = 'Asset',\n }\n\n useEffect(() => {\n const getData = async () => {\n try {\n const assetColumnIndex = getFieldIndex(\n ExpectedColumns.ASSET,\n queryResultBundle,\n )\n const fileHandle =\n (queryResultBundle?.queryResult!.queryResults.rows.map(\n row => row.values[assetColumnIndex],\n ) ?? []) as string[]\n const assetFileHandleIds = fileHandle.filter(\n v => v != null && v !== undefined,\n )\n if (assetFileHandleIds.length === 0) {\n setAssets(undefined)\n setError(undefined)\n return\n }\n const fileHandleAssociationList: FileHandleAssociation[] =\n assetFileHandleIds.map(fileId => {\n return {\n associateObjectId: entityId,\n associateObjectType: FileHandleAssociateType.TableEntity,\n fileHandleId: fileId,\n }\n })\n const batchFileRequest: BatchFileRequest = {\n includeFileHandles: false,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n requestedFiles: fileHandleAssociationList,\n }\n const files = await getFiles(batchFileRequest, accessToken)\n setError(undefined)\n const outFiles = files.requestedFiles\n .filter(row => row.preSignedURL !== undefined)\n .map(row => row.preSignedURL!)\n setAssets(outFiles)\n } catch (e) {\n setError(e)\n setAssets(undefined)\n }\n }\n getData()\n }, [entityId, queryResultBundle, accessToken])\n\n return { assets, error }\n}\n"],"mappings":";;;;;;AAYA,SAAwB,EACtB,GACA,GACA;CACA,IAAM,CAAC,GAAQ,KAAa,GAAgC,EACtD,CAAC,GAAO,KAAY,GAAmD,EACvE,EAAE,mBAAgB,GAAmB,EAEtC,IAAL,yBAAA,GAAA;SACE,EAAA,UAAU,WACV,EAAA,YAAY,YACZ,EAAA,QAAQ,SACR,EAAA,UAAU,WACV,EAAA,OAAO,QACP,EAAA,QAAQ;MACT;AAiDD,QA/CA,QAAgB;AA4Cd,eA3C4B;AAC1B,OAAI;IACF,IAAM,IAAmB,EACvB,EAAgB,OAChB,EACD,EAKK,KAHH,GAAmB,YAAa,aAAa,KAAK,KACjD,MAAO,EAAI,OAAO,GACnB,IAAI,EAAE,EAC6B,QACpC,MAAK,KAAK,QAAQ,MAAM,KAAA,EACzB;AACD,QAAI,EAAmB,WAAW,GAAG;AAEnC,KADA,EAAU,KAAA,EAAU,EACpB,EAAS,KAAA,EAAU;AACnB;;IAgBF,IAAM,IAAQ,MAAM,EAAS;KAL3B,oBAAoB;KACpB,sBAAsB;KACtB,6BAA6B;KAC7B,gBAXA,EAAmB,KAAI,OACd;MACL,mBAAmB;MACnB,qBAAqB,EAAwB;MAC7C,cAAc;MACf,EAMa;KAEW,EAAkB,EAAY;AAK3D,IAJA,EAAS,KAAA,EAAU,EAInB,EAHiB,EAAM,eACpB,QAAO,MAAO,EAAI,iBAAiB,KAAA,EAAU,CAC7C,KAAI,MAAO,EAAI,aACR,CAAS;YACZ,GAAG;AAEV,IADA,EAAS,EAAE,EACX,EAAU,KAAA,EAAU;;MAGf;IACR;EAAC;EAAU;EAAmB;EAAY,CAAC,EAEvC;EAAE;EAAQ;EAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGetInfoFromIds.js","names":[],"sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"sourcesContent":["import {\n getEntityHeaders,\n getEvaluations,\n getGroupHeadersBatch,\n} from '@/synapse-client'\nimport {\n EntityHeader,\n Evaluation,\n Reference,\n ReferenceList,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { chunk, uniq, without } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport { useSynapseContext } from '../context/SynapseContext'\nimport { SynapseConstants } from '../index'\n\nexport type UseGetInfoFromIdsProps<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n THookType = T extends EntityHeader\n ? 'ENTITY_HEADER'\n : T extends UserGroupHeader\n ? 'USER_PROFILE'\n : T extends Evaluation\n ? 'EVALUATION_QUEUE'\n : never,\n> = {\n ids: string[]\n type: THookType\n}\n\ntype LookupRequestType = string | Reference\n\nconst UserGroupHeaderTemplate: UserGroupHeader = {\n ownerId: '', // A foreign key to the ID of the 'principal' object for the user\n firstName: 'Unknown', // This person's given name (forename)\n lastName: 'Unknown', // This person's family name (surname)\n userName: 'Unknown', // A name chosen by the user that uniquely identifies them\n isIndividual: false,\n}\n\nconst entityHeaderTemplate: EntityHeader = {\n name: 'Unknown', //\tThe name of the entity\n id: 'unknown', //\tThe id of the entity\n type: 'org.sagebionetworks.repo.model.FileEntity', //\tThe type of the entity\n versionNumber: 0, //\tThe version number of the entity\n versionLabel: 'placeholder', //\tThe user defined version label of the entity\n benefactorId: 0, //\tThe ID of the entity that this Entity's ACL is inherited from.\n createdOn: 'null', //\tThe date this entity was created.\n modifiedOn: 'null', //\tThe date this entity was last modified.\n createdBy: 'null', //\tThe ID of the user that created this entity.\n modifiedBy: 'null', //\tThe ID of the user that last modified this entity.\n isLatestVersion: true, // If this version is the latest version of the entity\n}\n\nconst evaluationTemplate: Evaluation = {\n id: 'unknown', // The unique immutable ID for this Evaluation.\n etag: 'Unknown', // Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. The eTag changes every time an Evaluation is updated; it is used to detect when a client's copy of an Evaluation is out-of-date.\n name: 'Unknown', // The name of this Evaluation\n description: 'Unknown', // A text description of this Evaluation.\n ownerId: 'null', // The ID of the Synapse user who created this Evaluation.\n createdOn: 'null', //\tThe date on which Evaluation was created.\n contentSource: 'Unknown', // The Synapse ID of the Entity to which this Evaluation belongs, e.g. a reference to a Synapse project.\n submissionInstructionsMessage: 'Unknown', // Message to display to users detailing acceptable formatting for Submissions to this Evaluation.\n submissionReceiptMessage: 'Unknown', // Message to display to users upon successful submission to this Evaluation.\n}\n\nconst getEntityHeaderItems = async (\n lookupList: ReferenceList,\n accessToken: string | undefined,\n): Promise<EntityHeader[]> => {\n const newData = await getEntityHeaders(lookupList, accessToken)\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item.targetId) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...entityHeaderTemplate,\n id: item.targetId,\n name: `${item.targetId}`,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\nconst getUserGroupHeaderItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<UserGroupHeader[]> => {\n const newData = (await getGroupHeadersBatch(lookupList, accessToken)).children\n const notFound = lookupList.filter(\n item => newData.map(item => item.ownerId).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...UserGroupHeaderTemplate,\n ownerId: item,\n name: `Unknown User (${item})`,\n }))\n\n return [...newData, ...notFoundPlaceholders]\n}\n\nconst getEvaluationItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<Evaluation[]> => {\n const newData = await getEvaluations(\n { evaluationIds: lookupList },\n accessToken,\n )\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...evaluationTemplate,\n id: item,\n name: item,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\n/**\n * React hook to get user profiles or entities or evaluation queues. Utilizes a custom cache in sessionStorage.\n *\n * For fetching just one entity or usergroup, see useGetEntity/useGetUserGroupHeader hooks powered by react-query\n * @returns\n */\nexport default function useGetInfoFromIds<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n>(props: UseGetInfoFromIdsProps<T>): T[] {\n const { ids, type } = props\n const { accessToken } = useSynapseContext()\n\n const [data, setData] = useState<Array<T>>([])\n\n const idKey = (type === 'USER_PROFILE' ? 'ownerId' : 'id') as keyof T\n\n const getStorageKey = (type: UseGetInfoFromIdsProps<T>['type']) => {\n switch (type) {\n case 'USER_PROFILE':\n return SynapseConstants.USER_PROFILE_STORAGE_KEY\n case 'ENTITY_HEADER':\n return SynapseConstants.ENTITY_HEADER_STORAGE_KEY\n case 'EVALUATION_QUEUE':\n return SynapseConstants.EVALUATIONS_STORAGE_KEY\n default:\n return ''\n }\n }\n const storageKey: string = getStorageKey(type)\n\n // look at current list of data, see if incoming ids has new data,\n // if so grab those ids\n const curList = data.map(el => el[idKey]) as string[]\n const incomingList = ids.filter(el => el !== SynapseConstants.VALUE_NOT_SET)\n const newValues = uniq(without(incomingList, ...curList))\n\n useEffect(() => {\n const saveToSessionStorage = (data: T[]) => {\n if (!data.length) {\n return\n }\n //get what's there\n const dataInStorage = sessionStorage.getItem(storageKey)\n try {\n const dataInStorageAsObjectArr: T[] = dataInStorage\n ? JSON.parse(dataInStorage)\n : []\n //get an array of ids for items already in storage\n const ids = dataInStorageAsObjectArr.map(item => item[idKey])\n //push all the new data if ids are new\n for (const dataObject of data) {\n if (!ids.includes(dataObject[idKey])) {\n dataInStorageAsObjectArr.push(dataObject)\n }\n }\n sessionStorage.setItem(\n storageKey,\n JSON.stringify(dataInStorageAsObjectArr),\n )\n } catch (e) {\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n }\n }\n saveToSessionStorage(data)\n }, [data, idKey, storageKey])\n\n // Alina TODO: check if the items are already in Local Storage before making server call.\n\n // Michael TODO: There's a bug where the data held in useGetInfoFromIds will be stale if the user token changes,\n // this can be solved by using the useCompare hook on the token to track when it changes\n useDeepCompareEffect(() => {\n let isCancelled = false\n const getData = async () => {\n if (newValues.length > 0) {\n try {\n const newIds = Array.from<string>(newValues)\n const newReferences: LookupRequestType[] =\n type === 'ENTITY_HEADER'\n ? newIds.map(el => ({ targetId: el }))\n : newIds\n const newReferencesChunks = chunk(newReferences, 45)\n const totalData: T[] = []\n for (const newReferences of newReferencesChunks) {\n let newData: T[] = []\n switch (type) {\n case 'USER_PROFILE':\n newData = (await getUserGroupHeaderItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n case 'ENTITY_HEADER':\n newData = (await getEntityHeaderItems(\n newReferences as ReferenceList,\n accessToken,\n )) as T[]\n break\n case 'EVALUATION_QUEUE':\n newData = (await getEvaluationItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n }\n totalData.push(...newData)\n }\n if (!isCancelled) {\n setData(oldData => oldData.concat(...totalData))\n }\n } catch (error) {\n console.error('Error on data retrieval', error)\n }\n }\n }\n getData()\n return () => {\n isCancelled = true\n }\n }, [accessToken, type, newValues])\n return data\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useGetInfoFromIds.js","names":[],"sources":["../../../src/utils/hooks/useGetInfoFromIds.ts"],"sourcesContent":["import {\n getEntityHeaders,\n getEvaluations,\n getGroupHeadersBatch,\n} from '@/synapse-client'\nimport {\n EntityHeader,\n Evaluation,\n Reference,\n ReferenceList,\n UserGroupHeader,\n} from '@sage-bionetworks/synapse-types'\nimport { chunk, uniq, without } from 'lodash-es'\nimport { useEffect, useState } from 'react'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport { useSynapseContext } from '../context/SynapseContext'\nimport { SynapseConstants } from '../index'\n\nexport type UseGetInfoFromIdsProps<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n THookType = T extends EntityHeader\n ? 'ENTITY_HEADER'\n : T extends UserGroupHeader\n ? 'USER_PROFILE'\n : T extends Evaluation\n ? 'EVALUATION_QUEUE'\n : never,\n> = {\n ids: string[]\n type: THookType\n}\n\ntype LookupRequestType = string | Reference\n\nconst UserGroupHeaderTemplate: UserGroupHeader = {\n ownerId: '', // A foreign key to the ID of the 'principal' object for the user\n firstName: 'Unknown', // This person's given name (forename)\n lastName: 'Unknown', // This person's family name (surname)\n userName: 'Unknown', // A name chosen by the user that uniquely identifies them\n isIndividual: false,\n}\n\nconst entityHeaderTemplate: EntityHeader = {\n name: 'Unknown', //\tThe name of the entity\n id: 'unknown', //\tThe id of the entity\n type: 'org.sagebionetworks.repo.model.FileEntity', //\tThe type of the entity\n versionNumber: 0, //\tThe version number of the entity\n versionLabel: 'placeholder', //\tThe user defined version label of the entity\n benefactorId: 0, //\tThe ID of the entity that this Entity's ACL is inherited from.\n createdOn: 'null', //\tThe date this entity was created.\n modifiedOn: 'null', //\tThe date this entity was last modified.\n createdBy: 'null', //\tThe ID of the user that created this entity.\n modifiedBy: 'null', //\tThe ID of the user that last modified this entity.\n isLatestVersion: true, // If this version is the latest version of the entity\n}\n\nconst evaluationTemplate: Evaluation = {\n id: 'unknown', // The unique immutable ID for this Evaluation.\n etag: 'Unknown', // Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. The eTag changes every time an Evaluation is updated; it is used to detect when a client's copy of an Evaluation is out-of-date.\n name: 'Unknown', // The name of this Evaluation\n description: 'Unknown', // A text description of this Evaluation.\n ownerId: 'null', // The ID of the Synapse user who created this Evaluation.\n createdOn: 'null', //\tThe date on which Evaluation was created.\n contentSource: 'Unknown', // The Synapse ID of the Entity to which this Evaluation belongs, e.g. a reference to a Synapse project.\n submissionInstructionsMessage: 'Unknown', // Message to display to users detailing acceptable formatting for Submissions to this Evaluation.\n submissionReceiptMessage: 'Unknown', // Message to display to users upon successful submission to this Evaluation.\n}\n\nconst getEntityHeaderItems = async (\n lookupList: ReferenceList,\n accessToken: string | undefined,\n): Promise<EntityHeader[]> => {\n const newData = await getEntityHeaders(lookupList, accessToken)\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item.targetId) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...entityHeaderTemplate,\n id: item.targetId,\n name: `${item.targetId}`,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\nconst getUserGroupHeaderItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<UserGroupHeader[]> => {\n const newData = (await getGroupHeadersBatch(lookupList, accessToken)).children\n const notFound = lookupList.filter(\n item => newData.map(item => item.ownerId).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...UserGroupHeaderTemplate,\n ownerId: item,\n name: `Unknown User (${item})`,\n }))\n\n return [...newData, ...notFoundPlaceholders]\n}\n\nconst getEvaluationItems = async (\n lookupList: string[],\n accessToken: string | undefined,\n): Promise<Evaluation[]> => {\n const newData = await getEvaluations(\n { evaluationIds: lookupList },\n accessToken,\n )\n const notFound = lookupList.filter(\n item => newData.results.map(item => item.id).indexOf(item) === -1,\n )\n const notFoundPlaceholders = notFound.map(item => ({\n ...evaluationTemplate,\n id: item,\n name: item,\n }))\n\n return [...newData.results, ...notFoundPlaceholders]\n}\n\n/**\n * React hook to get user profiles or entities or evaluation queues. Utilizes a custom cache in sessionStorage.\n *\n * For fetching just one entity or usergroup, see useGetEntity/useGetUserGroupHeader hooks powered by react-query\n * @returns\n */\nexport default function useGetInfoFromIds<\n T extends EntityHeader | UserGroupHeader | Evaluation,\n>(props: UseGetInfoFromIdsProps<T>): T[] {\n const { ids, type } = props\n const { accessToken } = useSynapseContext()\n\n const [data, setData] = useState<Array<T>>([])\n\n const idKey = (type === 'USER_PROFILE' ? 'ownerId' : 'id') as keyof T\n\n const getStorageKey = (type: UseGetInfoFromIdsProps<T>['type']) => {\n switch (type) {\n case 'USER_PROFILE':\n return SynapseConstants.USER_PROFILE_STORAGE_KEY\n case 'ENTITY_HEADER':\n return SynapseConstants.ENTITY_HEADER_STORAGE_KEY\n case 'EVALUATION_QUEUE':\n return SynapseConstants.EVALUATIONS_STORAGE_KEY\n default:\n return ''\n }\n }\n const storageKey: string = getStorageKey(type)\n\n // look at current list of data, see if incoming ids has new data,\n // if so grab those ids\n const curList = data.map(el => el[idKey]) as string[]\n const incomingList = ids.filter(el => el !== SynapseConstants.VALUE_NOT_SET)\n const newValues = uniq(without(incomingList, ...curList))\n\n useEffect(() => {\n const saveToSessionStorage = (data: T[]) => {\n if (!data.length) {\n return\n }\n //get what's there\n const dataInStorage = sessionStorage.getItem(storageKey)\n try {\n const dataInStorageAsObjectArr: T[] = dataInStorage\n ? JSON.parse(dataInStorage)\n : []\n //get an array of ids for items already in storage\n const ids = dataInStorageAsObjectArr.map(item => item[idKey])\n //push all the new data if ids are new\n for (const dataObject of data) {\n if (!ids.includes(dataObject[idKey])) {\n dataInStorageAsObjectArr.push(dataObject)\n }\n }\n sessionStorage.setItem(\n storageKey,\n JSON.stringify(dataInStorageAsObjectArr),\n )\n } catch (e) {\n sessionStorage.setItem(storageKey, JSON.stringify(data))\n }\n }\n saveToSessionStorage(data)\n }, [data, idKey, storageKey])\n\n // Alina TODO: check if the items are already in Local Storage before making server call.\n\n // Michael TODO: There's a bug where the data held in useGetInfoFromIds will be stale if the user token changes,\n // this can be solved by using the useCompare hook on the token to track when it changes\n useDeepCompareEffect(() => {\n let isCancelled = false\n const getData = async () => {\n if (newValues.length > 0) {\n try {\n const newIds = Array.from<string>(newValues)\n const newReferences: LookupRequestType[] =\n type === 'ENTITY_HEADER'\n ? newIds.map(el => ({ targetId: el }))\n : newIds\n const newReferencesChunks = chunk(newReferences, 45)\n const totalData: T[] = []\n for (const newReferences of newReferencesChunks) {\n let newData: T[] = []\n switch (type) {\n case 'USER_PROFILE':\n newData = (await getUserGroupHeaderItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n case 'ENTITY_HEADER':\n newData = (await getEntityHeaderItems(\n newReferences as ReferenceList,\n accessToken,\n )) as T[]\n break\n case 'EVALUATION_QUEUE':\n newData = (await getEvaluationItems(\n newReferences as string[],\n accessToken,\n )) as T[]\n break\n }\n totalData.push(...newData)\n }\n if (!isCancelled) {\n setData(oldData => oldData.concat(...totalData))\n }\n } catch (error) {\n console.error('Error on data retrieval', error)\n }\n }\n }\n getData()\n return () => {\n isCancelled = true\n }\n }, [accessToken, type, newValues])\n return data\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useImageUrlUtils.js","names":[],"sources":["../../../src/utils/hooks/useImageUrlUtils.ts"],"sourcesContent":["import { useGetStablePresignedUrl } from '@/synapse-queries'\nimport {\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nexport const useImageUrl = (fileId: string, entityId: string) => {\n const fha: FileHandleAssociation = {\n associateObjectId: entityId,\n associateObjectType: FileHandleAssociateType.TableEntity,\n fileHandleId: fileId || '',\n }\n const stablePresignedUrl = useGetStablePresignedUrl(fha, false, {\n enabled: !!fileId,\n })\n const dataUrl = stablePresignedUrl?.dataUrl\n\n return dataUrl\n}\n"],"mappings":";;;;AAMA,IAAa,KAAe,GAAgB,MAMf,EALQ;CACjC,mBAAmB;CACnB,qBAAqB,EAAwB;CAC7C,cAAc,KAAU;CACzB,EACwD,IAAO,EAC9D,SAAS,CAAC,CAAC,GACZ,CAAC,EACkC"}
1
+ {"version":3,"file":"useImageUrlUtils.js","names":[],"sources":["../../../src/utils/hooks/useImageUrlUtils.ts"],"sourcesContent":["import { useGetStablePresignedUrl } from '@/synapse-queries'\nimport {\n FileHandleAssociateType,\n FileHandleAssociation,\n} from '@sage-bionetworks/synapse-types'\n\nexport const useImageUrl = (fileId: string, entityId: string) => {\n const fha: FileHandleAssociation = {\n associateObjectId: entityId,\n associateObjectType: FileHandleAssociateType.TableEntity,\n fileHandleId: fileId || '',\n }\n const stablePresignedUrl = useGetStablePresignedUrl(fha, false, {\n enabled: !!fileId,\n })\n const dataUrl = stablePresignedUrl?.dataUrl\n\n return dataUrl\n}\n"],"mappings":";;;;AAMA,IAAa,KAAe,GAAgB,MAMf,EAAyB;CAJlD,mBAAmB;CACnB,qBAAqB,EAAwB;CAC7C,cAAc,KAAU;CAE0B,EAAK,IAAO,EAC9D,SAAS,CAAC,CAAC,GACZ,CACe,EAAoB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useImmutableTableQuery.js","names":[],"sources":["../../../../src/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.ts"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils/types/UniqueFacetIdentifier'\nimport {\n QueryBundleRequest,\n QueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual } from 'lodash-es'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo } from 'react'\nimport { ReadonlyDeep } from 'type-fest'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport * as DeepLinkingUtils from '../../functions/deepLinkingUtils'\nimport { removeEmptyQueryParams } from '../../functions/queryUtils'\nimport { parseEntityIdAndVersionFromSqlStatement } from '../../functions/SqlFunctions'\nimport { DEFAULT_PAGE_SIZE } from '../../SynapseConstants'\nimport {\n QueryChangeCommitOptions,\n useTableQueryReducer,\n} from './useTableQueryReducer'\n\nexport type ImmutableTableQueryResult = {\n /** The ID of the table parsed from the SQL query */\n entityId?: string\n /** The version number of the table parsed from the SQL query */\n versionNumber?: number\n /** The current query request, which is passed on to the server for results */\n currentQueryRequest: ReadonlyDeep<QueryBundleRequest>\n /** The next (uncommitted) query request. This will become the current query request when commitChanges is called, or the configured debounced timer elapses. */\n nextQueryRequest: ReadonlyDeep<QueryBundleRequest>\n /** Resets the debounce timer to delay committing changes in `nextQueryRequest` */\n resetDebounceTimer: () => void\n /** Update the currentQueryRequest to be the nextQueryRequest */\n commitChanges: () => void\n getInitQueryRequest: () => QueryBundleRequest\n getCurrentQueryRequest: () => QueryBundleRequest\n setQuery: (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n pageSize: number\n /** The current page of results. The first page is `1` */\n currentPage: number\n setPageSize: (pageSize: number) => void\n /** pageNumber is 1-indexed */\n goToPage: (pageNumber: number) => void\n /** Resets the query to the initial state, clearing all user-specified filters */\n resetQuery: () => void\n addValueToSelectedFacet: (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n /** Removes a particular selected facet from the query */\n removeSelectedFacet: (\n facet: UniqueFacetIdentifier | UniqueFacetIdentifier[],\n ) => void\n /** Removes a particular value from a selected facet. If the value is the last value in the FacetColumnRequest, the selected facet will be removed. */\n removeValueFromSelectedFacet: (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n setRangeFacetValue: (\n facet: UniqueFacetIdentifier,\n min?: string,\n max?: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n /** Removes a particular QueryFilter from the query */\n removeQueryFilter: (filter: QueryFilter) => void\n /** Removes a particular value from a QueryFilter. If the value is the last value in the filter, the filter will be removed. */\n removeValueFromQueryFilter: (filter: QueryFilter, value: string) => void\n /** If `requireConfirmationOnChange` is true, this will become true when a function that triggers a query change is invoked. */\n isConfirmingChange: boolean\n /** If `isConfirmingChange` is true, invoke this function to complete the query change */\n onConfirmChange: () => void\n /** If `isConfirmingChange` is true, invoke this function to cancel the query change */\n onCancelChange: () => void\n\n /**\n * TODO: This hook could handle all potential query transformations, such as\n * - addFacetFilter, removeFacetFilter, clearFacetFilters\n * - addAdditionalFilter, remove..., clear...\n *\n * This could be preferable to allowing any QueryContext subscriber to arbitrarily update the query with `setQuery`\n * because we could uniformly handle all complex stateful logic in this hook\n */\n}\n\nexport type UseImmutableTableQueryOptions = {\n /** The initial table query request object */\n initQueryRequest: QueryBundleRequest\n /** Whether the URL should update when the query is modified. */\n shouldDeepLink?: boolean\n /** Unique index for the component on the page so URL updates do not conflict between table query components */\n componentIndex?: number\n /** Callback invoked when the query is modified */\n onQueryChange?: (newQueryJson: string) => void\n /** Whether to require explicit user confirmation before changing the query. */\n requireConfirmationOnChange?: boolean\n}\n\n// When changing the query with the debounce option, the amount of time to wait for additional debounced changes before updating the query\nexport const DEBOUNCE_DELAY_MS = 750\n\n/**\n * Utility to synchronize the URL with the current query request. Synchronization only occurs if `shouldDeepLink` is true.\n * @param shouldDeepLink whether the query should be synchronized with the URL\n * @param componentIndex the index of the component to synchronize with the URL - essential if multiple components to synchronize are on the page\n * @param setQuery - callback used to update the current query\n * @param initQueryRequest - the initial query request passed via props\n * @param currentQueryRequest - the query request that is currently used\n */\nfunction useSynchronizeQueryWithUrl(\n shouldDeepLink: boolean,\n componentIndex: number,\n setQuery: (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ) => void,\n initQueryRequest: QueryBundleRequest,\n currentQueryRequest: QueryBundleRequest,\n) {\n /**\n * Inspect the URL on mount to see if we have a particular query request that we must show.\n */\n useEffect(() => {\n // Only run this effect if deep linking is enabled\n if (shouldDeepLink) {\n DeepLinkingUtils.getQueryRequestFromLink(\n componentIndex,\n initQueryRequest.query,\n ).then(queryRequestFromLink => {\n if (queryRequestFromLink && queryRequestFromLink.query) {\n setQuery(prevState => ({\n ...prevState,\n ...queryRequestFromLink,\n query: {\n ...prevState.query,\n ...queryRequestFromLink.query,\n },\n }))\n }\n })\n }\n // should only run on mount, or if the component index changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [componentIndex])\n\n // If `shouldDeepLink` is true, synchronize the URL\n useEffect(() => {\n if (shouldDeepLink) {\n if (isEqual(initQueryRequest, currentQueryRequest)) {\n void DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n null,\n null,\n ).catch(error => {\n console.error('Failed to update URL with search param:', error)\n })\n } else {\n void DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n currentQueryRequest.query,\n initQueryRequest.query,\n ).catch(error => {\n console.error('Failed to update URL with search param:', error)\n })\n }\n }\n // Clean up the URL param when this component unmounts or deep linking is disabled\n return () => {\n if (shouldDeepLink) {\n DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n null,\n initQueryRequest.query,\n )\n }\n }\n }, [componentIndex, currentQueryRequest, initQueryRequest, shouldDeepLink])\n}\n\n/**\n * Custom hook that maintains and manages the state of a Synapse Table query.\n * @param options\n * @returns\n */\nexport default function useImmutableTableQuery(\n options: UseImmutableTableQueryOptions,\n): ImmutableTableQueryResult {\n const {\n initQueryRequest: initQueryRequestFromProps,\n componentIndex = 0,\n shouldDeepLink = false,\n onQueryChange,\n requireConfirmationOnChange = false,\n } = options\n\n const initQueryRequest = useMemo(() => {\n const request = cloneDeep(initQueryRequestFromProps)\n request.query = removeEmptyQueryParams(request.query)\n return request\n }, [initQueryRequestFromProps])\n\n const {\n currentQueryRequest,\n nextQueryRequest,\n isConfirmingChange,\n dispatch,\n } = useTableQueryReducer(\n initQueryRequest,\n requireConfirmationOnChange,\n onQueryChange,\n )\n\n const onConfirmChange = useCallback(() => {\n dispatch({ type: 'confirmChanges' })\n }, [dispatch])\n\n /**\n * Pass down a deep clone (so no side effects on the child's part) of the\n * current query request\n *\n * @returns\n * @memberof QueryWrapper\n */\n const getCurrentQueryRequest = useCallback(() => {\n return cloneDeep(currentQueryRequest)\n }, [currentQueryRequest])\n\n /**\n * Pass down a deep clone (so no side effects on the child's part) of the\n * first query request made\n *\n * @returns\n * @memberof QueryWrapper\n */\n const getInitQueryRequest = useCallback((): QueryBundleRequest => {\n return cloneDeep(initQueryRequest)\n }, [initQueryRequest])\n\n /**\n * Execute the given query request, updating all the data in the QueryContext to match the new query\n * @param {*} queryRequest Query request as specified by\n * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Query.html\n */\n const setQuery: ImmutableTableQueryResult['setQuery'] = useCallback(\n (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ): void => {\n dispatch({\n type: 'setQuery',\n queryOrUpdater: queryRequest,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n useSynchronizeQueryWithUrl(\n shouldDeepLink,\n componentIndex,\n setQuery,\n initQueryRequest,\n currentQueryRequest,\n )\n\n const onCancelChange = useCallback(() => {\n dispatch({ type: 'cancelChanges' })\n }, [dispatch])\n\n const { entityId, versionNumber } = useMemo(\n () =>\n parseEntityIdAndVersionFromSqlStatement(currentQueryRequest.query.sql),\n [currentQueryRequest.query.sql],\n )\n\n const pageSize = currentQueryRequest.query.limit ?? DEFAULT_PAGE_SIZE\n const currentPage = Math.ceil(\n ((currentQueryRequest.query.offset ?? 0) + Number(pageSize)) / pageSize,\n )\n\n const setPageSize = useCallback(\n (pageSize: number) => {\n dispatch({ type: 'setPageSize', pageSize })\n },\n [dispatch],\n )\n\n const goToPage = useCallback(\n (pageNumber: number) => {\n dispatch({ type: 'goToPage', pageNumber })\n },\n [dispatch],\n )\n\n const resetQuery = useCallback(() => {\n dispatch({\n type: 'resetQuery',\n })\n }, [dispatch])\n\n /* If the initial query changes, then reset the query to match the new prop */\n useDeepCompareEffect(() => {\n if (currentQueryRequest != initQueryRequest) {\n resetQuery()\n }\n }, [initQueryRequest])\n\n const addValueToSelectedFacet = useCallback(\n (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'addValueToSelectedFacet',\n facet: facet,\n value: value,\n commitOptions: commitOptions,\n })\n },\n [dispatch],\n )\n\n const setRangeFacetValue = useCallback(\n (\n facet: UniqueFacetIdentifier,\n min?: string,\n max?: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'setRangeFacetValue',\n facet,\n min,\n max,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n const removeSelectedFacet = useCallback(\n (facetsToRemove: UniqueFacetIdentifier | UniqueFacetIdentifier[]) => {\n dispatch({\n type: 'removeSelectedFacet',\n facetsToRemove: facetsToRemove,\n })\n },\n [dispatch],\n )\n\n const removeValueFromSelectedFacet = useCallback(\n (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'removeValueFromSelectedFacet',\n facet,\n value,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n const removeQueryFilter = useCallback(\n (queryFilter: QueryFilter) => {\n dispatch({\n type: 'removeQueryFilter',\n queryFilter,\n })\n },\n [dispatch],\n )\n\n const removeValueFromQueryFilter = useCallback(\n (queryFilter: QueryFilter, value: string) => {\n dispatch({\n type: 'removeValueFromQueryFilter',\n queryFilter,\n value,\n })\n },\n [dispatch],\n )\n const commitChanges = useCallback(() => {\n dispatch({ type: 'commitChanges' })\n }, [dispatch])\n\n const resetDebounceTimer = useCallback(() => {\n dispatch({ type: 'resetDebounce' })\n }, [dispatch])\n\n return {\n entityId,\n commitChanges,\n resetDebounceTimer,\n currentQueryRequest,\n nextQueryRequest,\n versionNumber,\n getInitQueryRequest,\n getCurrentQueryRequest,\n setQuery,\n pageSize,\n currentPage,\n setPageSize,\n goToPage,\n resetQuery,\n removeSelectedFacet,\n removeValueFromSelectedFacet,\n removeQueryFilter,\n removeValueFromQueryFilter,\n isConfirmingChange,\n onConfirmChange,\n onCancelChange,\n addValueToSelectedFacet,\n setRangeFacetValue,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useImmutableTableQuery.js","names":[],"sources":["../../../../src/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.ts"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils/types/UniqueFacetIdentifier'\nimport {\n QueryBundleRequest,\n QueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual } from 'lodash-es'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo } from 'react'\nimport { ReadonlyDeep } from 'type-fest'\nimport useDeepCompareEffect from 'use-deep-compare-effect'\nimport * as DeepLinkingUtils from '../../functions/deepLinkingUtils'\nimport { removeEmptyQueryParams } from '../../functions/queryUtils'\nimport { parseEntityIdAndVersionFromSqlStatement } from '../../functions/SqlFunctions'\nimport { DEFAULT_PAGE_SIZE } from '../../SynapseConstants'\nimport {\n QueryChangeCommitOptions,\n useTableQueryReducer,\n} from './useTableQueryReducer'\n\nexport type ImmutableTableQueryResult = {\n /** The ID of the table parsed from the SQL query */\n entityId?: string\n /** The version number of the table parsed from the SQL query */\n versionNumber?: number\n /** The current query request, which is passed on to the server for results */\n currentQueryRequest: ReadonlyDeep<QueryBundleRequest>\n /** The next (uncommitted) query request. This will become the current query request when commitChanges is called, or the configured debounced timer elapses. */\n nextQueryRequest: ReadonlyDeep<QueryBundleRequest>\n /** Resets the debounce timer to delay committing changes in `nextQueryRequest` */\n resetDebounceTimer: () => void\n /** Update the currentQueryRequest to be the nextQueryRequest */\n commitChanges: () => void\n getInitQueryRequest: () => QueryBundleRequest\n getCurrentQueryRequest: () => QueryBundleRequest\n setQuery: (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n pageSize: number\n /** The current page of results. The first page is `1` */\n currentPage: number\n setPageSize: (pageSize: number) => void\n /** pageNumber is 1-indexed */\n goToPage: (pageNumber: number) => void\n /** Resets the query to the initial state, clearing all user-specified filters */\n resetQuery: () => void\n addValueToSelectedFacet: (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n /** Removes a particular selected facet from the query */\n removeSelectedFacet: (\n facet: UniqueFacetIdentifier | UniqueFacetIdentifier[],\n ) => void\n /** Removes a particular value from a selected facet. If the value is the last value in the FacetColumnRequest, the selected facet will be removed. */\n removeValueFromSelectedFacet: (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n setRangeFacetValue: (\n facet: UniqueFacetIdentifier,\n min?: string,\n max?: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => void\n /** Removes a particular QueryFilter from the query */\n removeQueryFilter: (filter: QueryFilter) => void\n /** Removes a particular value from a QueryFilter. If the value is the last value in the filter, the filter will be removed. */\n removeValueFromQueryFilter: (filter: QueryFilter, value: string) => void\n /** If `requireConfirmationOnChange` is true, this will become true when a function that triggers a query change is invoked. */\n isConfirmingChange: boolean\n /** If `isConfirmingChange` is true, invoke this function to complete the query change */\n onConfirmChange: () => void\n /** If `isConfirmingChange` is true, invoke this function to cancel the query change */\n onCancelChange: () => void\n\n /**\n * TODO: This hook could handle all potential query transformations, such as\n * - addFacetFilter, removeFacetFilter, clearFacetFilters\n * - addAdditionalFilter, remove..., clear...\n *\n * This could be preferable to allowing any QueryContext subscriber to arbitrarily update the query with `setQuery`\n * because we could uniformly handle all complex stateful logic in this hook\n */\n}\n\nexport type UseImmutableTableQueryOptions = {\n /** The initial table query request object */\n initQueryRequest: QueryBundleRequest\n /** Whether the URL should update when the query is modified. */\n shouldDeepLink?: boolean\n /** Unique index for the component on the page so URL updates do not conflict between table query components */\n componentIndex?: number\n /** Callback invoked when the query is modified */\n onQueryChange?: (newQueryJson: string) => void\n /** Whether to require explicit user confirmation before changing the query. */\n requireConfirmationOnChange?: boolean\n}\n\n// When changing the query with the debounce option, the amount of time to wait for additional debounced changes before updating the query\nexport const DEBOUNCE_DELAY_MS = 750\n\n/**\n * Utility to synchronize the URL with the current query request. Synchronization only occurs if `shouldDeepLink` is true.\n * @param shouldDeepLink whether the query should be synchronized with the URL\n * @param componentIndex the index of the component to synchronize with the URL - essential if multiple components to synchronize are on the page\n * @param setQuery - callback used to update the current query\n * @param initQueryRequest - the initial query request passed via props\n * @param currentQueryRequest - the query request that is currently used\n */\nfunction useSynchronizeQueryWithUrl(\n shouldDeepLink: boolean,\n componentIndex: number,\n setQuery: (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ) => void,\n initQueryRequest: QueryBundleRequest,\n currentQueryRequest: QueryBundleRequest,\n) {\n /**\n * Inspect the URL on mount to see if we have a particular query request that we must show.\n */\n useEffect(() => {\n // Only run this effect if deep linking is enabled\n if (shouldDeepLink) {\n DeepLinkingUtils.getQueryRequestFromLink(\n componentIndex,\n initQueryRequest.query,\n ).then(queryRequestFromLink => {\n if (queryRequestFromLink && queryRequestFromLink.query) {\n setQuery(prevState => ({\n ...prevState,\n ...queryRequestFromLink,\n query: {\n ...prevState.query,\n ...queryRequestFromLink.query,\n },\n }))\n }\n })\n }\n // should only run on mount, or if the component index changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [componentIndex])\n\n // If `shouldDeepLink` is true, synchronize the URL\n useEffect(() => {\n if (shouldDeepLink) {\n if (isEqual(initQueryRequest, currentQueryRequest)) {\n void DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n null,\n null,\n ).catch(error => {\n console.error('Failed to update URL with search param:', error)\n })\n } else {\n void DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n currentQueryRequest.query,\n initQueryRequest.query,\n ).catch(error => {\n console.error('Failed to update URL with search param:', error)\n })\n }\n }\n // Clean up the URL param when this component unmounts or deep linking is disabled\n return () => {\n if (shouldDeepLink) {\n DeepLinkingUtils.updateUrlWithNewSearchParam(\n 'qw',\n componentIndex,\n null,\n initQueryRequest.query,\n )\n }\n }\n }, [componentIndex, currentQueryRequest, initQueryRequest, shouldDeepLink])\n}\n\n/**\n * Custom hook that maintains and manages the state of a Synapse Table query.\n * @param options\n * @returns\n */\nexport default function useImmutableTableQuery(\n options: UseImmutableTableQueryOptions,\n): ImmutableTableQueryResult {\n const {\n initQueryRequest: initQueryRequestFromProps,\n componentIndex = 0,\n shouldDeepLink = false,\n onQueryChange,\n requireConfirmationOnChange = false,\n } = options\n\n const initQueryRequest = useMemo(() => {\n const request = cloneDeep(initQueryRequestFromProps)\n request.query = removeEmptyQueryParams(request.query)\n return request\n }, [initQueryRequestFromProps])\n\n const {\n currentQueryRequest,\n nextQueryRequest,\n isConfirmingChange,\n dispatch,\n } = useTableQueryReducer(\n initQueryRequest,\n requireConfirmationOnChange,\n onQueryChange,\n )\n\n const onConfirmChange = useCallback(() => {\n dispatch({ type: 'confirmChanges' })\n }, [dispatch])\n\n /**\n * Pass down a deep clone (so no side effects on the child's part) of the\n * current query request\n *\n * @returns\n * @memberof QueryWrapper\n */\n const getCurrentQueryRequest = useCallback(() => {\n return cloneDeep(currentQueryRequest)\n }, [currentQueryRequest])\n\n /**\n * Pass down a deep clone (so no side effects on the child's part) of the\n * first query request made\n *\n * @returns\n * @memberof QueryWrapper\n */\n const getInitQueryRequest = useCallback((): QueryBundleRequest => {\n return cloneDeep(initQueryRequest)\n }, [initQueryRequest])\n\n /**\n * Execute the given query request, updating all the data in the QueryContext to match the new query\n * @param {*} queryRequest Query request as specified by\n * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Query.html\n */\n const setQuery: ImmutableTableQueryResult['setQuery'] = useCallback(\n (\n queryRequest: React.SetStateAction<QueryBundleRequest>,\n commitOptions?: QueryChangeCommitOptions,\n ): void => {\n dispatch({\n type: 'setQuery',\n queryOrUpdater: queryRequest,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n useSynchronizeQueryWithUrl(\n shouldDeepLink,\n componentIndex,\n setQuery,\n initQueryRequest,\n currentQueryRequest,\n )\n\n const onCancelChange = useCallback(() => {\n dispatch({ type: 'cancelChanges' })\n }, [dispatch])\n\n const { entityId, versionNumber } = useMemo(\n () =>\n parseEntityIdAndVersionFromSqlStatement(currentQueryRequest.query.sql),\n [currentQueryRequest.query.sql],\n )\n\n const pageSize = currentQueryRequest.query.limit ?? DEFAULT_PAGE_SIZE\n const currentPage = Math.ceil(\n ((currentQueryRequest.query.offset ?? 0) + Number(pageSize)) / pageSize,\n )\n\n const setPageSize = useCallback(\n (pageSize: number) => {\n dispatch({ type: 'setPageSize', pageSize })\n },\n [dispatch],\n )\n\n const goToPage = useCallback(\n (pageNumber: number) => {\n dispatch({ type: 'goToPage', pageNumber })\n },\n [dispatch],\n )\n\n const resetQuery = useCallback(() => {\n dispatch({\n type: 'resetQuery',\n })\n }, [dispatch])\n\n /* If the initial query changes, then reset the query to match the new prop */\n useDeepCompareEffect(() => {\n if (currentQueryRequest != initQueryRequest) {\n resetQuery()\n }\n }, [initQueryRequest])\n\n const addValueToSelectedFacet = useCallback(\n (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'addValueToSelectedFacet',\n facet: facet,\n value: value,\n commitOptions: commitOptions,\n })\n },\n [dispatch],\n )\n\n const setRangeFacetValue = useCallback(\n (\n facet: UniqueFacetIdentifier,\n min?: string,\n max?: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'setRangeFacetValue',\n facet,\n min,\n max,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n const removeSelectedFacet = useCallback(\n (facetsToRemove: UniqueFacetIdentifier | UniqueFacetIdentifier[]) => {\n dispatch({\n type: 'removeSelectedFacet',\n facetsToRemove: facetsToRemove,\n })\n },\n [dispatch],\n )\n\n const removeValueFromSelectedFacet = useCallback(\n (\n facet: UniqueFacetIdentifier,\n value: string,\n commitOptions?: QueryChangeCommitOptions,\n ) => {\n dispatch({\n type: 'removeValueFromSelectedFacet',\n facet,\n value,\n commitOptions,\n })\n },\n [dispatch],\n )\n\n const removeQueryFilter = useCallback(\n (queryFilter: QueryFilter) => {\n dispatch({\n type: 'removeQueryFilter',\n queryFilter,\n })\n },\n [dispatch],\n )\n\n const removeValueFromQueryFilter = useCallback(\n (queryFilter: QueryFilter, value: string) => {\n dispatch({\n type: 'removeValueFromQueryFilter',\n queryFilter,\n value,\n })\n },\n [dispatch],\n )\n const commitChanges = useCallback(() => {\n dispatch({ type: 'commitChanges' })\n }, [dispatch])\n\n const resetDebounceTimer = useCallback(() => {\n dispatch({ type: 'resetDebounce' })\n }, [dispatch])\n\n return {\n entityId,\n commitChanges,\n resetDebounceTimer,\n currentQueryRequest,\n nextQueryRequest,\n versionNumber,\n getInitQueryRequest,\n getCurrentQueryRequest,\n setQuery,\n pageSize,\n currentPage,\n setPageSize,\n goToPage,\n resetQuery,\n removeSelectedFacet,\n removeValueFromSelectedFacet,\n removeQueryFilter,\n removeValueFromQueryFilter,\n isConfirmingChange,\n onConfirmChange,\n onCancelChange,\n addValueToSelectedFacet,\n setRangeFacetValue,\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useTableQueryReducer.js","names":[],"sources":["../../../../src/utils/hooks/useImmutableTableQuery/useTableQueryReducer.ts"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils/types'\nimport { useDebouncedEffect } from '@react-hookz/web'\nimport {\n QueryBundleRequest,\n QueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual } from 'lodash-es'\nimport * as React from 'react'\nimport { useMemo, useReducer, useState } from 'react'\nimport {\n queryRequestsHaveSameTotalResults,\n removeEmptyQueryParams,\n} from '../../functions/queryUtils'\nimport {\n addValueToSelectedFacet,\n getQueryFromSetStateAction,\n goToPage,\n removeQueryFilter,\n removeSelectedFacet,\n removeValueFromQueryFilter,\n removeValueFromSelectedFacet,\n setPageSize,\n setRangeFacetValue,\n} from './TableQueryReducerActions'\nimport { DEBOUNCE_DELAY_MS } from './useImmutableTableQuery'\n\nexport type QueryChangeCommitOptions =\n | {\n // This and future changes including debounce will not be committed until the debounceDelay has elapsed\n debounce: true\n }\n | {\n // The change will not be committed until the commit function is invoked.\n noCommit: true\n }\n\nexport type TableQueryReducerState = {\n currentQueryRequest: QueryBundleRequest\n nextQueryRequest: QueryBundleRequest\n isConfirmingChange: boolean\n}\n\nexport type TableQueryReducerAction = {\n commitOptions?: QueryChangeCommitOptions\n} & (\n | {\n type: 'setQuery'\n queryOrUpdater: React.SetStateAction<QueryBundleRequest>\n }\n | {\n type: 'addValueToSelectedFacet'\n facet: UniqueFacetIdentifier\n value: string\n }\n | {\n type: 'removeSelectedFacet'\n facetsToRemove: UniqueFacetIdentifier | UniqueFacetIdentifier[]\n }\n | {\n type: 'setRangeFacetValue'\n facet: UniqueFacetIdentifier\n min?: string\n max?: string\n }\n | {\n type: 'removeValueFromSelectedFacet'\n facet: UniqueFacetIdentifier\n value: string\n }\n | {\n type: 'setPageSize'\n pageSize: number\n }\n | {\n type: 'goToPage'\n pageNumber: number\n }\n | {\n type: 'resetQuery'\n }\n | {\n type: 'removeQueryFilter'\n queryFilter: QueryFilter\n }\n | {\n type: 'removeValueFromQueryFilter'\n queryFilter: QueryFilter\n value: string\n }\n | {\n type: 'resetUncommittedChanges'\n }\n | { type: 'commitChanges' }\n | { type: 'confirmChanges' }\n | { type: 'cancelChanges' }\n | { type: 'resetDebounce' }\n)\n\n/**\n * Utility hook that internally uses a reducer to manage the state of a table query.\n * @param initQueryRequest\n * @param requireConfirmationOnChange\n * @param onQueryChange\n */\nexport function useTableQueryReducer(\n initQueryRequest: QueryBundleRequest,\n requireConfirmationOnChange: boolean,\n onQueryChange?: (queryJsonString: string) => void,\n) {\n // Increment to reset the debounce counter.\n const [resetDebounceCounter, setResetDebounceCounter] = useState(0)\n const [commitAfterDebounce, setCommitAfterDebounce] = useState(false)\n\n // Note: we must use a reducer because we're tracking interrelated state variables. Attempts to separate these will\n // likely result in bugs such as dropped state changes when React performs a batch update.\n const [state, dispatch] = useReducer(\n (\n prevState: TableQueryReducerState,\n action: TableQueryReducerAction,\n ): TableQueryReducerState => {\n let updatedNextQueryRequest = prevState.nextQueryRequest\n switch (action.type) {\n case 'resetDebounce': {\n setResetDebounceCounter(v => v + 1)\n // Do not update state\n return prevState\n }\n case 'setQuery': {\n updatedNextQueryRequest = getQueryFromSetStateAction(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'addValueToSelectedFacet': {\n updatedNextQueryRequest = addValueToSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeSelectedFacet': {\n updatedNextQueryRequest = removeSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'setRangeFacetValue': {\n updatedNextQueryRequest = setRangeFacetValue(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeValueFromSelectedFacet': {\n updatedNextQueryRequest = removeValueFromSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeQueryFilter': {\n updatedNextQueryRequest = removeQueryFilter(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeValueFromQueryFilter': {\n updatedNextQueryRequest = removeValueFromQueryFilter(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'setPageSize': {\n updatedNextQueryRequest = setPageSize(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'goToPage': {\n updatedNextQueryRequest = goToPage(action, prevState.nextQueryRequest)\n break\n }\n case 'resetQuery': {\n updatedNextQueryRequest = initQueryRequest\n break\n }\n case 'cancelChanges':\n case 'resetUncommittedChanges': {\n updatedNextQueryRequest = prevState.currentQueryRequest\n break\n }\n case 'confirmChanges':\n case 'commitChanges': {\n updatedNextQueryRequest = cloneDeep(prevState.nextQueryRequest)\n // commit the changes\n break\n }\n }\n\n updatedNextQueryRequest.query = removeEmptyQueryParams(\n updatedNextQueryRequest.query,\n )\n\n const isConfirmingChange =\n requireConfirmationOnChange &&\n !queryRequestsHaveSameTotalResults(\n prevState.currentQueryRequest.query,\n updatedNextQueryRequest.query,\n )\n\n if (\n // No commit cases:\n // If `noCommit` is explicitly specified in the commit options\n (action.commitOptions && 'noCommit' in action.commitOptions) ||\n // Or if the user is in the 'confirm' state (modal is shown) AND\n // the action is not the 'confirmChanges' or 'cancelChanges' action.\n (isConfirmingChange &&\n action.type !== 'confirmChanges' &&\n action.type !== 'cancelChanges')\n ) {\n return {\n currentQueryRequest: prevState.currentQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: isConfirmingChange,\n }\n } else if (action.commitOptions && 'debounce' in action.commitOptions) {\n // If debouncing, update the state flag. An effect will commit the changes after the debounce delay.\n setCommitAfterDebounce(true)\n return {\n currentQueryRequest: prevState.currentQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: isConfirmingChange,\n }\n } else {\n // Commit case: clear the debounce flag and update state\n setCommitAfterDebounce(false)\n if (\n onQueryChange &&\n !isEqual(prevState.currentQueryRequest, updatedNextQueryRequest)\n ) {\n const queryJsonString = JSON.stringify(updatedNextQueryRequest.query)\n onQueryChange(queryJsonString)\n }\n return {\n currentQueryRequest: updatedNextQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: false,\n }\n }\n },\n {\n currentQueryRequest: initQueryRequest,\n nextQueryRequest: initQueryRequest,\n isConfirmingChange: false,\n },\n )\n\n useDebouncedEffect(\n () => {\n if (commitAfterDebounce) {\n dispatch({ type: 'commitChanges' })\n setCommitAfterDebounce(false)\n }\n },\n // nextQueryRequest MUST be included in the dependencies to ensure the debounce resets when it changes\n [\n state.nextQueryRequest,\n commitAfterDebounce,\n setCommitAfterDebounce,\n resetDebounceCounter,\n ],\n DEBOUNCE_DELAY_MS,\n )\n\n return useMemo(\n () => ({\n currentQueryRequest: state.currentQueryRequest,\n nextQueryRequest: state.nextQueryRequest,\n isConfirmingChange: state.isConfirmingChange,\n dispatch,\n }),\n [\n state.currentQueryRequest,\n state.isConfirmingChange,\n state.nextQueryRequest,\n ],\n )\n}\n"],"mappings":";;;;;;;AAwGA,SAAgB,EACd,GACA,GACA,GACA;CAEA,IAAM,CAAC,GAAsB,KAA2B,EAAS,EAAE,EAC7D,CAAC,GAAqB,KAA0B,EAAS,GAAM,EAI/D,CAAC,GAAO,KAAY,GAEtB,GACA,MAC2B;EAC3B,IAAI,IAA0B,EAAU;AACxC,UAAQ,EAAO,MAAf;GACE,KAAK,gBAGH,QAFA,GAAwB,MAAK,IAAI,EAAE,EAE5B;GAET,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EAAS,GAAQ,EAAU,iBAAiB;AACtE;GAEF,KAAK;AACH,QAA0B;AAC1B;GAEF,KAAK;GACL,KAAK;AACH,QAA0B,EAAU;AACpC;GAEF,KAAK;GACL,KAAK;AACH,QAA0B,EAAU,EAAU,iBAAiB;AAE/D;;AAIJ,IAAwB,QAAQ,EAC9B,EAAwB,MACzB;EAED,IAAM,IACJ,KACA,CAAC,EACC,EAAU,oBAAoB,OAC9B,EAAwB,MACzB;AAmCD,SA9BC,EAAO,iBAAiB,cAAc,EAAO,iBAG7C,KACC,EAAO,SAAS,oBAChB,EAAO,SAAS,kBAEX;GACL,qBAAqB,EAAU;GAC/B,kBAAkB;GACE;GACrB,GACQ,EAAO,iBAAiB,cAAc,EAAO,iBAEtD,EAAuB,GAAK,EACrB;GACL,qBAAqB,EAAU;GAC/B,kBAAkB;GACE;GACrB,KAGD,EAAuB,GAAM,EAE3B,KACA,CAAC,EAAQ,EAAU,qBAAqB,EAAwB,IAGhE,EADwB,KAAK,UAAU,EAAwB,MAAM,CACvC,EAEzB;GACL,qBAAqB;GACrB,kBAAkB;GAClB,oBAAoB;GACrB;IAGL;EACE,qBAAqB;EACrB,kBAAkB;EAClB,oBAAoB;EACrB,CACF;AAmBD,QAjBA,QACQ;AACJ,EAAI,MACF,EAAS,EAAE,MAAM,iBAAiB,CAAC,EACnC,EAAuB,GAAM;IAIjC;EACE,EAAM;EACN;EACA;EACA;EACD,EAAA,IAEF,EAEM,SACE;EACL,qBAAqB,EAAM;EAC3B,kBAAkB,EAAM;EACxB,oBAAoB,EAAM;EAC1B;EACD,GACD;EACE,EAAM;EACN,EAAM;EACN,EAAM;EACP,CACF"}
1
+ {"version":3,"file":"useTableQueryReducer.js","names":[],"sources":["../../../../src/utils/hooks/useImmutableTableQuery/useTableQueryReducer.ts"],"sourcesContent":["import { UniqueFacetIdentifier } from '@/utils/types'\nimport { useDebouncedEffect } from '@react-hookz/web'\nimport {\n QueryBundleRequest,\n QueryFilter,\n} from '@sage-bionetworks/synapse-types'\nimport { cloneDeep, isEqual } from 'lodash-es'\nimport * as React from 'react'\nimport { useMemo, useReducer, useState } from 'react'\nimport {\n queryRequestsHaveSameTotalResults,\n removeEmptyQueryParams,\n} from '../../functions/queryUtils'\nimport {\n addValueToSelectedFacet,\n getQueryFromSetStateAction,\n goToPage,\n removeQueryFilter,\n removeSelectedFacet,\n removeValueFromQueryFilter,\n removeValueFromSelectedFacet,\n setPageSize,\n setRangeFacetValue,\n} from './TableQueryReducerActions'\nimport { DEBOUNCE_DELAY_MS } from './useImmutableTableQuery'\n\nexport type QueryChangeCommitOptions =\n | {\n // This and future changes including debounce will not be committed until the debounceDelay has elapsed\n debounce: true\n }\n | {\n // The change will not be committed until the commit function is invoked.\n noCommit: true\n }\n\nexport type TableQueryReducerState = {\n currentQueryRequest: QueryBundleRequest\n nextQueryRequest: QueryBundleRequest\n isConfirmingChange: boolean\n}\n\nexport type TableQueryReducerAction = {\n commitOptions?: QueryChangeCommitOptions\n} & (\n | {\n type: 'setQuery'\n queryOrUpdater: React.SetStateAction<QueryBundleRequest>\n }\n | {\n type: 'addValueToSelectedFacet'\n facet: UniqueFacetIdentifier\n value: string\n }\n | {\n type: 'removeSelectedFacet'\n facetsToRemove: UniqueFacetIdentifier | UniqueFacetIdentifier[]\n }\n | {\n type: 'setRangeFacetValue'\n facet: UniqueFacetIdentifier\n min?: string\n max?: string\n }\n | {\n type: 'removeValueFromSelectedFacet'\n facet: UniqueFacetIdentifier\n value: string\n }\n | {\n type: 'setPageSize'\n pageSize: number\n }\n | {\n type: 'goToPage'\n pageNumber: number\n }\n | {\n type: 'resetQuery'\n }\n | {\n type: 'removeQueryFilter'\n queryFilter: QueryFilter\n }\n | {\n type: 'removeValueFromQueryFilter'\n queryFilter: QueryFilter\n value: string\n }\n | {\n type: 'resetUncommittedChanges'\n }\n | { type: 'commitChanges' }\n | { type: 'confirmChanges' }\n | { type: 'cancelChanges' }\n | { type: 'resetDebounce' }\n)\n\n/**\n * Utility hook that internally uses a reducer to manage the state of a table query.\n * @param initQueryRequest\n * @param requireConfirmationOnChange\n * @param onQueryChange\n */\nexport function useTableQueryReducer(\n initQueryRequest: QueryBundleRequest,\n requireConfirmationOnChange: boolean,\n onQueryChange?: (queryJsonString: string) => void,\n) {\n // Increment to reset the debounce counter.\n const [resetDebounceCounter, setResetDebounceCounter] = useState(0)\n const [commitAfterDebounce, setCommitAfterDebounce] = useState(false)\n\n // Note: we must use a reducer because we're tracking interrelated state variables. Attempts to separate these will\n // likely result in bugs such as dropped state changes when React performs a batch update.\n const [state, dispatch] = useReducer(\n (\n prevState: TableQueryReducerState,\n action: TableQueryReducerAction,\n ): TableQueryReducerState => {\n let updatedNextQueryRequest = prevState.nextQueryRequest\n switch (action.type) {\n case 'resetDebounce': {\n setResetDebounceCounter(v => v + 1)\n // Do not update state\n return prevState\n }\n case 'setQuery': {\n updatedNextQueryRequest = getQueryFromSetStateAction(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'addValueToSelectedFacet': {\n updatedNextQueryRequest = addValueToSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeSelectedFacet': {\n updatedNextQueryRequest = removeSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'setRangeFacetValue': {\n updatedNextQueryRequest = setRangeFacetValue(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeValueFromSelectedFacet': {\n updatedNextQueryRequest = removeValueFromSelectedFacet(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeQueryFilter': {\n updatedNextQueryRequest = removeQueryFilter(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'removeValueFromQueryFilter': {\n updatedNextQueryRequest = removeValueFromQueryFilter(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'setPageSize': {\n updatedNextQueryRequest = setPageSize(\n action,\n prevState.nextQueryRequest,\n )\n break\n }\n case 'goToPage': {\n updatedNextQueryRequest = goToPage(action, prevState.nextQueryRequest)\n break\n }\n case 'resetQuery': {\n updatedNextQueryRequest = initQueryRequest\n break\n }\n case 'cancelChanges':\n case 'resetUncommittedChanges': {\n updatedNextQueryRequest = prevState.currentQueryRequest\n break\n }\n case 'confirmChanges':\n case 'commitChanges': {\n updatedNextQueryRequest = cloneDeep(prevState.nextQueryRequest)\n // commit the changes\n break\n }\n }\n\n updatedNextQueryRequest.query = removeEmptyQueryParams(\n updatedNextQueryRequest.query,\n )\n\n const isConfirmingChange =\n requireConfirmationOnChange &&\n !queryRequestsHaveSameTotalResults(\n prevState.currentQueryRequest.query,\n updatedNextQueryRequest.query,\n )\n\n if (\n // No commit cases:\n // If `noCommit` is explicitly specified in the commit options\n (action.commitOptions && 'noCommit' in action.commitOptions) ||\n // Or if the user is in the 'confirm' state (modal is shown) AND\n // the action is not the 'confirmChanges' or 'cancelChanges' action.\n (isConfirmingChange &&\n action.type !== 'confirmChanges' &&\n action.type !== 'cancelChanges')\n ) {\n return {\n currentQueryRequest: prevState.currentQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: isConfirmingChange,\n }\n } else if (action.commitOptions && 'debounce' in action.commitOptions) {\n // If debouncing, update the state flag. An effect will commit the changes after the debounce delay.\n setCommitAfterDebounce(true)\n return {\n currentQueryRequest: prevState.currentQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: isConfirmingChange,\n }\n } else {\n // Commit case: clear the debounce flag and update state\n setCommitAfterDebounce(false)\n if (\n onQueryChange &&\n !isEqual(prevState.currentQueryRequest, updatedNextQueryRequest)\n ) {\n const queryJsonString = JSON.stringify(updatedNextQueryRequest.query)\n onQueryChange(queryJsonString)\n }\n return {\n currentQueryRequest: updatedNextQueryRequest,\n nextQueryRequest: updatedNextQueryRequest,\n isConfirmingChange: false,\n }\n }\n },\n {\n currentQueryRequest: initQueryRequest,\n nextQueryRequest: initQueryRequest,\n isConfirmingChange: false,\n },\n )\n\n useDebouncedEffect(\n () => {\n if (commitAfterDebounce) {\n dispatch({ type: 'commitChanges' })\n setCommitAfterDebounce(false)\n }\n },\n // nextQueryRequest MUST be included in the dependencies to ensure the debounce resets when it changes\n [\n state.nextQueryRequest,\n commitAfterDebounce,\n setCommitAfterDebounce,\n resetDebounceCounter,\n ],\n DEBOUNCE_DELAY_MS,\n )\n\n return useMemo(\n () => ({\n currentQueryRequest: state.currentQueryRequest,\n nextQueryRequest: state.nextQueryRequest,\n isConfirmingChange: state.isConfirmingChange,\n dispatch,\n }),\n [\n state.currentQueryRequest,\n state.isConfirmingChange,\n state.nextQueryRequest,\n ],\n )\n}\n"],"mappings":";;;;;;;AAwGA,SAAgB,EACd,GACA,GACA,GACA;CAEA,IAAM,CAAC,GAAsB,KAA2B,EAAS,EAAE,EAC7D,CAAC,GAAqB,KAA0B,EAAS,GAAM,EAI/D,CAAC,GAAO,KAAY,GAEtB,GACA,MAC2B;EAC3B,IAAI,IAA0B,EAAU;AACxC,UAAQ,EAAO,MAAf;GACE,KAAK,gBAGH,QAFA,GAAwB,MAAK,IAAI,EAAE,EAE5B;GAET,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EACxB,GACA,EAAU,iBACX;AACD;GAEF,KAAK;AACH,QAA0B,EAAS,GAAQ,EAAU,iBAAiB;AACtE;GAEF,KAAK;AACH,QAA0B;AAC1B;GAEF,KAAK;GACL,KAAK;AACH,QAA0B,EAAU;AACpC;GAEF,KAAK;GACL,KAAK;AACH,QAA0B,EAAU,EAAU,iBAAiB;AAE/D;;AAIJ,IAAwB,QAAQ,EAC9B,EAAwB,MACzB;EAED,IAAM,IACJ,KACA,CAAC,EACC,EAAU,oBAAoB,OAC9B,EAAwB,MACzB;AAmCD,SA9BC,EAAO,iBAAiB,cAAc,EAAO,iBAG7C,KACC,EAAO,SAAS,oBAChB,EAAO,SAAS,kBAEX;GACL,qBAAqB,EAAU;GAC/B,kBAAkB;GACE;GACrB,GACQ,EAAO,iBAAiB,cAAc,EAAO,iBAEtD,EAAuB,GAAK,EACrB;GACL,qBAAqB,EAAU;GAC/B,kBAAkB;GACE;GACrB,KAGD,EAAuB,GAAM,EAE3B,KACA,CAAC,EAAQ,EAAU,qBAAqB,EAAwB,IAGhE,EADwB,KAAK,UAAU,EAAwB,MACjD,CAAgB,EAEzB;GACL,qBAAqB;GACrB,kBAAkB;GAClB,oBAAoB;GACrB;IAGL;EACE,qBAAqB;EACrB,kBAAkB;EAClB,oBAAoB;EACrB,CACF;AAmBD,QAjBA,QACQ;AACJ,EAAI,MACF,EAAS,EAAE,MAAM,iBAAiB,CAAC,EACnC,EAAuB,GAAM;IAIjC;EACE,EAAM;EACN;EACA;EACA;EACD,EAAA,IAEF,EAEM,SACE;EACL,qBAAqB,EAAM;EAC3B,kBAAkB,EAAM;EACxB,oBAAoB,EAAM;EAC1B;EACD,GACD;EACE,EAAM;EACN,EAAM;EACN,EAAM;EACP,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useIsBot.js","names":[],"sources":["../../../src/utils/hooks/useIsBot.ts"],"sourcesContent":["import { useMemo } from 'react'\n\n/**\n * Hook that detects if the current user agent is likely a bot/crawler.\n * This is useful for automatically fetching all content for search engine indexing.\n *\n * @returns true if the user agent appears to be a bot, false otherwise\n */\nexport function useIsBot(): boolean {\n return useMemo(() => {\n if (typeof navigator === 'undefined' || !navigator.userAgent) {\n return false\n }\n\n const userAgent = navigator.userAgent.toLowerCase()\n\n // Common bot/crawler patterns\n const botPatterns = [\n 'bot',\n 'crawler',\n 'spider',\n 'crawling',\n 'google',\n 'bing',\n 'yahoo',\n 'baidu',\n 'yandex',\n 'duckduckbot',\n 'slurp',\n 'teoma',\n 'ia_archiver',\n 'bingpreview',\n 'facebookexternalhit',\n 'twitterbot',\n 'rogerbot',\n 'linkedinbot',\n 'embedly',\n 'quora link preview',\n 'showyoubot',\n 'outbrain',\n 'pinterest',\n 'slackbot',\n 'vkshare',\n 'w3c_validator',\n 'redditbot',\n 'applebot',\n 'whatsapp',\n 'flipboard',\n 'tumblr',\n 'bitlybot',\n 'skypeuripreview',\n 'nuzzel',\n 'discordbot',\n 'qwantify',\n 'pinterestbot',\n 'bitrix',\n ]\n\n return botPatterns.some(pattern => userAgent.includes(pattern))\n }, [])\n}\n"],"mappings":";;AAQA,SAAgB,IAAoB;AAClC,QAAO,QAAc;AACnB,MAAI,OAAO,YAAc,OAAe,CAAC,UAAU,UACjD,QAAO;EAGT,IAAM,IAAY,UAAU,UAAU,aAAa;AA4CnD,UAzCoB,wYAuCnB,EAEkB,MAAK,MAAW,EAAU,SAAS,EAAQ,CAAC;IAC9D,EAAE,CAAC"}
1
+ {"version":3,"file":"useIsBot.js","names":[],"sources":["../../../src/utils/hooks/useIsBot.ts"],"sourcesContent":["import { useMemo } from 'react'\n\n/**\n * Hook that detects if the current user agent is likely a bot/crawler.\n * This is useful for automatically fetching all content for search engine indexing.\n *\n * @returns true if the user agent appears to be a bot, false otherwise\n */\nexport function useIsBot(): boolean {\n return useMemo(() => {\n if (typeof navigator === 'undefined' || !navigator.userAgent) {\n return false\n }\n\n const userAgent = navigator.userAgent.toLowerCase()\n\n // Common bot/crawler patterns\n const botPatterns = [\n 'bot',\n 'crawler',\n 'spider',\n 'crawling',\n 'google',\n 'bing',\n 'yahoo',\n 'baidu',\n 'yandex',\n 'duckduckbot',\n 'slurp',\n 'teoma',\n 'ia_archiver',\n 'bingpreview',\n 'facebookexternalhit',\n 'twitterbot',\n 'rogerbot',\n 'linkedinbot',\n 'embedly',\n 'quora link preview',\n 'showyoubot',\n 'outbrain',\n 'pinterest',\n 'slackbot',\n 'vkshare',\n 'w3c_validator',\n 'redditbot',\n 'applebot',\n 'whatsapp',\n 'flipboard',\n 'tumblr',\n 'bitlybot',\n 'skypeuripreview',\n 'nuzzel',\n 'discordbot',\n 'qwantify',\n 'pinterestbot',\n 'bitrix',\n ]\n\n return botPatterns.some(pattern => userAgent.includes(pattern))\n }, [])\n}\n"],"mappings":";;AAQA,SAAgB,IAAoB;AAClC,QAAO,QAAc;AACnB,MAAI,OAAO,YAAc,OAAe,CAAC,UAAU,UACjD,QAAO;EAGT,IAAM,IAAY,UAAU,UAAU,aAAa;AA4CnD,UAAO,wYAAA,EAAY,MAAK,MAAW,EAAU,SAAS,EAAQ,CAAC;IAC9D,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useListState.js","names":[],"sources":["../../../src/utils/hooks/useListState.ts"],"sourcesContent":["import { Dispatch, SetStateAction, useState } from 'react'\n\nexport interface ListStateReturn<T> {\n // initial state of the list\n list: T[]\n // generates a function to update a specific index of the list\n handleListChange: (index: number) => (changedValue: T) => void\n // generates a function to remove a index from the list\n handleListRemove: (index: number) => () => void\n // generates a function to append to the end of the list\n appendToList: (...newItem: T[]) => void\n //changes the entire list state\n setList: Dispatch<SetStateAction<T[]>>\n}\n/**\n * This is used when a component's state uses a List<T> and has child components\n * that are responsible for creating, updating, deleting the objects within the List.\n *\n *\n * This should be used in conjunction with list.map() to generate child elements.\n *\n * The handle*() functions will generate a callback function for\n * the child to use to perform an item change, or removal on the list.\n *\n * Generally, appending items to the list will be handled by the parent\n * so appendToList() is just a regular function instead of a function generator\n * For Example:\n *\n * ```\n * export const ParentComponent: React.FunctionComponent<ParentComponentProps> = ({\n * prop1,\n * prop2,\n * }) => {\n * const {list: myList,\n * handleListChange: handleMyListChange,\n * handleListRemove: handleMyListPush,\n * appendToList: handleMyListRemove} = useListState<string>(['asdf','qwerty'])\n *\n * return (\n * <div>\n * myList.map((item, index) => {\n return <ChildComponent\n value={item}\n onChange={handleMyListChange(index)}\n onRemove={handleMyListRemove(index)}\n />\n })\n <button onClick={(event) => {appendToList(\"some new value\")} }> >Add Child</button>\n * </div>\n * )\n *\n * }\n * ```\n *\n *\n * @param initialState The initial value of the array\n * @returns an ListStateReturn object containing the useState value and additonal change/remove/push hnndlers. Use object destructuring\n */\nexport const useListState = <T>(initialState: T[]): ListStateReturn<T> => {\n const [list, setList] = useState<T[]>(initialState)\n\n const handleListChange =\n (index: number) =>\n (changedValue: T): void => {\n const modifiedList = [...list]\n modifiedList[index] = changedValue\n setList(modifiedList)\n }\n\n const handleListRemove = (index: number) => (): void => {\n const modifiedList = list.filter((value, arr_index) => index !== arr_index)\n setList(modifiedList)\n }\n\n const appendToList = (...newItem: T[]): void => {\n const modifiedList = [...list]\n modifiedList.push(...newItem)\n setList(modifiedList)\n }\n return { list, handleListChange, handleListRemove, appendToList, setList }\n}\n"],"mappings":";;AA0DA,IAAa,KAAmB,MAA0C;CACxE,IAAM,CAAC,GAAM,KAAW,EAAc,EAAa;AAoBnD,QAAO;EAAE;EAAM,mBAjBZ,OACA,MAA0B;GACzB,IAAM,IAAe,CAAC,GAAG,EAAK;AAE9B,GADA,EAAa,KAAS,GACtB,EAAQ,EAAa;;EAaQ,mBAVP,YAA8B;AAEtD,KADqB,EAAK,QAAQ,GAAO,MAAc,MAAU,EAAU,CACtD;;EAQ4B,eAL7B,GAAG,MAAuB;GAC9C,IAAM,IAAe,CAAC,GAAG,EAAK;AAE9B,GADA,EAAa,KAAK,GAAG,EAAQ,EAC7B,EAAQ,EAAa;;EAE0C;EAAS"}
1
+ {"version":3,"file":"useListState.js","names":[],"sources":["../../../src/utils/hooks/useListState.ts"],"sourcesContent":["import { Dispatch, SetStateAction, useState } from 'react'\n\nexport interface ListStateReturn<T> {\n // initial state of the list\n list: T[]\n // generates a function to update a specific index of the list\n handleListChange: (index: number) => (changedValue: T) => void\n // generates a function to remove a index from the list\n handleListRemove: (index: number) => () => void\n // generates a function to append to the end of the list\n appendToList: (...newItem: T[]) => void\n //changes the entire list state\n setList: Dispatch<SetStateAction<T[]>>\n}\n/**\n * This is used when a component's state uses a List<T> and has child components\n * that are responsible for creating, updating, deleting the objects within the List.\n *\n *\n * This should be used in conjunction with list.map() to generate child elements.\n *\n * The handle*() functions will generate a callback function for\n * the child to use to perform an item change, or removal on the list.\n *\n * Generally, appending items to the list will be handled by the parent\n * so appendToList() is just a regular function instead of a function generator\n * For Example:\n *\n * ```\n * export const ParentComponent: React.FunctionComponent<ParentComponentProps> = ({\n * prop1,\n * prop2,\n * }) => {\n * const {list: myList,\n * handleListChange: handleMyListChange,\n * handleListRemove: handleMyListPush,\n * appendToList: handleMyListRemove} = useListState<string>(['asdf','qwerty'])\n *\n * return (\n * <div>\n * myList.map((item, index) => {\n return <ChildComponent\n value={item}\n onChange={handleMyListChange(index)}\n onRemove={handleMyListRemove(index)}\n />\n })\n <button onClick={(event) => {appendToList(\"some new value\")} }> >Add Child</button>\n * </div>\n * )\n *\n * }\n * ```\n *\n *\n * @param initialState The initial value of the array\n * @returns an ListStateReturn object containing the useState value and additonal change/remove/push hnndlers. Use object destructuring\n */\nexport const useListState = <T>(initialState: T[]): ListStateReturn<T> => {\n const [list, setList] = useState<T[]>(initialState)\n\n const handleListChange =\n (index: number) =>\n (changedValue: T): void => {\n const modifiedList = [...list]\n modifiedList[index] = changedValue\n setList(modifiedList)\n }\n\n const handleListRemove = (index: number) => (): void => {\n const modifiedList = list.filter((value, arr_index) => index !== arr_index)\n setList(modifiedList)\n }\n\n const appendToList = (...newItem: T[]): void => {\n const modifiedList = [...list]\n modifiedList.push(...newItem)\n setList(modifiedList)\n }\n return { list, handleListChange, handleListRemove, appendToList, setList }\n}\n"],"mappings":";;AA0DA,IAAa,KAAmB,MAA0C;CACxE,IAAM,CAAC,GAAM,KAAW,EAAc,EAAa;AAoBnD,QAAO;EAAE;EAAM,mBAjBZ,OACA,MAA0B;GACzB,IAAM,IAAe,CAAC,GAAG,EAAK;AAE9B,GADA,EAAa,KAAS,GACtB,EAAQ,EAAa;;EAaQ,mBAVP,YAA8B;AAEtD,KADqB,EAAK,QAAQ,GAAO,MAAc,MAAU,EACzD,CAAa;;EAQ4B,eAL7B,GAAG,MAAuB;GAC9C,IAAM,IAAe,CAAC,GAAG,EAAK;AAE9B,GADA,EAAa,KAAK,GAAG,EAAQ,EAC7B,EAAQ,EAAa;;EAE0C;EAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLogin.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useLogin.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,0BAA0B,EAC3B,MAAM,8EAA8E,CAAA;AAErF,OAAO,EAIL,oBAAoB,EAErB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAuB,MAAM,OAAO,CAAA;AAGrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAA;AAExF,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,kBAAkB,CAAC,EAAE,0BAA0B,CAAA;IAE/C,uBAAuB,CAAC,EAAE,CACxB,UAAU,EAAE,IAAI,CAAC,0BAA0B,EAAE,YAAY,GAAG,QAAQ,CAAC,KAClE,IAAI,CAAA;CACV,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EACA,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,GACtB,WAAW,CAAA;IACf,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAC9D,yBAAyB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACvE,qBAAqB,EAAE,CACrB,IAAI,EAAE,MAAM,EAEZ,OAAO,CAAC,EAAE,oBAAoB,KAC3B,IAAI,CAAA;IAET,uBAAuB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7D,2BAA2B,EAAE,OAAO,CAAA;IACpC,2BAA2B,EAAE,OAAO,CAAA;IACpC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAUD;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,cAAc,CAuPtE"}
1
+ {"version":3,"file":"useLogin.d.ts","sourceRoot":"","sources":["../../../src/utils/hooks/useLogin.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,0BAA0B,EAC3B,MAAM,8EAA8E,CAAA;AAErF,OAAO,EAIL,oBAAoB,EAErB,MAAM,iCAAiC,CAAA;AAGxC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAuB,MAAM,OAAO,CAAA;AAGrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAA;AAGxF,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,kBAAkB,CAAC,EAAE,0BAA0B,CAAA;IAE/C,uBAAuB,CAAC,EAAE,CACxB,UAAU,EAAE,IAAI,CAAC,0BAA0B,EAAE,YAAY,GAAG,QAAQ,CAAC,KAClE,IAAI,CAAA;CACV,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EACA,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,GACtB,WAAW,CAAA;IACf,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAC9D,yBAAyB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACvE,qBAAqB,EAAE,CACrB,IAAI,EAAE,MAAM,EAEZ,OAAO,CAAC,EAAE,oBAAoB,KAC3B,IAAI,CAAA;IAET,uBAAuB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7D,2BAA2B,EAAE,OAAO,CAAA;IACpC,2BAA2B,EAAE,OAAO,CAAA;IACpC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAUD;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,cAAc,CA0PtE"}