synapse-react-client 4.0.44 → 4.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SWC.index.d.ts +1 -1
- package/dist/SWC.index.js +2 -2
- package/dist/SWC.index.js.map +1 -1
- package/dist/aridhia-queries/AridhiaKeyFactory.d.ts +1 -0
- package/dist/aridhia-queries/AridhiaKeyFactory.d.ts.map +1 -1
- package/dist/aridhia-queries/AridhiaKeyFactory.js +3 -0
- package/dist/aridhia-queries/AridhiaKeyFactory.js.map +1 -1
- package/dist/aridhia-queries/aridhiaTokenExchange.js +6 -6
- package/dist/aridhia-queries/index.d.ts +1 -0
- package/dist/aridhia-queries/index.d.ts.map +1 -1
- package/dist/aridhia-queries/index.js +11 -10
- package/dist/aridhia-queries/useGetAridhiaVocabularyTerm.d.ts +19 -0
- package/dist/aridhia-queries/useGetAridhiaVocabularyTerm.d.ts.map +1 -0
- package/dist/aridhia-queries/useGetAridhiaVocabularyTerm.js +14 -0
- package/dist/aridhia-queries/useGetAridhiaVocabularyTerm.js.map +1 -0
- package/dist/components/AccessRequirementList/AccessApprovalCheckMark.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js +6 -4
- package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.d.ts +2 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.js +126 -125
- package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.d.ts +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListItem.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/AccessorRequirementHelpText.js +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/AccessorRequirementHelpText.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js +28 -16
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js +99 -97
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ResearchProjectForm/ResearchProjectForm.js +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ResearchProjectForm/ResearchProjectForm.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ReviewCollaboratorsAndSigningOfficialAccordion.js +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ReviewCollaboratorsAndSigningOfficialAccordion.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.js +11 -8
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/requestFlowTypes.d.ts +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/requestFlowTypes.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/RequestDataStep.d.ts +1 -4
- package/dist/components/AccessRequirementList/RequestDataStep.d.ts.map +1 -1
- package/dist/components/AccessRequirementList/RequestDataStep.js +15 -18
- package/dist/components/AccessRequirementList/RequestDataStep.js.map +1 -1
- package/dist/components/AccessTokenPage/AccessTokenPage.js +1 -1
- package/dist/components/AccessTokenPage/AccessTokenPage.js.map +1 -1
- package/dist/components/AcknowledgementsPage/AcknowledgementsPage.js +1 -1
- package/dist/components/AcknowledgementsPage/AcknowledgementsPage.js.map +1 -1
- package/dist/components/Aridhia/DarWizard/DarSectionForm.d.ts.map +1 -1
- package/dist/components/Aridhia/DarWizard/DarSectionForm.js +17 -13
- package/dist/components/Aridhia/DarWizard/DarSectionForm.js.map +1 -1
- package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.d.ts +3 -3
- package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.d.ts.map +1 -1
- package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.js +33 -34
- package/dist/components/Aridhia/DarWizard/DarSubmittedPanel.js.map +1 -1
- package/dist/components/Aridhia/fairFormToRjsf.js +1 -1
- package/dist/components/Aridhia/fairFormToRjsf.js.map +1 -1
- package/dist/components/Authentication/LastLoginInfo.js +1 -1
- package/dist/components/Authentication/LastLoginInfo.js.map +1 -1
- package/dist/components/CertificationQuiz/CertificationQuiz.js +1 -1
- package/dist/components/CertificationQuiz/CertificationQuiz.js.map +1 -1
- package/dist/components/ChallengeSubmission/AvailableEvaluationQueueList.js +1 -1
- package/dist/components/ChallengeSubmission/AvailableEvaluationQueueList.js.map +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js.map +1 -1
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js +6 -6
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/ChallengeTeamTable.d.ts.map +1 -1
- package/dist/components/ChallengeTeamWizard/ChallengeTeamTable.js +4 -4
- package/dist/components/ChallengeTeamWizard/ChallengeTeamTable.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/SelectChallengeTeam.js +4 -4
- package/dist/components/CitationPopover/CitationPopoverContent.js +4 -4
- package/dist/components/CitationPopover/CitationPopoverContent.js.map +1 -1
- package/dist/components/ComponentCollapse.d.ts.map +1 -1
- package/dist/components/ComponentCollapse.js +2 -1
- package/dist/components/ComponentCollapse.js.map +1 -1
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.d.ts.map +1 -1
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.js +52 -49
- package/dist/components/CreateOrUpdateAccessRequirementWizard/CreateOrUpdateAccessRequirementWizard.js.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.js +231 -219
- package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
- package/dist/components/DataGrid/SyncGridWithSourceButton.d.ts.map +1 -1
- package/dist/components/DataGrid/SyncGridWithSourceButton.js +5 -4
- package/dist/components/DataGrid/SyncGridWithSourceButton.js.map +1 -1
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.d.ts.map +1 -1
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.js +7 -5
- package/dist/components/DataGrid/components/ColumnHeaderWithTooltip.js.map +1 -1
- package/dist/components/DataGrid/components/ExportCsvFromGridButton.js +4 -4
- package/dist/components/DataGrid/components/ExportCsvFromGridButton.js.map +1 -1
- package/dist/components/DataGrid/components/ReorderColumnsButton.js +4 -4
- package/dist/components/DataGrid/components/ReorderColumnsButton.js.map +1 -1
- package/dist/components/DataGrid/components/ReorderColumnsDialog.d.ts.map +1 -1
- package/dist/components/DataGrid/components/ReorderColumnsDialog.js +12 -8
- package/dist/components/DataGrid/components/ReorderColumnsDialog.js.map +1 -1
- package/dist/components/DataGrid/components/UploadCsvToGridButton.js +1 -1
- package/dist/components/DataGrid/components/UploadCsvToGridButton.js.map +1 -1
- package/dist/components/DataGrid/hooks/useGridUndoRedo.js +5 -5
- package/dist/components/DataGrid/utils/applyModelChange.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/applyModelChange.js +14 -14
- package/dist/components/DataGrid/utils/applyModelChange.js.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.js +1 -1
- package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
- package/dist/components/DataGrid/utils/splitPatch.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/splitPatch.js +22 -13
- package/dist/components/DataGrid/utils/splitPatch.js.map +1 -1
- package/dist/components/DownloadCart/DownloadCartPage.js +1 -1
- package/dist/components/DownloadCart/DownloadCartPage.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.d.ts.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js +2 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss +1 -3
- package/dist/components/EntityBadgeIcons/EntityBadgeIcons.d.ts.map +1 -1
- package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js +16 -10
- package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
- package/dist/components/EntityDownloadButton/EntityDownloadButton.js +1 -1
- package/dist/components/EntityDownloadButton/EntityDownloadButton.js.map +1 -1
- package/dist/components/EntityFinder/EntityFinder.js +4 -4
- package/dist/components/EntityFinder/EntityIdTextField.js +1 -1
- package/dist/components/EntityFinder/EntityIdTextField.js.map +1 -1
- package/dist/components/EntityFinder/tree/VirtualizedTree.d.ts.map +1 -1
- package/dist/components/EntityFinder/tree/VirtualizedTree.js +5 -4
- package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
- package/dist/components/EntityHeaderTable/EntityHeaderTable.js +1 -1
- package/dist/components/EntityHeaderTable/EntityHeaderTable.js.map +1 -1
- package/dist/components/EntityTreeTable/components/EntityNameCell.d.ts.map +1 -1
- package/dist/components/EntityTreeTable/components/EntityNameCell.js +2 -1
- package/dist/components/EntityTreeTable/components/EntityNameCell.js.map +1 -1
- package/dist/components/EntityUpload/FileUploadProgress.d.ts.map +1 -1
- package/dist/components/EntityUpload/FileUploadProgress.js +11 -7
- package/dist/components/EntityUpload/FileUploadProgress.js.map +1 -1
- package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptionsList.d.ts.map +1 -1
- package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptionsList.js +4 -3
- package/dist/components/Evaluation/round_limits/EvaluationRoundLimitOptionsList.js.map +1 -1
- package/dist/components/ExperimentalMode/ExperimentalMode.js +1 -1
- package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
- package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js +1 -1
- package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js.map +1 -1
- package/dist/components/FormTemplateEditor/ChoiceOptionsEditor.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/ChoiceOptionsEditor.js +2 -1
- package/dist/components/FormTemplateEditor/ChoiceOptionsEditor.js.map +1 -1
- package/dist/components/FormTemplateEditor/FieldDefinitionDrawer.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/FieldDefinitionDrawer.js +2 -1
- package/dist/components/FormTemplateEditor/FieldDefinitionDrawer.js.map +1 -1
- package/dist/components/FormTemplateEditor/FieldLibrary.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/FieldLibrary.js +2 -1
- package/dist/components/FormTemplateEditor/FieldLibrary.js.map +1 -1
- package/dist/components/FormTemplateEditor/FieldLibraryRow.js +4 -4
- package/dist/components/FormTemplateEditor/FieldLibraryRow.js.map +1 -1
- package/dist/components/FormTemplateEditor/FormStructurePanel.js +1 -1
- package/dist/components/FormTemplateEditor/FormStructurePanel.js.map +1 -1
- package/dist/components/FormTemplateEditor/FormTemplateEditorForm.js +1 -1
- package/dist/components/FormTemplateEditor/FormTemplateEditorForm.js.map +1 -1
- package/dist/components/FormTemplateEditor/StepCard.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/StepCard.js +15 -10
- package/dist/components/FormTemplateEditor/StepCard.js.map +1 -1
- package/dist/components/FormTemplateEditor/StepFieldRow.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/StepFieldRow.js +11 -6
- package/dist/components/FormTemplateEditor/StepFieldRow.js.map +1 -1
- package/dist/components/FormTemplateEditor/TemplateFileHandleField.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/TemplateFileHandleField.js +2 -1
- package/dist/components/FormTemplateEditor/TemplateFileHandleField.js.map +1 -1
- package/dist/components/FormTemplateEditor/useSaveFormTemplate.d.ts.map +1 -1
- package/dist/components/FormTemplateEditor/useSaveFormTemplate.js.map +1 -1
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.d.ts +9 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.d.ts.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.js +45 -0
- package/dist/components/FormTemplateReferenceField/FormTemplatePreviewDialog.js.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.d.ts +22 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.d.ts.map +1 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.js +139 -0
- package/dist/components/FormTemplateReferenceField/FormTemplateReferenceField.js.map +1 -0
- package/dist/components/Forum/ForumTable.d.ts +17 -5
- package/dist/components/Forum/ForumTable.d.ts.map +1 -1
- package/dist/components/Forum/ForumTable.js +94 -77
- package/dist/components/Forum/ForumTable.js.map +1 -1
- package/dist/components/GenericCard/DuoTermTags/DuoTermTags.js +45 -45
- package/dist/components/GenericCard/PortalDOI/PortalDOI.js +5 -5
- package/dist/components/GenericCard/PortalDOI/PortalDOI.js.map +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.js +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
- package/dist/components/HelpPopover/HelpPopover.js +1 -1
- package/dist/components/HelpPopover/HelpPopover.js.map +1 -1
- package/dist/components/IconSvg/IconSvg.d.ts.map +1 -1
- package/dist/components/IconSvg/IconSvg.js +154 -47
- package/dist/components/IconSvg/IconSvg.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ButtonTemplate.d.ts.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ButtonTemplate.js +19 -16
- package/dist/components/JsonSchemaForm/templates/ButtonTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.d.ts.map +1 -1
- package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js +2 -1
- package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js.map +1 -1
- package/dist/components/JsonSchemaForm/widgets/PhoneWidget.d.ts +9 -0
- package/dist/components/JsonSchemaForm/widgets/PhoneWidget.d.ts.map +1 -0
- package/dist/components/JsonSchemaForm/widgets/PhoneWidget.js +21 -0
- package/dist/components/JsonSchemaForm/widgets/PhoneWidget.js.map +1 -0
- package/dist/components/Markdown/MarkdownCollapse.d.ts.map +1 -1
- package/dist/components/Markdown/MarkdownCollapse.js +3 -1
- package/dist/components/Markdown/MarkdownCollapse.js.map +1 -1
- package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js +1 -1
- package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js.map +1 -1
- package/dist/components/OAuthClientManagement/OAuthManagement.js +14 -13
- package/dist/components/OAuthClientManagement/OAuthManagement.js.map +1 -1
- package/dist/components/PortalList/PortalCard.d.ts.map +1 -1
- package/dist/components/PortalList/PortalCard.js +6 -5
- package/dist/components/PortalList/PortalCard.js.map +1 -1
- package/dist/components/PortalList/PortalList.js +4 -4
- package/dist/components/PortalList/PortalList.js.map +1 -1
- package/dist/components/ProjectVisibilityChip/ProjectVisibilityChip.js +6 -6
- package/dist/components/QueryBuilder/FilterConditionRow.js +4 -4
- package/dist/components/QueryCountButton/QueryCountButton.js +5 -5
- package/dist/components/QueryCountButton/QueryCountButton.js.map +1 -1
- package/dist/components/ReleaseCard/ReleaseCardLarge.js +5 -5
- package/dist/components/ReleaseCard/ReleaseCardLarge.js.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.d.ts.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js +142 -108
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.d.ts.map +1 -1
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.js +15 -15
- package/dist/components/SetBasicAccessRequirementFields/AccessorRequirements.js.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.d.ts.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js +57 -55
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
- package/dist/components/ShareThisPage/ShareThisPage.js +4 -4
- package/dist/components/ShareThisPage/ShareThisPage.js.map +1 -1
- package/dist/components/SustainabilityScorecard/DialLayout.js +1 -1
- package/dist/components/SustainabilityScorecard/DialLayout.js.map +1 -1
- package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js +1 -1
- package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChat.js +1 -1
- package/dist/components/SynapseChat/SynapseChat.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChatInteraction.d.ts.map +1 -1
- package/dist/components/SynapseChat/SynapseChatInteraction.js +2 -1
- package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
- package/dist/components/SynapseChat/components/ChatInputArea/ChatInputArea.js +1 -1
- package/dist/components/SynapseChat/components/ChatInputArea/ChatInputArea.js.map +1 -1
- package/dist/components/SynapseFooter/SynapseFooter.js +1 -1
- package/dist/components/SynapseFooter/SynapseFooter.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseFeaturedDatasetItem.js +1 -1
- package/dist/components/SynapseHomepageV2/SynapseFeaturedDatasetItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageNavBar.d.ts.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageNavBar.js +2 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageNavBar.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseInActionItem.js +4 -4
- package/dist/components/SynapseHomepageV2/SynapseInActionItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseTrendingProjectsItem.js +1 -1
- package/dist/components/SynapseHomepageV2/SynapseTrendingProjectsItem.js.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js +4 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js.map +1 -1
- package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js +1 -1
- package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchResultsCard.d.ts.map +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchResultsCard.js +12 -9
- package/dist/components/SynapseSearchPageResults/SynapseSearchResultsCard.js.map +1 -1
- package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js +4 -4
- package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js.map +1 -1
- package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js +140 -140
- package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js +1 -1
- package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js +9 -9
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormActions.d.ts.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormActions.js +6 -4
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormActions.js.map +1 -1
- package/dist/components/TanStackTable/ColumnHeader.js +1 -1
- package/dist/components/TanStackTable/ColumnHeader.js.map +1 -1
- package/dist/components/TeamSubjectsSelector/TeamBadgeOrError.js +1 -1
- package/dist/components/TeamSubjectsSelector/TeamBadgeOrError.js.map +1 -1
- package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.d.ts.map +1 -1
- package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js +4 -3
- package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js.map +1 -1
- package/dist/components/Webhook/WebhookDashboard.js +1 -1
- package/dist/components/Webhook/WebhookDashboard.js.map +1 -1
- package/dist/components/dataaccess/AccessRequirementDashboard.js +4 -4
- package/dist/components/dataaccess/AccessRequirementDashboard.js.map +1 -1
- package/dist/components/dataaccess/AccessRequirementTable.js +1 -1
- package/dist/components/dataaccess/AccessRequirementTable.js.map +1 -1
- package/dist/components/dataaccess/EDucTemplateTable.d.ts.map +1 -1
- package/dist/components/dataaccess/EDucTemplateTable.js +13 -11
- package/dist/components/dataaccess/EDucTemplateTable.js.map +1 -1
- package/dist/components/dataaccess/FormTemplateTable.js +6 -6
- package/dist/components/dataaccess/FormTemplateTable.js.map +1 -1
- package/dist/components/dataaccess/ReviewerDashboard.js +17 -17
- package/dist/components/dataaccess/ReviewerDashboard.js.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js +4 -4
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.d.ts.map +1 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js +4 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js.map +1 -1
- package/dist/components/doi/CreateOrUpdateDoiModal.js +1 -1
- package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
- package/dist/components/download_list/DownloadDetails.d.ts.map +1 -1
- package/dist/components/download_list/DownloadDetails.js +12 -10
- package/dist/components/download_list/DownloadDetails.js.map +1 -1
- package/dist/components/entity/metadata/AnnotationsTable.js +1 -1
- package/dist/components/entity/metadata/AnnotationsTable.js.map +1 -1
- package/dist/components/entity/page/CreatedByModifiedBy.js +1 -1
- package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.d.ts.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
- package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js +1 -1
- package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js.map +1 -1
- package/dist/components/error/ErrorChip.d.ts.map +1 -1
- package/dist/components/error/ErrorChip.js +2 -1
- package/dist/components/error/ErrorChip.js.map +1 -1
- package/dist/components/table/CsvPreview/CreateTableFromCsvDialog.js +14 -14
- package/dist/components/table/CsvPreview/CsvPreviewWithOptions.js +4 -4
- package/dist/components/table/CsvPreview/CsvPreviewWithOptions.js.map +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.js +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
- package/dist/features/curator/dashboard/components/NextStepButton.d.ts.map +1 -1
- package/dist/features/curator/dashboard/components/NextStepButton.js +7 -6
- package/dist/features/curator/dashboard/components/NextStepButton.js.map +1 -1
- package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js +8 -8
- package/dist/features/entity/metadata-task/components/CreateOrUpdateCurationTaskDialog.js.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +4 -4
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksPage.js +4 -4
- package/dist/features/entity/metadata-task/components/MetadataTasksPage.js.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js.map +1 -1
- package/dist/features/entity/metadata-task/create-task/CurateTaskForm.js +4 -4
- package/dist/features/entity/metadata-task/create-task/CurateTaskForm.js.map +1 -1
- package/dist/features/entity/metadata-task/create-task/TaskCategoryCard.js +1 -1
- package/dist/features/entity/metadata-task/create-task/TaskCategoryCard.js.map +1 -1
- package/dist/features/entity/metadata-task/create-task/TaskFormHeader.js +1 -1
- package/dist/features/entity/metadata-task/create-task/TaskFormHeader.js.map +1 -1
- package/dist/features/entity/metadata-task/create-task/curationTaskFlowRoutes.js +10 -10
- package/dist/features/entity/metadata-task/create-task/curationTaskFlowRoutes.js.map +1 -1
- package/dist/features/team/invitation/components/OpenInvitationsToUserCard.js +1 -1
- package/dist/features/team/invitation/components/OpenInvitationsToUserCard.js.map +1 -1
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.d.ts +5 -0
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.d.ts.map +1 -1
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.js +37 -26
- package/dist/mocks/accessRequirement/mockJsonSchemaAccessRequirements.js.map +1 -1
- package/dist/mocks/msw/handlers/aridhiaHandlers.d.ts +8 -0
- package/dist/mocks/msw/handlers/aridhiaHandlers.d.ts.map +1 -1
- package/dist/mocks/msw/handlers/aridhiaHandlers.js +52 -41
- package/dist/mocks/msw/handlers/aridhiaHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.d.ts.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.js +40 -33
- package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/entityHandlers.d.ts.map +1 -1
- package/dist/mocks/msw/handlers/entityHandlers.js +70 -67
- package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/gridHandlers.js +2 -2
- package/dist/mocks/msw/handlers/gridHandlers.js.map +1 -1
- package/dist/mocks/msw/server.d.ts.map +1 -1
- package/dist/mocks/msw/server.js +4 -2
- package/dist/mocks/msw/server.js.map +1 -1
- package/dist/synapse-client/SynapseClient.d.ts +10 -0
- package/dist/synapse-client/SynapseClient.d.ts.map +1 -1
- package/dist/synapse-client/SynapseClient.js +486 -482
- package/dist/synapse-client/SynapseClient.js.map +1 -1
- package/dist/synapse-client/index.js +3 -3
- package/dist/synapse-queries/KeyFactory.d.ts +13 -1
- package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
- package/dist/synapse-queries/KeyFactory.js +11 -1
- package/dist/synapse-queries/KeyFactory.js.map +1 -1
- package/dist/synapse-queries/dataaccess/index.js +4 -4
- package/dist/synapse-queries/dataaccess/useAccessRequirements.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useAccessRequirements.js +4 -1
- package/dist/synapse-queries/dataaccess/useAccessRequirements.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useEDuc.d.ts +19 -5
- package/dist/synapse-queries/dataaccess/useEDuc.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useEDuc.js +29 -21
- package/dist/synapse-queries/dataaccess/useEDuc.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useFormTemplate.d.ts +12 -0
- package/dist/synapse-queries/dataaccess/useFormTemplate.d.ts.map +1 -1
- package/dist/synapse-queries/dataaccess/useFormTemplate.js +52 -30
- package/dist/synapse-queries/dataaccess/useFormTemplate.js.map +1 -1
- package/dist/synapse-queries/forum/useForum.d.ts +5 -1
- package/dist/synapse-queries/forum/useForum.d.ts.map +1 -1
- package/dist/synapse-queries/forum/useForum.js +23 -1
- package/dist/synapse-queries/forum/useForum.js.map +1 -1
- package/dist/synapse-queries/index.js +185 -184
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.d.ts +5 -3
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.d.ts.map +1 -1
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.js +12 -15
- package/dist/synapse-queries/jsonschema/useCreateJsonSchema.js.map +1 -1
- package/dist/synapse-queries/user/useGetUserChallenges.d.ts.map +1 -1
- package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
- package/dist/theme/DefaultTheme.d.ts.map +1 -1
- package/dist/theme/DefaultTheme.js +4 -1
- package/dist/theme/DefaultTheme.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/APIConstants.d.ts.map +1 -1
- package/dist/utils/APIConstants.js.map +1 -1
- package/dist/utils/AppUtils/AppUtils.js +1 -1
- package/dist/utils/AppUtils/AppUtils.js.map +1 -1
- package/dist/utils/featureflag/FeatureFlags.d.ts.map +1 -1
- package/dist/utils/featureflag/FeatureFlags.js.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.d.ts.map +1 -1
- package/dist/utils/functions/EntityTypeUtils.js.map +1 -1
- package/dist/utils/functions/getEndpoint.d.ts +1 -0
- package/dist/utils/functions/getEndpoint.d.ts.map +1 -1
- package/dist/utils/functions/getEndpoint.js +3 -3
- package/dist/utils/functions/getEndpoint.js.map +1 -1
- package/dist/utils/types/RequestableAccessRequirement.d.ts +8 -0
- package/dist/utils/types/RequestableAccessRequirement.d.ts.map +1 -0
- package/dist/utils/types/RequestableAccessRequirement.js +9 -0
- package/dist/utils/types/RequestableAccessRequirement.js.map +1 -0
- package/package.json +6 -5
- package/dist/testutils/ReactQueryMockUtils.js +0 -676
- package/dist/testutils/ReactQueryMockUtils.js.map +0 -1
|
@@ -14,10 +14,11 @@ import { Box as _, Button as v, IconButton as y, LinearProgress as ee, Paper as
|
|
|
14
14
|
import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
15
15
|
import { FileHandleAssociateType as te } from "@sage-bionetworks/synapse-types";
|
|
16
16
|
import { useQueryClient as ne } from "@tanstack/react-query";
|
|
17
|
-
import
|
|
17
|
+
import re from "@mui/icons-material/Close";
|
|
18
18
|
import T from "dayjs";
|
|
19
19
|
import E from "dayjs/plugin/relativeTime";
|
|
20
20
|
import D from "dayjs/plugin/duration";
|
|
21
|
+
import ie from "@mui/icons-material/Download";
|
|
21
22
|
//#region src/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.tsx
|
|
22
23
|
function ae(e) {
|
|
23
24
|
return "showDirectoryPicker" in e;
|
package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadIneligibleForPackagingFilesFromListButton.js","names":[],"sources":["../../../src/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.tsx"],"sourcesContent":["import { useSynapseContext } from '@/utils'\nimport { implementsExternalFileHandleInterface } from '@/utils/types/IsType'\nimport { calculateFriendlyFileSize } from '@/utils/functions/calculateFriendlyFileSize'\nimport {\n Box,\n Button,\n IconButton,\n LinearProgress,\n Paper,\n Typography,\n} from '@mui/material'\nimport { Close, Download } from '@mui/icons-material'\nimport CloudWarning from '@/assets/icons/CloudWarning'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport dayjs from 'dayjs'\nimport duration from 'dayjs/plugin/duration'\nimport relativeTime from 'dayjs/plugin/relativeTime'\nimport {\n BatchFileRequest,\n BatchFileResult,\n ExternalFileHandle,\n FileEntity,\n FileHandleAssociation,\n FileHandleAssociateType,\n} from '@sage-bionetworks/synapse-types'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport {\n useGetAvailableFilesToDownloadInfinite,\n useGetDownloadListStatistics,\n useRemoveFilesFromDownloadList,\n} from '@/synapse-queries/download/useDownloadList'\nimport { getFiles } from '@/synapse-client/SynapseClient'\nimport { useGetEntityQueryOptions } from '@/synapse-queries/entity/useEntity'\n\nimport { deduplicateFileName, getFileName } from './fileNameUtils'\nimport styles from './DownloadIneligibleForPackagingFilesFromListButton.module.scss'\n\n// showDirectoryPicker is not yet available on the Window interface.\ninterface FileSystemAccessWindow extends Window {\n showDirectoryPicker(options?: {\n mode?: 'read' | 'readwrite'\n startIn?:\n | 'desktop'\n | 'documents'\n | 'downloads'\n | 'music'\n | 'pictures'\n | 'videos'\n }): Promise<FileSystemDirectoryHandle>\n}\n\n/**\n * Type guard to check if File System Access API is available\n */\nfunction supportsFileSystemAccess(\n window: Window,\n): window is FileSystemAccessWindow {\n return 'showDirectoryPicker' in window\n}\n\nconst FILE_BATCH_SIZE = 50 // Maximum batch size for fetching presigned URLs\nconst ETA_DELAY_MS = 3000 // Wait this long before showing ETA\n\ndayjs.extend(duration)\ndayjs.extend(relativeTime)\n\nexport type DownloadIneligibleForPackagingFilesFromListButtonProps = {\n buttonText?: string\n variant?: 'text' | 'outlined' | 'contained'\n}\n\n/**\n * Button component that downloads files from the user's download list that are ineligible for packaging.\n * Uses the infinite query hook to fetch all pages of download list items,\n * batches requests to get presigned URLs, and triggers browser downloads.\n */\nexport function DownloadIneligibleForPackagingFilesFromListButton(\n props: DownloadIneligibleForPackagingFilesFromListButtonProps,\n) {\n const { buttonText = 'Start Multi-file Download', variant = 'contained' } =\n props\n const { accessToken, isAuthenticated } = useSynapseContext()\n const queryClient = useQueryClient()\n const getEntityQueryOptions = useGetEntityQueryOptions<FileEntity>()\n const [isDownloading, setIsDownloading] = useState(false)\n const isDownloadingRef = useRef(false)\n const [showExternalFileWarning, setShowExternalFileWarning] = useState(false)\n const isCancelledRef = useRef(false)\n const abortControllerRef = useRef<AbortController | null>(null)\n const [downloadProgress, setDownloadProgress] = useState<{\n currentFile: string\n fileIndex: number\n totalFiles: number\n bytesDownloaded: number\n totalBytes: number\n estimatedSecondsRemaining: number | null\n } | null>(null)\n const fileDownloadStartRef = useRef<number | null>(null)\n\n // Accessibility: tracks the last announced file index and progress bucket so\n // the aria-live region only updates on meaningful changes (not every chunk).\n const [announceText, setAnnounceText] = useState('')\n const lastAnnouncedFileIndexRef = useRef<number>(-1)\n const lastAnnouncedPercentBucketRef = useRef<number>(-1)\n\n useEffect(() => {\n if (!downloadProgress) {\n lastAnnouncedFileIndexRef.current = -1\n lastAnnouncedPercentBucketRef.current = -1\n setAnnounceText('')\n return\n }\n\n const { fileIndex, totalFiles, currentFile, bytesDownloaded, totalBytes } =\n downloadProgress\n\n // Announce when a new file starts downloading\n if (fileIndex !== lastAnnouncedFileIndexRef.current) {\n lastAnnouncedFileIndexRef.current = fileIndex\n lastAnnouncedPercentBucketRef.current = -1\n setAnnounceText(\n `Downloading file ${fileIndex} of ${totalFiles}: ${currentFile}`,\n )\n return\n }\n\n // Announce at each 25% milestone (25, 50, 75, 100) once per file\n if (totalBytes > 0) {\n const percent = Math.floor((bytesDownloaded / totalBytes) * 100)\n const bucket = Math.floor(percent / 25) * 25\n if (bucket > 0 && bucket !== lastAnnouncedPercentBucketRef.current) {\n lastAnnouncedPercentBucketRef.current = bucket\n setAnnounceText(`${bucket}% of ${currentFile} downloaded`)\n }\n }\n }, [downloadProgress])\n\n const { data, status, hasNextPage, fetchNextPage, error } =\n useGetAvailableFilesToDownloadInfinite()\n\n const { mutateAsync: removeFilesFromDownloadList } =\n useRemoveFilesFromDownloadList()\n\n const { refetch: refetchDownloadListStatistics } =\n useGetDownloadListStatistics()\n\n /**\n * Downloads all files from the download list.\n */\n const downloadAllFiles = useCallback(\n async (downloadListItems: typeof data) => {\n // Prevent re-entry if already downloading\n if (isDownloadingRef.current) {\n return\n }\n\n if (!downloadListItems || !isAuthenticated) {\n setIsDownloading(false)\n return\n }\n\n try {\n isDownloadingRef.current = true\n isCancelledRef.current = false\n setShowExternalFileWarning(false)\n const allItems = downloadListItems.pages.flatMap(page => page.page)\n\n // Filter to only include files that are NOT eligible for packaging\n const nonPackageableFiles = allItems.filter(\n item => !item.isEligibleForPackaging,\n )\n\n if (nonPackageableFiles.length === 0) {\n displayToast('No non-packageable files available to download', 'info')\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n\n // Determine if File System Access API is available\n const useFileSystemAccess = supportsFileSystemAccess(window)\n\n // Prompt user to select a directory for downloads BEFORE async operations\n // (must be called during user gesture to avoid SecurityError)\n let directoryHandle: FileSystemDirectoryHandle | undefined\n\n if (useFileSystemAccess) {\n // TypeScript narrowing: we know window has showDirectoryPicker at this point\n const fileSystemWindow = window as unknown as FileSystemAccessWindow\n\n try {\n directoryHandle = await fileSystemWindow.showDirectoryPicker({\n mode: 'readwrite',\n })\n } catch (error) {\n // User cancelled directory picker\n console.error('Failed to select directory:', error)\n displayToast(\n 'Directory selection is required to directly download files from the website.',\n 'warning',\n )\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n } else {\n // Show info message for browsers without File System Access API\n displayToast(\n 'Your browser will download files individually to your default downloads folder. Each file may trigger a separate download prompt.',\n 'info',\n )\n }\n\n // Initialize progress tracking\n fileDownloadStartRef.current = null\n setDownloadProgress({\n currentFile: '',\n fileIndex: 0,\n totalFiles: nonPackageableFiles.length,\n bytesDownloaded: 0,\n totalBytes: 0,\n estimatedSecondsRemaining: null,\n })\n\n // Fetch entities to get dataFileHandleId for each file\n // Use React Query's ensureQueryData to leverage cache and built-in retry logic (handles 429s with exponential backoff)\n // Map key combines entity ID and version to handle multiple versions of the same entity\n const entityIdToFileHandleId = new Map<string, string>()\n\n // Fetch all entities in parallel - React Query handles concurrency and retries\n const entityPromises = nonPackageableFiles.map(async item => {\n if (isCancelledRef.current) {\n return\n }\n\n try {\n const entity = await queryClient.ensureQueryData(\n getEntityQueryOptions(\n item.fileEntityId,\n item.versionNumber?.toString(),\n ),\n )\n // Use composite key: entityId-version (or entityId if no version)\n const mapKey = `${entity.id!}-${item.versionNumber ?? 'latest'}`\n entityIdToFileHandleId.set(mapKey, entity.dataFileHandleId)\n } catch (error) {\n console.error(`Failed to fetch entity ${item.fileEntityId}:`, error)\n // Continue even if one entity fails\n }\n })\n\n await Promise.all(entityPromises)\n\n // Convert download list items to file handle associations using the fetched dataFileHandleIds\n const fileHandleAssociations: FileHandleAssociation[] =\n nonPackageableFiles\n .filter(item => {\n const mapKey = `${item.fileEntityId}-${\n item.versionNumber ?? 'latest'\n }`\n return entityIdToFileHandleId.has(mapKey)\n })\n .map(item => {\n const mapKey = `${item.fileEntityId}-${\n item.versionNumber ?? 'latest'\n }`\n return {\n fileHandleId: entityIdToFileHandleId.get(mapKey)!,\n associateObjectId: item.fileEntityId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n }\n })\n\n if (fileHandleAssociations.length === 0) {\n displayToast(\n 'Failed to fetch file information for download',\n 'danger',\n )\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n\n // Split into batches to avoid overwhelming the API\n const batches: FileHandleAssociation[][] = []\n for (\n let i = 0;\n i < fileHandleAssociations.length;\n i += FILE_BATCH_SIZE\n ) {\n batches.push(fileHandleAssociations.slice(i, i + FILE_BATCH_SIZE))\n }\n\n let downloadedCount = 0\n let failedCount = 0\n const successfullyDownloadedItems: typeof nonPackageableFiles = []\n\n // Track used filenames to prevent collisions\n const usedFilenames = new Map<string, number>()\n\n /**\n * Helper function to download file using traditional anchor tag method\n * @returns boolean indicating if the download was successful\n */\n const downloadFileTraditional = async (\n downloadUrl: string,\n fileName: string,\n ): Promise<boolean> => {\n try {\n const response = await fetch(downloadUrl)\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = blobUrl\n anchor.download = fileName\n anchor.style.display = 'none'\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n\n // Clean up blob URL after a delay to ensure download initiation\n setTimeout(() => URL.revokeObjectURL(blobUrl), 1000)\n\n return true\n } catch (error) {\n console.error(`Failed to download file ${fileName}:`, error)\n // Fallback to window.open method\n try {\n window.open(downloadUrl, '_blank', 'noopener,noreferrer')\n return true\n } catch (fallbackError) {\n console.error(`Fallback window.open also failed:`, fallbackError)\n return false\n }\n }\n }\n\n /**\n * Helper function to process a single file download\n * @returns boolean indicating if download is successful\n */\n const processFileDownload = async (\n fileResult: BatchFileResult['requestedFiles'][0],\n originalItem: (typeof nonPackageableFiles)[0] | undefined,\n ): Promise<boolean> => {\n try {\n let downloadUrl: string | undefined\n\n // Check if this is an external file handle\n if (\n fileResult.fileHandle &&\n implementsExternalFileHandleInterface(fileResult.fileHandle)\n ) {\n // For external file handles, use the externalURL\n downloadUrl = (fileResult.fileHandle as ExternalFileHandle)\n .externalURL\n setShowExternalFileWarning(true)\n } else if (fileResult.preSignedURL) {\n // For other file types, use the presigned URL\n downloadUrl = fileResult.preSignedURL\n }\n\n if (!downloadUrl) {\n return false\n }\n\n // Get filename from file result or create a default name\n const baseFileName = getFileName(fileResult, originalItem)\n\n // Handle filename collisions by adding a counter suffix\n const fileName = deduplicateFileName(baseFileName, usedFilenames)\n\n // Update progress with current file info\n const currentFileIndex = downloadedCount + 1\n fileDownloadStartRef.current = null\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n currentFile: fileName,\n fileIndex: currentFileIndex,\n bytesDownloaded: 0,\n totalBytes: 0,\n estimatedSecondsRemaining: null,\n }\n : null,\n )\n\n // Use traditional download method for unsupported browsers\n if (!useFileSystemAccess) {\n const success = await downloadFileTraditional(\n downloadUrl,\n fileName,\n )\n if (success) {\n downloadedCount = currentFileIndex\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n }\n return success\n }\n\n // Use File System Access API for supported browsers\n if (!directoryHandle) {\n return false\n }\n\n try {\n // Fetch the file\n abortControllerRef.current = new AbortController()\n const response = await fetch(downloadUrl, {\n signal: abortControllerRef.current.signal,\n })\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n // Get total file size from headers\n const contentLength = response.headers.get('content-length')\n const totalBytes = contentLength ? parseInt(contentLength, 10) : 0\n\n // Update progress with file size\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n bytesDownloaded: 0,\n totalBytes,\n }\n : null,\n )\n\n // Create file handle in selected directory\n const fileHandle = await directoryHandle.getFileHandle(fileName, {\n create: true,\n })\n const writableStream = await fileHandle.createWritable()\n\n // Stream the response body to the file with progress tracking\n if (response.body) {\n const reader = response.body.getReader()\n let bytesReceived = 0\n let streamAborted = false\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n bytesReceived += value.length\n await writableStream.write(value)\n\n // Check for cancellation on every chunk\n if (isCancelledRef.current) {\n streamAborted = true\n reader.cancel()\n await writableStream.abort()\n break\n }\n\n if (fileDownloadStartRef.current === null) {\n fileDownloadStartRef.current = Date.now()\n }\n const elapsedMs = Date.now() - fileDownloadStartRef.current\n let estimatedSecondsRemaining: number | null = null\n if (\n elapsedMs > ETA_DELAY_MS &&\n bytesReceived > 0 &&\n totalBytes > 0\n ) {\n const rate = bytesReceived / elapsedMs\n estimatedSecondsRemaining = Math.max(\n 0,\n (totalBytes - bytesReceived) / rate / 1000,\n )\n }\n\n // Update progress\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n bytesDownloaded: bytesReceived,\n ...(elapsedMs > ETA_DELAY_MS && {\n estimatedSecondsRemaining,\n }),\n }\n : null,\n )\n }\n } catch (streamError) {\n if (!streamAborted) {\n await writableStream.abort()\n streamAborted = true\n }\n throw streamError\n } finally {\n if (!streamAborted) {\n await writableStream.close()\n }\n }\n\n // If cancelled mid-stream, return false without falling through to success path\n if (isCancelledRef.current) {\n return false\n }\n } else {\n // Fallback if streaming not supported\n try {\n const blob = await response.blob()\n await writableStream.write(blob)\n } finally {\n await writableStream.close()\n }\n }\n\n downloadedCount = currentFileIndex\n\n // Track successfully downloaded item for batch removal\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n return true\n } catch (fetchError) {\n // If the fetch was aborted due to cancellation, do not fall back\n if (\n isCancelledRef.current ||\n (fetchError instanceof DOMException &&\n fetchError.name === 'AbortError')\n ) {\n return false\n }\n console.error(\n `Failed to fetch/write file ${fileResult.fileHandleId}:`,\n fetchError,\n )\n // Fallback to window.open if File System Access API fails\n try {\n downloadFileTraditional(downloadUrl, fileName)\n downloadedCount = currentFileIndex\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n return true\n } catch (fallbackError) {\n console.error(\n `Fallback downloadFileTraditional also failed for ${fileResult.fileHandleId}:`,\n fallbackError,\n )\n return false\n }\n }\n } catch (error) {\n console.error(\n `Failed to download file ${fileResult.fileHandleId}:`,\n error,\n )\n return false\n }\n }\n\n /**\n * Fallback function to fetch files individually when batch fails\n */\n const fetchIndividually = async (\n batch: FileHandleAssociation[],\n ): Promise<void> => {\n console.info(\n `Attempting to fetch ${batch.length} files individually after batch failure`,\n )\n\n for (const fileHandleAssociation of batch) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info('Individual file fetch cancelled by user')\n break\n }\n\n const singleBatch = [fileHandleAssociation]\n try {\n const batchRequest: BatchFileRequest = {\n requestedFiles: singleBatch,\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n }\n\n const batchResult = await getFiles(batchRequest, accessToken)\n\n if (batchResult.requestedFiles.length > 0) {\n const fileResult = batchResult.requestedFiles[0]\n const originalItem = nonPackageableFiles.find(\n item =>\n item.fileEntityId ===\n fileHandleAssociation.associateObjectId,\n )\n\n const success = await processFileDownload(\n fileResult,\n originalItem,\n )\n if (!success) {\n failedCount++\n }\n } else {\n failedCount++\n }\n } catch (error) {\n console.error(\n `Failed to fetch individual file ${fileHandleAssociation.fileHandleId}:`,\n error,\n )\n failedCount++\n }\n }\n }\n\n // Process each batch\n for (const batch of batches) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info('Download cancelled by user')\n break\n }\n\n // Try to fetch the entire batch (getFiles has built-in retry logic)\n let batchResult: BatchFileResult | null = null\n try {\n const batchRequest: BatchFileRequest = {\n requestedFiles: batch,\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n }\n batchResult = await getFiles(batchRequest, accessToken)\n } catch (error) {\n console.error('Failed to fetch batch:', error)\n }\n\n if (batchResult) {\n // Batch fetch succeeded, process all files\n for (let i = 0; i < batchResult.requestedFiles.length; i++) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info(\n 'Download cancelled by user during batch processing',\n )\n break\n }\n\n const fileResult = batchResult.requestedFiles[i]\n const originalItem = nonPackageableFiles.find(\n item => item.fileEntityId === batch[i].associateObjectId,\n )\n\n const success = await processFileDownload(\n fileResult,\n originalItem,\n )\n if (!success) {\n failedCount++\n }\n }\n } else {\n // Batch fetch failed after retries, try fetching files individually\n await fetchIndividually(batch)\n }\n }\n\n // Remove all successfully downloaded files from the download list in one batch\n if (successfullyDownloadedItems.length > 0) {\n await removeFilesFromDownloadList({\n batchToRemove: successfullyDownloadedItems.map(\n (item: (typeof nonPackageableFiles)[0]) => ({\n fileEntityId: item.fileEntityId,\n versionNumber: item.versionNumber,\n }),\n ),\n })\n // Refetch statistics after removing files\n await refetchDownloadListStatistics()\n // Scroll to top of page to show updated statistics\n window.scrollTo({ top: 0, behavior: 'smooth' })\n }\n\n // Show completion message\n if (isCancelledRef.current) {\n displayToast(\n `Download cancelled. ${downloadedCount} file${\n downloadedCount !== 1 ? 's were' : ' was'\n } downloaded before cancellation.`,\n 'info',\n )\n } else if (downloadedCount > 0) {\n displayToast(\n `Download started for ${downloadedCount} file${\n downloadedCount !== 1 ? 's' : ''\n }${\n failedCount > 0\n ? `. ${failedCount} file${failedCount !== 1 ? 's' : ''} failed.`\n : '.'\n }`,\n failedCount > 0 ? 'warning' : 'success',\n )\n } else {\n displayToast('No files could be downloaded', 'warning')\n }\n } catch (error) {\n console.error('Error downloading files:', error)\n const message =\n (error as SynapseClientError | undefined)?.reason ??\n (error instanceof Error ? error.message : undefined) ??\n 'An error occurred while downloading files'\n displayToast(message, 'danger')\n } finally {\n setDownloadProgress(null)\n setIsDownloading(false)\n isDownloadingRef.current = false\n }\n },\n [\n accessToken,\n isAuthenticated,\n queryClient,\n getEntityQueryOptions,\n removeFilesFromDownloadList,\n refetchDownloadListStatistics,\n ],\n )\n\n const handleClick = useCallback(async () => {\n if (isDownloading) {\n return\n }\n setIsDownloading(true)\n\n // Fetch all remaining pages imperatively, then download\n try {\n let stillHasNextPage = hasNextPage\n let latestData = data\n while (stillHasNextPage && fetchNextPage) {\n const result = await fetchNextPage()\n stillHasNextPage = result.hasNextPage\n if (result.data) {\n latestData = result.data\n }\n }\n\n // All pages loaded, now download\n if (status === 'success' && latestData) {\n void downloadAllFiles(latestData)\n } else {\n // Failed to load data\n setIsDownloading(false)\n isDownloadingRef.current = false\n displayToast(\n 'Failed to load download list. Please try again.',\n 'danger',\n )\n }\n } catch (error) {\n console.error('Error downloading files:', error)\n setIsDownloading(false)\n isDownloadingRef.current = false\n const message =\n (error as SynapseClientError | undefined)?.reason ??\n (error instanceof Error ? error.message : undefined) ??\n 'An error occurred while preparing files for download'\n displayToast(message, 'danger')\n }\n }, [\n isDownloading,\n hasNextPage,\n fetchNextPage,\n status,\n data,\n downloadAllFiles,\n ])\n\n const handleCancel = useCallback(() => {\n isCancelledRef.current = true\n abortControllerRef.current?.abort()\n setDownloadProgress(null)\n setIsDownloading(false)\n isDownloadingRef.current = false\n }, [])\n\n if (error) {\n return null\n }\n\n return (\n <>\n {/* Visually-hidden live region — announces file start and progress milestones to screen readers */}\n <span\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={styles.visuallyHidden}\n >\n {announceText}\n </span>\n {!!downloadProgress && showExternalFileWarning && (\n <Paper\n elevation={8}\n className={styles.externalWarningPanel}\n sx={{ zIndex: theme => theme.zIndex.modal }}\n role=\"region\"\n aria-label=\"External file warning\"\n >\n <CloudWarning className={styles.warningIcon} />\n <Box className={styles.warningTextContainer}>\n <Typography className={styles.warningTitleText}>\n Some of your files are hosted externally.\n </Typography>\n <Typography className={styles.progressFileNameText}>\n Downloads may be slower than expected\n </Typography>\n </Box>\n <IconButton\n onClick={() => setShowExternalFileWarning(false)}\n size=\"small\"\n className={styles.warningCloseButton}\n aria-label=\"Dismiss external file warning\"\n >\n <Close />\n </IconButton>\n </Paper>\n )}\n {!!downloadProgress && (\n <Paper\n elevation={8}\n className={styles.progressPanel}\n sx={{ zIndex: theme => theme.zIndex.modal }}\n role=\"region\"\n aria-label=\"Download progress\"\n >\n <Typography className={styles.progressTitleText}>\n Downloading file {downloadProgress.fileIndex} of{' '}\n {downloadProgress.totalFiles}\n </Typography>\n <Typography className={styles.progressFileNameText}>\n {downloadProgress.currentFile}\n </Typography>\n <Box className={styles.progressFooter}>\n <Box className={styles.progressBarContainer}>\n <Box className={styles.progressStats}>\n <Typography className={styles.progressStatsText}>\n {downloadProgress.totalBytes > 0\n ? `${Math.round(\n (downloadProgress.bytesDownloaded /\n downloadProgress.totalBytes) *\n 100,\n )}% ( ${calculateFriendlyFileSize(\n downloadProgress.bytesDownloaded,\n )} / ${calculateFriendlyFileSize(\n downloadProgress.totalBytes,\n )} )`\n : ''}\n </Typography>\n <Typography className={styles.progressStatsText}>\n {downloadProgress.estimatedSecondsRemaining !== null\n ? `About ${dayjs\n .duration({\n seconds: downloadProgress.estimatedSecondsRemaining,\n })\n .humanize()} remaining`\n : ''}\n </Typography>\n </Box>\n <LinearProgress\n variant={\n downloadProgress.totalBytes > 0\n ? 'determinate'\n : 'indeterminate'\n }\n value={\n downloadProgress.totalBytes > 0\n ? (downloadProgress.bytesDownloaded /\n downloadProgress.totalBytes) *\n 100\n : undefined\n }\n aria-label=\"Download progress\"\n className={styles.progressBar}\n />\n </Box>\n <Box className={styles.cancelButtonContainer}>\n <Button\n variant=\"outlined\"\n onClick={handleCancel}\n className={styles.cancelButton}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n </Paper>\n )}\n <Button\n variant={variant}\n onClick={() => {\n void handleClick()\n }}\n loading={isDownloading}\n startIcon={<Download />}\n >\n {buttonText}\n </Button>\n </>\n )\n}\n\nexport default DownloadIneligibleForPackagingFilesFromListButton\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DownloadIneligibleForPackagingFilesFromListButton.js","names":[],"sources":["../../../src/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.tsx"],"sourcesContent":["import { useSynapseContext } from '@/utils'\nimport { implementsExternalFileHandleInterface } from '@/utils/types/IsType'\nimport { calculateFriendlyFileSize } from '@/utils/functions/calculateFriendlyFileSize'\nimport {\n Box,\n Button,\n IconButton,\n LinearProgress,\n Paper,\n Typography,\n} from '@mui/material'\nimport Close from '@mui/icons-material/Close'\nimport Download from '@mui/icons-material/Download'\nimport CloudWarning from '@/assets/icons/CloudWarning'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport dayjs from 'dayjs'\nimport duration from 'dayjs/plugin/duration'\nimport relativeTime from 'dayjs/plugin/relativeTime'\nimport {\n BatchFileRequest,\n BatchFileResult,\n ExternalFileHandle,\n FileEntity,\n FileHandleAssociation,\n FileHandleAssociateType,\n} from '@sage-bionetworks/synapse-types'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport {\n useGetAvailableFilesToDownloadInfinite,\n useGetDownloadListStatistics,\n useRemoveFilesFromDownloadList,\n} from '@/synapse-queries/download/useDownloadList'\nimport { getFiles } from '@/synapse-client/SynapseClient'\nimport { useGetEntityQueryOptions } from '@/synapse-queries/entity/useEntity'\n\nimport { deduplicateFileName, getFileName } from './fileNameUtils'\nimport styles from './DownloadIneligibleForPackagingFilesFromListButton.module.scss'\n\n// showDirectoryPicker is not yet available on the Window interface.\ninterface FileSystemAccessWindow extends Window {\n showDirectoryPicker(options?: {\n mode?: 'read' | 'readwrite'\n startIn?:\n | 'desktop'\n | 'documents'\n | 'downloads'\n | 'music'\n | 'pictures'\n | 'videos'\n }): Promise<FileSystemDirectoryHandle>\n}\n\n/**\n * Type guard to check if File System Access API is available\n */\nfunction supportsFileSystemAccess(\n window: Window,\n): window is FileSystemAccessWindow {\n return 'showDirectoryPicker' in window\n}\n\nconst FILE_BATCH_SIZE = 50 // Maximum batch size for fetching presigned URLs\nconst ETA_DELAY_MS = 3000 // Wait this long before showing ETA\n\ndayjs.extend(duration)\ndayjs.extend(relativeTime)\n\nexport type DownloadIneligibleForPackagingFilesFromListButtonProps = {\n buttonText?: string\n variant?: 'text' | 'outlined' | 'contained'\n}\n\n/**\n * Button component that downloads files from the user's download list that are ineligible for packaging.\n * Uses the infinite query hook to fetch all pages of download list items,\n * batches requests to get presigned URLs, and triggers browser downloads.\n */\nexport function DownloadIneligibleForPackagingFilesFromListButton(\n props: DownloadIneligibleForPackagingFilesFromListButtonProps,\n) {\n const { buttonText = 'Start Multi-file Download', variant = 'contained' } =\n props\n const { accessToken, isAuthenticated } = useSynapseContext()\n const queryClient = useQueryClient()\n const getEntityQueryOptions = useGetEntityQueryOptions<FileEntity>()\n const [isDownloading, setIsDownloading] = useState(false)\n const isDownloadingRef = useRef(false)\n const [showExternalFileWarning, setShowExternalFileWarning] = useState(false)\n const isCancelledRef = useRef(false)\n const abortControllerRef = useRef<AbortController | null>(null)\n const [downloadProgress, setDownloadProgress] = useState<{\n currentFile: string\n fileIndex: number\n totalFiles: number\n bytesDownloaded: number\n totalBytes: number\n estimatedSecondsRemaining: number | null\n } | null>(null)\n const fileDownloadStartRef = useRef<number | null>(null)\n\n // Accessibility: tracks the last announced file index and progress bucket so\n // the aria-live region only updates on meaningful changes (not every chunk).\n const [announceText, setAnnounceText] = useState('')\n const lastAnnouncedFileIndexRef = useRef<number>(-1)\n const lastAnnouncedPercentBucketRef = useRef<number>(-1)\n\n useEffect(() => {\n if (!downloadProgress) {\n lastAnnouncedFileIndexRef.current = -1\n lastAnnouncedPercentBucketRef.current = -1\n setAnnounceText('')\n return\n }\n\n const { fileIndex, totalFiles, currentFile, bytesDownloaded, totalBytes } =\n downloadProgress\n\n // Announce when a new file starts downloading\n if (fileIndex !== lastAnnouncedFileIndexRef.current) {\n lastAnnouncedFileIndexRef.current = fileIndex\n lastAnnouncedPercentBucketRef.current = -1\n setAnnounceText(\n `Downloading file ${fileIndex} of ${totalFiles}: ${currentFile}`,\n )\n return\n }\n\n // Announce at each 25% milestone (25, 50, 75, 100) once per file\n if (totalBytes > 0) {\n const percent = Math.floor((bytesDownloaded / totalBytes) * 100)\n const bucket = Math.floor(percent / 25) * 25\n if (bucket > 0 && bucket !== lastAnnouncedPercentBucketRef.current) {\n lastAnnouncedPercentBucketRef.current = bucket\n setAnnounceText(`${bucket}% of ${currentFile} downloaded`)\n }\n }\n }, [downloadProgress])\n\n const { data, status, hasNextPage, fetchNextPage, error } =\n useGetAvailableFilesToDownloadInfinite()\n\n const { mutateAsync: removeFilesFromDownloadList } =\n useRemoveFilesFromDownloadList()\n\n const { refetch: refetchDownloadListStatistics } =\n useGetDownloadListStatistics()\n\n /**\n * Downloads all files from the download list.\n */\n const downloadAllFiles = useCallback(\n async (downloadListItems: typeof data) => {\n // Prevent re-entry if already downloading\n if (isDownloadingRef.current) {\n return\n }\n\n if (!downloadListItems || !isAuthenticated) {\n setIsDownloading(false)\n return\n }\n\n try {\n isDownloadingRef.current = true\n isCancelledRef.current = false\n setShowExternalFileWarning(false)\n const allItems = downloadListItems.pages.flatMap(page => page.page)\n\n // Filter to only include files that are NOT eligible for packaging\n const nonPackageableFiles = allItems.filter(\n item => !item.isEligibleForPackaging,\n )\n\n if (nonPackageableFiles.length === 0) {\n displayToast('No non-packageable files available to download', 'info')\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n\n // Determine if File System Access API is available\n const useFileSystemAccess = supportsFileSystemAccess(window)\n\n // Prompt user to select a directory for downloads BEFORE async operations\n // (must be called during user gesture to avoid SecurityError)\n let directoryHandle: FileSystemDirectoryHandle | undefined\n\n if (useFileSystemAccess) {\n // TypeScript narrowing: we know window has showDirectoryPicker at this point\n const fileSystemWindow = window as unknown as FileSystemAccessWindow\n\n try {\n directoryHandle = await fileSystemWindow.showDirectoryPicker({\n mode: 'readwrite',\n })\n } catch (error) {\n // User cancelled directory picker\n console.error('Failed to select directory:', error)\n displayToast(\n 'Directory selection is required to directly download files from the website.',\n 'warning',\n )\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n } else {\n // Show info message for browsers without File System Access API\n displayToast(\n 'Your browser will download files individually to your default downloads folder. Each file may trigger a separate download prompt.',\n 'info',\n )\n }\n\n // Initialize progress tracking\n fileDownloadStartRef.current = null\n setDownloadProgress({\n currentFile: '',\n fileIndex: 0,\n totalFiles: nonPackageableFiles.length,\n bytesDownloaded: 0,\n totalBytes: 0,\n estimatedSecondsRemaining: null,\n })\n\n // Fetch entities to get dataFileHandleId for each file\n // Use React Query's ensureQueryData to leverage cache and built-in retry logic (handles 429s with exponential backoff)\n // Map key combines entity ID and version to handle multiple versions of the same entity\n const entityIdToFileHandleId = new Map<string, string>()\n\n // Fetch all entities in parallel - React Query handles concurrency and retries\n const entityPromises = nonPackageableFiles.map(async item => {\n if (isCancelledRef.current) {\n return\n }\n\n try {\n const entity = await queryClient.ensureQueryData(\n getEntityQueryOptions(\n item.fileEntityId,\n item.versionNumber?.toString(),\n ),\n )\n // Use composite key: entityId-version (or entityId if no version)\n const mapKey = `${entity.id!}-${item.versionNumber ?? 'latest'}`\n entityIdToFileHandleId.set(mapKey, entity.dataFileHandleId)\n } catch (error) {\n console.error(`Failed to fetch entity ${item.fileEntityId}:`, error)\n // Continue even if one entity fails\n }\n })\n\n await Promise.all(entityPromises)\n\n // Convert download list items to file handle associations using the fetched dataFileHandleIds\n const fileHandleAssociations: FileHandleAssociation[] =\n nonPackageableFiles\n .filter(item => {\n const mapKey = `${item.fileEntityId}-${\n item.versionNumber ?? 'latest'\n }`\n return entityIdToFileHandleId.has(mapKey)\n })\n .map(item => {\n const mapKey = `${item.fileEntityId}-${\n item.versionNumber ?? 'latest'\n }`\n return {\n fileHandleId: entityIdToFileHandleId.get(mapKey)!,\n associateObjectId: item.fileEntityId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n }\n })\n\n if (fileHandleAssociations.length === 0) {\n displayToast(\n 'Failed to fetch file information for download',\n 'danger',\n )\n setIsDownloading(false)\n isDownloadingRef.current = false\n return\n }\n\n // Split into batches to avoid overwhelming the API\n const batches: FileHandleAssociation[][] = []\n for (\n let i = 0;\n i < fileHandleAssociations.length;\n i += FILE_BATCH_SIZE\n ) {\n batches.push(fileHandleAssociations.slice(i, i + FILE_BATCH_SIZE))\n }\n\n let downloadedCount = 0\n let failedCount = 0\n const successfullyDownloadedItems: typeof nonPackageableFiles = []\n\n // Track used filenames to prevent collisions\n const usedFilenames = new Map<string, number>()\n\n /**\n * Helper function to download file using traditional anchor tag method\n * @returns boolean indicating if the download was successful\n */\n const downloadFileTraditional = async (\n downloadUrl: string,\n fileName: string,\n ): Promise<boolean> => {\n try {\n const response = await fetch(downloadUrl)\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = blobUrl\n anchor.download = fileName\n anchor.style.display = 'none'\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n\n // Clean up blob URL after a delay to ensure download initiation\n setTimeout(() => URL.revokeObjectURL(blobUrl), 1000)\n\n return true\n } catch (error) {\n console.error(`Failed to download file ${fileName}:`, error)\n // Fallback to window.open method\n try {\n window.open(downloadUrl, '_blank', 'noopener,noreferrer')\n return true\n } catch (fallbackError) {\n console.error(`Fallback window.open also failed:`, fallbackError)\n return false\n }\n }\n }\n\n /**\n * Helper function to process a single file download\n * @returns boolean indicating if download is successful\n */\n const processFileDownload = async (\n fileResult: BatchFileResult['requestedFiles'][0],\n originalItem: (typeof nonPackageableFiles)[0] | undefined,\n ): Promise<boolean> => {\n try {\n let downloadUrl: string | undefined\n\n // Check if this is an external file handle\n if (\n fileResult.fileHandle &&\n implementsExternalFileHandleInterface(fileResult.fileHandle)\n ) {\n // For external file handles, use the externalURL\n downloadUrl = (fileResult.fileHandle as ExternalFileHandle)\n .externalURL\n setShowExternalFileWarning(true)\n } else if (fileResult.preSignedURL) {\n // For other file types, use the presigned URL\n downloadUrl = fileResult.preSignedURL\n }\n\n if (!downloadUrl) {\n return false\n }\n\n // Get filename from file result or create a default name\n const baseFileName = getFileName(fileResult, originalItem)\n\n // Handle filename collisions by adding a counter suffix\n const fileName = deduplicateFileName(baseFileName, usedFilenames)\n\n // Update progress with current file info\n const currentFileIndex = downloadedCount + 1\n fileDownloadStartRef.current = null\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n currentFile: fileName,\n fileIndex: currentFileIndex,\n bytesDownloaded: 0,\n totalBytes: 0,\n estimatedSecondsRemaining: null,\n }\n : null,\n )\n\n // Use traditional download method for unsupported browsers\n if (!useFileSystemAccess) {\n const success = await downloadFileTraditional(\n downloadUrl,\n fileName,\n )\n if (success) {\n downloadedCount = currentFileIndex\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n }\n return success\n }\n\n // Use File System Access API for supported browsers\n if (!directoryHandle) {\n return false\n }\n\n try {\n // Fetch the file\n abortControllerRef.current = new AbortController()\n const response = await fetch(downloadUrl, {\n signal: abortControllerRef.current.signal,\n })\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n // Get total file size from headers\n const contentLength = response.headers.get('content-length')\n const totalBytes = contentLength ? parseInt(contentLength, 10) : 0\n\n // Update progress with file size\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n bytesDownloaded: 0,\n totalBytes,\n }\n : null,\n )\n\n // Create file handle in selected directory\n const fileHandle = await directoryHandle.getFileHandle(fileName, {\n create: true,\n })\n const writableStream = await fileHandle.createWritable()\n\n // Stream the response body to the file with progress tracking\n if (response.body) {\n const reader = response.body.getReader()\n let bytesReceived = 0\n let streamAborted = false\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n bytesReceived += value.length\n await writableStream.write(value)\n\n // Check for cancellation on every chunk\n if (isCancelledRef.current) {\n streamAborted = true\n reader.cancel()\n await writableStream.abort()\n break\n }\n\n if (fileDownloadStartRef.current === null) {\n fileDownloadStartRef.current = Date.now()\n }\n const elapsedMs = Date.now() - fileDownloadStartRef.current\n let estimatedSecondsRemaining: number | null = null\n if (\n elapsedMs > ETA_DELAY_MS &&\n bytesReceived > 0 &&\n totalBytes > 0\n ) {\n const rate = bytesReceived / elapsedMs\n estimatedSecondsRemaining = Math.max(\n 0,\n (totalBytes - bytesReceived) / rate / 1000,\n )\n }\n\n // Update progress\n setDownloadProgress(prev =>\n prev\n ? {\n ...prev,\n bytesDownloaded: bytesReceived,\n ...(elapsedMs > ETA_DELAY_MS && {\n estimatedSecondsRemaining,\n }),\n }\n : null,\n )\n }\n } catch (streamError) {\n if (!streamAborted) {\n await writableStream.abort()\n streamAborted = true\n }\n throw streamError\n } finally {\n if (!streamAborted) {\n await writableStream.close()\n }\n }\n\n // If cancelled mid-stream, return false without falling through to success path\n if (isCancelledRef.current) {\n return false\n }\n } else {\n // Fallback if streaming not supported\n try {\n const blob = await response.blob()\n await writableStream.write(blob)\n } finally {\n await writableStream.close()\n }\n }\n\n downloadedCount = currentFileIndex\n\n // Track successfully downloaded item for batch removal\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n return true\n } catch (fetchError) {\n // If the fetch was aborted due to cancellation, do not fall back\n if (\n isCancelledRef.current ||\n (fetchError instanceof DOMException &&\n fetchError.name === 'AbortError')\n ) {\n return false\n }\n console.error(\n `Failed to fetch/write file ${fileResult.fileHandleId}:`,\n fetchError,\n )\n // Fallback to window.open if File System Access API fails\n try {\n downloadFileTraditional(downloadUrl, fileName)\n downloadedCount = currentFileIndex\n if (originalItem) {\n successfullyDownloadedItems.push(originalItem)\n }\n return true\n } catch (fallbackError) {\n console.error(\n `Fallback downloadFileTraditional also failed for ${fileResult.fileHandleId}:`,\n fallbackError,\n )\n return false\n }\n }\n } catch (error) {\n console.error(\n `Failed to download file ${fileResult.fileHandleId}:`,\n error,\n )\n return false\n }\n }\n\n /**\n * Fallback function to fetch files individually when batch fails\n */\n const fetchIndividually = async (\n batch: FileHandleAssociation[],\n ): Promise<void> => {\n console.info(\n `Attempting to fetch ${batch.length} files individually after batch failure`,\n )\n\n for (const fileHandleAssociation of batch) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info('Individual file fetch cancelled by user')\n break\n }\n\n const singleBatch = [fileHandleAssociation]\n try {\n const batchRequest: BatchFileRequest = {\n requestedFiles: singleBatch,\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n }\n\n const batchResult = await getFiles(batchRequest, accessToken)\n\n if (batchResult.requestedFiles.length > 0) {\n const fileResult = batchResult.requestedFiles[0]\n const originalItem = nonPackageableFiles.find(\n item =>\n item.fileEntityId ===\n fileHandleAssociation.associateObjectId,\n )\n\n const success = await processFileDownload(\n fileResult,\n originalItem,\n )\n if (!success) {\n failedCount++\n }\n } else {\n failedCount++\n }\n } catch (error) {\n console.error(\n `Failed to fetch individual file ${fileHandleAssociation.fileHandleId}:`,\n error,\n )\n failedCount++\n }\n }\n }\n\n // Process each batch\n for (const batch of batches) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info('Download cancelled by user')\n break\n }\n\n // Try to fetch the entire batch (getFiles has built-in retry logic)\n let batchResult: BatchFileResult | null = null\n try {\n const batchRequest: BatchFileRequest = {\n requestedFiles: batch,\n includeFileHandles: true,\n includePreSignedURLs: true,\n includePreviewPreSignedURLs: false,\n }\n batchResult = await getFiles(batchRequest, accessToken)\n } catch (error) {\n console.error('Failed to fetch batch:', error)\n }\n\n if (batchResult) {\n // Batch fetch succeeded, process all files\n for (let i = 0; i < batchResult.requestedFiles.length; i++) {\n // Check if user cancelled the operation\n if (isCancelledRef.current) {\n console.info(\n 'Download cancelled by user during batch processing',\n )\n break\n }\n\n const fileResult = batchResult.requestedFiles[i]\n const originalItem = nonPackageableFiles.find(\n item => item.fileEntityId === batch[i].associateObjectId,\n )\n\n const success = await processFileDownload(\n fileResult,\n originalItem,\n )\n if (!success) {\n failedCount++\n }\n }\n } else {\n // Batch fetch failed after retries, try fetching files individually\n await fetchIndividually(batch)\n }\n }\n\n // Remove all successfully downloaded files from the download list in one batch\n if (successfullyDownloadedItems.length > 0) {\n await removeFilesFromDownloadList({\n batchToRemove: successfullyDownloadedItems.map(\n (item: (typeof nonPackageableFiles)[0]) => ({\n fileEntityId: item.fileEntityId,\n versionNumber: item.versionNumber,\n }),\n ),\n })\n // Refetch statistics after removing files\n await refetchDownloadListStatistics()\n // Scroll to top of page to show updated statistics\n window.scrollTo({ top: 0, behavior: 'smooth' })\n }\n\n // Show completion message\n if (isCancelledRef.current) {\n displayToast(\n `Download cancelled. ${downloadedCount} file${\n downloadedCount !== 1 ? 's were' : ' was'\n } downloaded before cancellation.`,\n 'info',\n )\n } else if (downloadedCount > 0) {\n displayToast(\n `Download started for ${downloadedCount} file${\n downloadedCount !== 1 ? 's' : ''\n }${\n failedCount > 0\n ? `. ${failedCount} file${failedCount !== 1 ? 's' : ''} failed.`\n : '.'\n }`,\n failedCount > 0 ? 'warning' : 'success',\n )\n } else {\n displayToast('No files could be downloaded', 'warning')\n }\n } catch (error) {\n console.error('Error downloading files:', error)\n const message =\n (error as SynapseClientError | undefined)?.reason ??\n (error instanceof Error ? error.message : undefined) ??\n 'An error occurred while downloading files'\n displayToast(message, 'danger')\n } finally {\n setDownloadProgress(null)\n setIsDownloading(false)\n isDownloadingRef.current = false\n }\n },\n [\n accessToken,\n isAuthenticated,\n queryClient,\n getEntityQueryOptions,\n removeFilesFromDownloadList,\n refetchDownloadListStatistics,\n ],\n )\n\n const handleClick = useCallback(async () => {\n if (isDownloading) {\n return\n }\n setIsDownloading(true)\n\n // Fetch all remaining pages imperatively, then download\n try {\n let stillHasNextPage = hasNextPage\n let latestData = data\n while (stillHasNextPage && fetchNextPage) {\n const result = await fetchNextPage()\n stillHasNextPage = result.hasNextPage\n if (result.data) {\n latestData = result.data\n }\n }\n\n // All pages loaded, now download\n if (status === 'success' && latestData) {\n void downloadAllFiles(latestData)\n } else {\n // Failed to load data\n setIsDownloading(false)\n isDownloadingRef.current = false\n displayToast(\n 'Failed to load download list. Please try again.',\n 'danger',\n )\n }\n } catch (error) {\n console.error('Error downloading files:', error)\n setIsDownloading(false)\n isDownloadingRef.current = false\n const message =\n (error as SynapseClientError | undefined)?.reason ??\n (error instanceof Error ? error.message : undefined) ??\n 'An error occurred while preparing files for download'\n displayToast(message, 'danger')\n }\n }, [\n isDownloading,\n hasNextPage,\n fetchNextPage,\n status,\n data,\n downloadAllFiles,\n ])\n\n const handleCancel = useCallback(() => {\n isCancelledRef.current = true\n abortControllerRef.current?.abort()\n setDownloadProgress(null)\n setIsDownloading(false)\n isDownloadingRef.current = false\n }, [])\n\n if (error) {\n return null\n }\n\n return (\n <>\n {/* Visually-hidden live region — announces file start and progress milestones to screen readers */}\n <span\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={styles.visuallyHidden}\n >\n {announceText}\n </span>\n {!!downloadProgress && showExternalFileWarning && (\n <Paper\n elevation={8}\n className={styles.externalWarningPanel}\n sx={{ zIndex: theme => theme.zIndex.modal }}\n role=\"region\"\n aria-label=\"External file warning\"\n >\n <CloudWarning className={styles.warningIcon} />\n <Box className={styles.warningTextContainer}>\n <Typography className={styles.warningTitleText}>\n Some of your files are hosted externally.\n </Typography>\n <Typography className={styles.progressFileNameText}>\n Downloads may be slower than expected\n </Typography>\n </Box>\n <IconButton\n onClick={() => setShowExternalFileWarning(false)}\n size=\"small\"\n className={styles.warningCloseButton}\n aria-label=\"Dismiss external file warning\"\n >\n <Close />\n </IconButton>\n </Paper>\n )}\n {!!downloadProgress && (\n <Paper\n elevation={8}\n className={styles.progressPanel}\n sx={{ zIndex: theme => theme.zIndex.modal }}\n role=\"region\"\n aria-label=\"Download progress\"\n >\n <Typography className={styles.progressTitleText}>\n Downloading file {downloadProgress.fileIndex} of{' '}\n {downloadProgress.totalFiles}\n </Typography>\n <Typography className={styles.progressFileNameText}>\n {downloadProgress.currentFile}\n </Typography>\n <Box className={styles.progressFooter}>\n <Box className={styles.progressBarContainer}>\n <Box className={styles.progressStats}>\n <Typography className={styles.progressStatsText}>\n {downloadProgress.totalBytes > 0\n ? `${Math.round(\n (downloadProgress.bytesDownloaded /\n downloadProgress.totalBytes) *\n 100,\n )}% ( ${calculateFriendlyFileSize(\n downloadProgress.bytesDownloaded,\n )} / ${calculateFriendlyFileSize(\n downloadProgress.totalBytes,\n )} )`\n : ''}\n </Typography>\n <Typography className={styles.progressStatsText}>\n {downloadProgress.estimatedSecondsRemaining !== null\n ? `About ${dayjs\n .duration({\n seconds: downloadProgress.estimatedSecondsRemaining,\n })\n .humanize()} remaining`\n : ''}\n </Typography>\n </Box>\n <LinearProgress\n variant={\n downloadProgress.totalBytes > 0\n ? 'determinate'\n : 'indeterminate'\n }\n value={\n downloadProgress.totalBytes > 0\n ? (downloadProgress.bytesDownloaded /\n downloadProgress.totalBytes) *\n 100\n : undefined\n }\n aria-label=\"Download progress\"\n className={styles.progressBar}\n />\n </Box>\n <Box className={styles.cancelButtonContainer}>\n <Button\n variant=\"outlined\"\n onClick={handleCancel}\n className={styles.cancelButton}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n </Paper>\n )}\n <Button\n variant={variant}\n onClick={() => {\n void handleClick()\n }}\n loading={isDownloading}\n startIcon={<Download />}\n >\n {buttonText}\n </Button>\n </>\n )\n}\n\nexport default DownloadIneligibleForPackagingFilesFromListButton\n"],"mappings":"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"}
|
package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadIneligibleForPackagingFilesFromListButton.module.js","names":[],"sources":["../../../src/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss"],"sourcesContent":["// Hides an element visually while keeping it accessible to screen readers.\n.visuallyHidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n$progress-panel-bottom: 20px;\n$progress-panel-height: 150px;\n\n.externalWarningPanel {\n position: fixed;\n bottom: calc(
|
|
1
|
+
{"version":3,"file":"DownloadIneligibleForPackagingFilesFromListButton.module.js","names":[],"sources":["../../../src/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss"],"sourcesContent":["// Hides an element visually while keeping it accessible to screen readers.\n.visuallyHidden {\n border: 0;\n clip: rect(0 0 0 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n$progress-panel-bottom: 20px;\n$progress-panel-height: 150px;\n\n.externalWarningPanel {\n position: fixed;\n bottom: calc(#{$progress-panel-height} + 10px); // positioned above the progress panel\n right: 20px;\n width: 500px;\n padding: 16px;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n box-sizing: border-box;\n\n // Use &#{&} to increase specificity and override MUI Paper's default styles\n &#{&} {\n border-radius: 0;\n background-color: #223549;\n }\n}\n\n.warningIcon {\n flex-shrink: 0;\n\n // Use &#{&} to increase specificity and override MUI SvgIcon's default styles\n &#{&} {\n font-size: 40px;\n }\n}\n\n.warningTextContainer {\n flex: 1;\n}\n\n.warningTitleText {\n // Use &#{&} to increase specificity and override MUI Typography's default styles\n &#{&} {\n color: white;\n font-size: 14px;\n line-height: 1.4;\n font-weight: 700;\n }\n}\n\n.warningCloseButton {\n // Use &#{&} to increase specificity and override MUI IconButton's default styles\n &#{&} {\n color: white;\n align-self: flex-start;\n flex-shrink: 0;\n\n &:hover {\n color: #889baf;\n }\n }\n}\n\n.progressPanel {\n position: fixed;\n bottom: $progress-panel-bottom;\n right: 20px;\n width: 500px;\n height: $progress-panel-height;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n box-sizing: border-box;\n\n // Use &#{&} to increase specificity and override MUI Paper's default styles\n &#{&} {\n border-radius: 0;\n background-color: #223549;\n }\n}\n\n.progressTitleText {\n // Use &#{&} to increase specificity and override MUI Typography's default styles\n &#{&} {\n color: white;\n font-size: 14px;\n line-height: 1.4;\n }\n}\n\n.progressFileNameText {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n\n // Use &#{&} to increase specificity and override MUI Typography's default styles\n &#{&} {\n color: #889baf;\n font-size: 13px;\n line-height: 1.4;\n }\n}\n\n.progressFooter {\n display: flex;\n flex: 1;\n gap: 8px;\n align-items: center;\n margin-top: auto;\n}\n\n.progressBarContainer {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.progressStats {\n display: flex;\n justify-content: space-between;\n}\n\n.progressStatsText {\n // Use &#{&} to increase specificity and override MUI Typography's default styles\n &#{&} {\n color: #889baf;\n font-size: 12px;\n }\n}\n\n.progressBar {\n :global(.MuiLinearProgress-bar) {\n background-color: #469285;\n }\n}\n\n.cancelButtonContainer {\n width: 80px;\n display: flex;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.cancelButton {\n // Use &#{&} to increase specificity and override MUI Button's default styles\n &#{&} {\n color: white;\n border-color: white;\n min-width: 0;\n padding: 10px 16px;\n font-size: 0.75rem;\n line-height: 1;\n\n &:hover {\n border-color: #889baf;\n color: #889baf;\n }\n }\n}\n"],"mappings":""}
|
package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.module.scss
CHANGED
|
@@ -15,9 +15,7 @@ $progress-panel-height: 150px;
|
|
|
15
15
|
|
|
16
16
|
.externalWarningPanel {
|
|
17
17
|
position: fixed;
|
|
18
|
-
bottom: calc(
|
|
19
|
-
#{$progress-panel-height} + 10px
|
|
20
|
-
); // positioned above the progress panel
|
|
18
|
+
bottom: calc(#{$progress-panel-height} + 10px); // positioned above the progress panel
|
|
21
19
|
right: 20px;
|
|
22
20
|
width: 500px;
|
|
23
21
|
padding: 16px;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityBadgeIcons.d.ts","sourceRoot":"","sources":["../../../src/components/EntityBadgeIcons/EntityBadgeIcons.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EntityBadgeIcons.d.ts","sourceRoot":"","sources":["../../../src/components/EntityBadgeIcons/EntityBadgeIcons.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAOlD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EACL,MAAM,GACN,QAAQ,GACR,cAAc,GACd,SAAS,GACT,SAAS,GACT,QAAQ,GACR,OAAO,GACP,cAAc,CAAA;IAClB,cAAc,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAAA;IACnD,qFAAqF;IACrF,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,oFAAoF;IACpF,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,+HAA+H;IAC/H,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,0FAA0F;IAC1F,uBAAuB,CAAC,EAAE,OAAO,CAAA;IAEjC,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACxC,wFAAwF;IACxF,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,OAAO,qBAAqB,KAC3B,KAAK,CAAC,SAqSR,CAAA;AAED,eAAe,gBAAgB,CAAA"}
|
|
@@ -14,11 +14,17 @@ import { Tooltip as d } from "@mui/material";
|
|
|
14
14
|
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
15
15
|
import { ALL_ENTITY_BUNDLE_FIELDS as h } from "@sage-bionetworks/synapse-types";
|
|
16
16
|
import { EntityType as g } from "@sage-bionetworks/synapse-client";
|
|
17
|
-
import
|
|
18
|
-
import
|
|
17
|
+
import _ from "@mui/icons-material/LinkOffTwoTone";
|
|
18
|
+
import v from "@mui/icons-material/LockTwoTone";
|
|
19
|
+
import y from "@mui/icons-material/PublicTwoTone";
|
|
20
|
+
import { useInView as b } from "react-intersection-observer";
|
|
21
|
+
import x from "@mui/icons-material/CheckTwoTone";
|
|
22
|
+
import S from "@mui/icons-material/ChatBubbleTwoTone";
|
|
23
|
+
import C from "@mui/icons-material/DescriptionTwoTone";
|
|
24
|
+
import w from "@mui/icons-material/LocalOfferTwoTone";
|
|
19
25
|
//#region src/components/EntityBadgeIcons/EntityBadgeIcons.tsx
|
|
20
26
|
var T = (T) => {
|
|
21
|
-
let { entityId: E, versionNumber: D, flexWrap: O = "nowrap", justifyContent: k = "flex-start", showIsPublicPrivate: A = !0, showHasLocalSharingSettings: j = !0, showHasAnnotations: M = !0, showHasWiki: N = !0, showHasDiscussionThread: P = !0, showUnlink: F = !0, onUnlink: I = () => {}, onUnlinkError: L = () => {}, canOpenModal: R } = T, { data: re } = e(), z = re || {}, { ref: B, inView: V } =
|
|
27
|
+
let { entityId: E, versionNumber: D, flexWrap: O = "nowrap", justifyContent: k = "flex-start", showIsPublicPrivate: A = !0, showHasLocalSharingSettings: j = !0, showHasAnnotations: M = !0, showHasWiki: N = !0, showHasDiscussionThread: P = !0, showUnlink: F = !0, onUnlink: I = () => {}, onUnlinkError: L = () => {}, canOpenModal: R } = T, { data: re } = e(), z = re || {}, { ref: B, inView: V } = b(), { data: H } = r(E, D, !0, { enabled: V }), U = H?.annotations, { data: W } = t(E, D, h, { enabled: V }), [G, K] = l(!1), [q, J] = l(!1), [Y, X] = l(""), { data: Z } = ee(E, {
|
|
22
28
|
enabled: V,
|
|
23
29
|
staleTime: 60 * 1e3
|
|
24
30
|
}), { data: Q } = te(E, {
|
|
@@ -66,7 +72,7 @@ var T = (T) => {
|
|
|
66
72
|
title: "Public",
|
|
67
73
|
enterNextDelay: 100,
|
|
68
74
|
placement: "right",
|
|
69
|
-
children: /* @__PURE__ */ p(
|
|
75
|
+
children: /* @__PURE__ */ p(y, {
|
|
70
76
|
"aria-hidden": !1,
|
|
71
77
|
role: "img",
|
|
72
78
|
className: "EntityBadge__Badge",
|
|
@@ -77,7 +83,7 @@ var T = (T) => {
|
|
|
77
83
|
title: "Private",
|
|
78
84
|
enterNextDelay: 100,
|
|
79
85
|
placement: "right",
|
|
80
|
-
children: /* @__PURE__ */ p(
|
|
86
|
+
children: /* @__PURE__ */ p(v, {
|
|
81
87
|
"aria-hidden": !1,
|
|
82
88
|
role: "img",
|
|
83
89
|
className: "EntityBadge__Badge",
|
|
@@ -88,7 +94,7 @@ var T = (T) => {
|
|
|
88
94
|
title: "Sharing Settings have been set",
|
|
89
95
|
enterNextDelay: 100,
|
|
90
96
|
placement: "right",
|
|
91
|
-
children: /* @__PURE__ */ p(
|
|
97
|
+
children: /* @__PURE__ */ p(x, {
|
|
92
98
|
"aria-hidden": !1,
|
|
93
99
|
role: "img",
|
|
94
100
|
className: "EntityBadge__Badge",
|
|
@@ -105,7 +111,7 @@ var T = (T) => {
|
|
|
105
111
|
onClick: () => K(!0),
|
|
106
112
|
closeOnClick: !0
|
|
107
113
|
} : void 0,
|
|
108
|
-
children: /* @__PURE__ */ p(
|
|
114
|
+
children: /* @__PURE__ */ p(w, {
|
|
109
115
|
"aria-hidden": !1,
|
|
110
116
|
role: R ? "button" : "img",
|
|
111
117
|
className: `EntityBadge__Badge ${Y}`,
|
|
@@ -117,7 +123,7 @@ var T = (T) => {
|
|
|
117
123
|
title: "Has a wiki",
|
|
118
124
|
enterNextDelay: 100,
|
|
119
125
|
placement: "right",
|
|
120
|
-
children: /* @__PURE__ */ p(
|
|
126
|
+
children: /* @__PURE__ */ p(C, {
|
|
121
127
|
"aria-hidden": !1,
|
|
122
128
|
role: "img",
|
|
123
129
|
className: "EntityBadge__Badge",
|
|
@@ -128,7 +134,7 @@ var T = (T) => {
|
|
|
128
134
|
title: "Has been mentioned in discussion",
|
|
129
135
|
enterNextDelay: 100,
|
|
130
136
|
placement: "right",
|
|
131
|
-
children: /* @__PURE__ */ p(
|
|
137
|
+
children: /* @__PURE__ */ p(S, {
|
|
132
138
|
"aria-hidden": !1,
|
|
133
139
|
role: "img",
|
|
134
140
|
className: "EntityBadge__Badge",
|
|
@@ -151,7 +157,7 @@ var T = (T) => {
|
|
|
151
157
|
title: "Remove this link",
|
|
152
158
|
enterNextDelay: 100,
|
|
153
159
|
placement: "right",
|
|
154
|
-
children: /* @__PURE__ */ p(
|
|
160
|
+
children: /* @__PURE__ */ p(_, {
|
|
155
161
|
"aria-hidden": !1,
|
|
156
162
|
role: "button",
|
|
157
163
|
onClick: () => J(!0),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityBadgeIcons.js","names":[],"sources":["../../../src/components/EntityBadgeIcons/EntityBadgeIcons.tsx"],"sourcesContent":["import {\n useDeleteEntity,\n useGetEntityBundle,\n useGetJson,\n useGetSchemaBinding,\n useGetValidationResults,\n} from '@/synapse-queries'\nimport { useGetRealmPrincipals } from '@/synapse-queries/realm/useRealmPrincipals'\nimport { isEntityPublic } from '@/utils/functions/AccessControlListUtils'\nimport {\n ChatBubbleTwoTone,\n CheckTwoTone,\n DescriptionTwoTone,\n LinkOffTwoTone,\n LocalOfferTwoTone,\n LockTwoTone,\n PublicTwoTone,\n} from '@mui/icons-material'\nimport { Tooltip } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { ALL_ENTITY_BUNDLE_FIELDS } from '@sage-bionetworks/synapse-types'\nimport { isEmpty } from 'lodash-es'\nimport React, { useEffect, useState } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport { getDisplayedAnnotation } from '../entity/metadata/AnnotationsTable'\nimport { EntityModal } from '../entity/metadata/EntityModal'\nimport { HoverPopover } from '../styled/HoverPopover'\nimport WarningDialog from '../SynapseForm/WarningDialog'\n\nexport type EntityBadgeIconsProps = {\n entityId: string\n versionNumber?: number\n flexWrap?: // possible settings for flex-wrap\n | 'wrap'\n | 'nowrap'\n | '-moz-initial'\n | 'inherit'\n | 'initial'\n | 'revert'\n | 'unset'\n | 'wrap-reverse'\n justifyContent?: 'flex-start' | 'flex-end' | string\n /** Shows an icon indicating if the entity is 'public' or 'private'. Default true */\n showIsPublicPrivate?: boolean\n /** Shows an icon if the entity has sharing settings set on itself. Default true */\n showHasLocalSharingSettings?: boolean\n /** Shows an icon if the entity has annotations, or if it has a validation schema (in experimental mode only). Default true */\n showHasAnnotations?: boolean\n /** Shows an icon if the entity a wiki. Default true */\n showHasWiki?: boolean\n /** Shows an icon if the entity has been mentioned in discussion threads. Default true */\n showHasDiscussionThread?: boolean\n /* Shows an 'unlink' button if the entity is a link and the user has permission to delete it. Default true */\n showUnlink?: boolean\n /* Invoked after the entity is unlinked/deleted in case there is cleanup to do. Returns the entityId */\n onUnlink?: (entityId: string) => void\n onUnlinkError?: (error: unknown) => void\n /** Whether or not the badges (e.g. Annotations) can trigger opening a modal on click */\n canOpenModal: boolean\n}\n\n/**\n * Stateless component used to show icons that show an entity's status.\n * Adapted from https://github.com/Sage-Bionetworks/SynapseWebClient/blob/46b9b717636cda2421926d96365244bbb72a05b6/src/main/java/org/sagebionetworks/web/client/widget/entity/EntityBadge.java\n */\nexport const EntityBadgeIcons = (\n props: EntityBadgeIconsProps,\n): React.ReactNode => {\n const {\n entityId,\n versionNumber,\n flexWrap = 'nowrap',\n justifyContent = 'flex-start',\n showIsPublicPrivate = true,\n showHasLocalSharingSettings = true,\n showHasAnnotations = true,\n showHasWiki = true,\n showHasDiscussionThread = true,\n showUnlink = true,\n onUnlink = () => {\n /* noop */\n },\n onUnlinkError = () => {\n /* noop */\n },\n canOpenModal,\n } = props\n\n const { data } = useGetRealmPrincipals()\n const realmPrincipals = data || {}\n\n enum SchemaConformanceState {\n NO_SCHEMA = '', // or not in experimental mode\n VALID = 'Valid',\n INVALID = 'Invalid',\n ANNOTATIONS_MISSING = 'Missing',\n }\n\n const { ref, inView } = useInView()\n\n const { data: entityData } = useGetJson(entityId, versionNumber, true, {\n enabled: inView,\n })\n const annotations = entityData?.annotations\n const { data: bundle } = useGetEntityBundle(\n entityId,\n versionNumber,\n ALL_ENTITY_BUNDLE_FIELDS,\n {\n enabled: inView,\n },\n )\n const [showModal, setShowModal] = useState(false)\n const [showUnlinkConfirmModal, setShowUnlinkConfirmModal] = useState(false)\n const [schemaConformance, setSchemaConformance] = useState(\n SchemaConformanceState.NO_SCHEMA,\n )\n\n const { data: boundSchema } = useGetSchemaBinding(entityId, {\n enabled: inView,\n staleTime: 60 * 1000, // 60 seconds\n })\n\n const { data: schemaValidationResults } = useGetValidationResults(entityId, {\n enabled: inView && !!boundSchema,\n staleTime: 60 * 1000, // 60 seconds\n })\n\n const annotationsCount =\n annotations && !isEmpty(annotations) ? Object.keys(annotations).length : 0\n useEffect(() => {\n if (schemaValidationResults) {\n if (schemaValidationResults.isValid) {\n setSchemaConformance(SchemaConformanceState.VALID)\n } else if (annotationsCount) {\n // If annotations exist, then indicate that they are invalid\n setSchemaConformance(SchemaConformanceState.INVALID)\n } else {\n // If annotations are missing, it's still invalid, but should appear differently\n setSchemaConformance(SchemaConformanceState.ANNOTATIONS_MISSING)\n }\n }\n }, [\n schemaValidationResults,\n annotationsCount,\n annotations,\n SchemaConformanceState.VALID,\n SchemaConformanceState.INVALID,\n SchemaConformanceState.ANNOTATIONS_MISSING,\n ])\n\n const { mutate: unlinkEntity } = useDeleteEntity({\n onSuccess: (_, entityId) => {\n onUnlink(entityId)\n },\n onError: onUnlinkError,\n })\n\n /**\n * Convert the list of annotations to a string of <tr>...anno1</tr><tr>...anno2</tr>...\n * If there are no annotations, this is the empty string ('')\n */\n const annotationsTableRows = (\n <>\n {annotations\n ? Object.entries(annotations ?? []).map(([key, value]) => (\n <tr key={key}>\n <td>\n <b>{key}</b>\n </td>\n <td>\n {Array.isArray(value)\n ? value.map(getDisplayedAnnotation).join(', ')\n : getDisplayedAnnotation(value as string | number | boolean)}\n </td>\n </tr>\n ))\n : ''}\n </>\n )\n // We also show the schema name if there is one (and we're in experimental mode)\n const valiationSchemaTableRow = (\n <>\n {boundSchema ? (\n <tr>\n <td>\n <b>Validation Schema</b>\n </td>\n <td>{boundSchema.jsonSchemaVersionInfo.schemaName}</td>\n </tr>\n ) : (\n ''\n )}\n </>\n )\n const annotationsTitle = schemaValidationResults\n ? `${schemaConformance} Annotations`\n : ''\n const annotationsHtml = (\n <div className=\"EntityBadgeTooltip\">\n <table>\n <tbody>\n {annotationsTableRows ? annotationsTableRows : ''}\n {valiationSchemaTableRow}\n </tbody>\n </table>\n </div>\n )\n\n return (\n <div className=\"EntityBadge\" ref={ref} style={{ flexWrap, justifyContent }}>\n {bundle && (\n <>\n <div onClick={e => e.stopPropagation()}>\n <EntityModal\n entityId={entityId}\n show={showModal}\n showTabs={false}\n onClose={() => setShowModal(false)}\n initialTab={'ANNOTATIONS'}\n />\n </div>\n {showIsPublicPrivate &&\n bundle.benefactorAcl &&\n isEntityPublic(\n bundle.benefactorAcl.resourceAccess,\n realmPrincipals,\n ) ? (\n <Tooltip title=\"Public\" enterNextDelay={100} placement=\"right\">\n <PublicTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'is-public-icon'}\n />\n </Tooltip>\n ) : null}\n {showIsPublicPrivate &&\n bundle.benefactorAcl &&\n !isEntityPublic(\n bundle.benefactorAcl.resourceAccess,\n realmPrincipals,\n ) ? (\n <Tooltip title=\"Private\" enterNextDelay={100} placement=\"right\">\n <LockTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'is-private-icon'}\n />\n </Tooltip>\n ) : null}\n {showHasLocalSharingSettings &&\n bundle.benefactorAcl &&\n entityId === bundle.benefactorAcl.id ? (\n <Tooltip\n title=\"Sharing Settings have been set\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <CheckTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'sharing-settings-icon'}\n />\n </Tooltip>\n ) : null}\n\n {showHasAnnotations &&\n !!(annotationsCount || schemaValidationResults) && (\n <HoverPopover\n title={annotationsTitle}\n popoverContent={annotationsHtml}\n placement=\"right\"\n maxWidth={500}\n actionButton={\n canOpenModal\n ? {\n content: 'Edit Annotations',\n onClick: () => setShowModal(true),\n closeOnClick: true,\n }\n : undefined\n }\n >\n <LocalOfferTwoTone\n aria-hidden={false}\n role={canOpenModal ? 'button' : 'img'}\n className={`EntityBadge__Badge ${schemaConformance}`}\n data-html={true}\n data-testid={'annotations-icon'}\n />\n </HoverPopover>\n )}\n {showHasWiki && bundle.rootWikiId && (\n <Tooltip title=\"Has a wiki\" enterNextDelay={100} placement=\"right\">\n <DescriptionTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'wiki-icon'}\n />\n </Tooltip>\n )}\n {showHasDiscussionThread &&\n !!bundle.threadCount &&\n !!(bundle.threadCount > 0) && (\n <Tooltip\n title=\"Has been mentioned in discussion\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <ChatBubbleTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'discussion-icon'}\n />\n </Tooltip>\n )}\n {showUnlink &&\n bundle.entityType === EntityType.link &&\n bundle.permissions?.canDelete && (\n <>\n <WarningDialog\n open={showUnlinkConfirmModal}\n title=\"Confirm Unlink\"\n content={\n 'Are you sure you want to remove this link? The original object will not be changed.'\n }\n confirmButtonText=\"Unlink\"\n confirmButtonColor=\"error\"\n onConfirm={() => {\n unlinkEntity(entityId)\n setShowUnlinkConfirmModal(false)\n }}\n onCancel={() => {\n setShowUnlinkConfirmModal(false)\n }}\n />\n <Tooltip\n title=\"Remove this link\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <LinkOffTwoTone\n aria-hidden={false}\n role=\"button\"\n onClick={() => setShowUnlinkConfirmModal(true)}\n className=\"EntityBadge__Badge Unlink\"\n data-testid={'unlink-icon'}\n />\n </Tooltip>\n </>\n )}\n </>\n )}\n </div>\n )\n}\n\nexport default EntityBadgeIcons\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityBadgeIcons.js","names":[],"sources":["../../../src/components/EntityBadgeIcons/EntityBadgeIcons.tsx"],"sourcesContent":["import {\n useDeleteEntity,\n useGetEntityBundle,\n useGetJson,\n useGetSchemaBinding,\n useGetValidationResults,\n} from '@/synapse-queries'\nimport { useGetRealmPrincipals } from '@/synapse-queries/realm/useRealmPrincipals'\nimport { isEntityPublic } from '@/utils/functions/AccessControlListUtils'\nimport ChatBubbleTwoTone from '@mui/icons-material/ChatBubbleTwoTone'\nimport CheckTwoTone from '@mui/icons-material/CheckTwoTone'\nimport DescriptionTwoTone from '@mui/icons-material/DescriptionTwoTone'\nimport LinkOffTwoTone from '@mui/icons-material/LinkOffTwoTone'\nimport LocalOfferTwoTone from '@mui/icons-material/LocalOfferTwoTone'\nimport LockTwoTone from '@mui/icons-material/LockTwoTone'\nimport PublicTwoTone from '@mui/icons-material/PublicTwoTone'\nimport { Tooltip } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { ALL_ENTITY_BUNDLE_FIELDS } from '@sage-bionetworks/synapse-types'\nimport { isEmpty } from 'lodash-es'\nimport React, { useEffect, useState } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport { getDisplayedAnnotation } from '../entity/metadata/AnnotationsTable'\nimport { EntityModal } from '../entity/metadata/EntityModal'\nimport { HoverPopover } from '../styled/HoverPopover'\nimport WarningDialog from '../SynapseForm/WarningDialog'\n\nexport type EntityBadgeIconsProps = {\n entityId: string\n versionNumber?: number\n flexWrap?: // possible settings for flex-wrap\n | 'wrap'\n | 'nowrap'\n | '-moz-initial'\n | 'inherit'\n | 'initial'\n | 'revert'\n | 'unset'\n | 'wrap-reverse'\n justifyContent?: 'flex-start' | 'flex-end' | string\n /** Shows an icon indicating if the entity is 'public' or 'private'. Default true */\n showIsPublicPrivate?: boolean\n /** Shows an icon if the entity has sharing settings set on itself. Default true */\n showHasLocalSharingSettings?: boolean\n /** Shows an icon if the entity has annotations, or if it has a validation schema (in experimental mode only). Default true */\n showHasAnnotations?: boolean\n /** Shows an icon if the entity a wiki. Default true */\n showHasWiki?: boolean\n /** Shows an icon if the entity has been mentioned in discussion threads. Default true */\n showHasDiscussionThread?: boolean\n /* Shows an 'unlink' button if the entity is a link and the user has permission to delete it. Default true */\n showUnlink?: boolean\n /* Invoked after the entity is unlinked/deleted in case there is cleanup to do. Returns the entityId */\n onUnlink?: (entityId: string) => void\n onUnlinkError?: (error: unknown) => void\n /** Whether or not the badges (e.g. Annotations) can trigger opening a modal on click */\n canOpenModal: boolean\n}\n\n/**\n * Stateless component used to show icons that show an entity's status.\n * Adapted from https://github.com/Sage-Bionetworks/SynapseWebClient/blob/46b9b717636cda2421926d96365244bbb72a05b6/src/main/java/org/sagebionetworks/web/client/widget/entity/EntityBadge.java\n */\nexport const EntityBadgeIcons = (\n props: EntityBadgeIconsProps,\n): React.ReactNode => {\n const {\n entityId,\n versionNumber,\n flexWrap = 'nowrap',\n justifyContent = 'flex-start',\n showIsPublicPrivate = true,\n showHasLocalSharingSettings = true,\n showHasAnnotations = true,\n showHasWiki = true,\n showHasDiscussionThread = true,\n showUnlink = true,\n onUnlink = () => {\n /* noop */\n },\n onUnlinkError = () => {\n /* noop */\n },\n canOpenModal,\n } = props\n\n const { data } = useGetRealmPrincipals()\n const realmPrincipals = data || {}\n\n enum SchemaConformanceState {\n NO_SCHEMA = '', // or not in experimental mode\n VALID = 'Valid',\n INVALID = 'Invalid',\n ANNOTATIONS_MISSING = 'Missing',\n }\n\n const { ref, inView } = useInView()\n\n const { data: entityData } = useGetJson(entityId, versionNumber, true, {\n enabled: inView,\n })\n const annotations = entityData?.annotations\n const { data: bundle } = useGetEntityBundle(\n entityId,\n versionNumber,\n ALL_ENTITY_BUNDLE_FIELDS,\n {\n enabled: inView,\n },\n )\n const [showModal, setShowModal] = useState(false)\n const [showUnlinkConfirmModal, setShowUnlinkConfirmModal] = useState(false)\n const [schemaConformance, setSchemaConformance] = useState(\n SchemaConformanceState.NO_SCHEMA,\n )\n\n const { data: boundSchema } = useGetSchemaBinding(entityId, {\n enabled: inView,\n staleTime: 60 * 1000, // 60 seconds\n })\n\n const { data: schemaValidationResults } = useGetValidationResults(entityId, {\n enabled: inView && !!boundSchema,\n staleTime: 60 * 1000, // 60 seconds\n })\n\n const annotationsCount =\n annotations && !isEmpty(annotations) ? Object.keys(annotations).length : 0\n useEffect(() => {\n if (schemaValidationResults) {\n if (schemaValidationResults.isValid) {\n setSchemaConformance(SchemaConformanceState.VALID)\n } else if (annotationsCount) {\n // If annotations exist, then indicate that they are invalid\n setSchemaConformance(SchemaConformanceState.INVALID)\n } else {\n // If annotations are missing, it's still invalid, but should appear differently\n setSchemaConformance(SchemaConformanceState.ANNOTATIONS_MISSING)\n }\n }\n }, [\n schemaValidationResults,\n annotationsCount,\n annotations,\n SchemaConformanceState.VALID,\n SchemaConformanceState.INVALID,\n SchemaConformanceState.ANNOTATIONS_MISSING,\n ])\n\n const { mutate: unlinkEntity } = useDeleteEntity({\n onSuccess: (_, entityId) => {\n onUnlink(entityId)\n },\n onError: onUnlinkError,\n })\n\n /**\n * Convert the list of annotations to a string of <tr>...anno1</tr><tr>...anno2</tr>...\n * If there are no annotations, this is the empty string ('')\n */\n const annotationsTableRows = (\n <>\n {annotations\n ? Object.entries(annotations ?? []).map(([key, value]) => (\n <tr key={key}>\n <td>\n <b>{key}</b>\n </td>\n <td>\n {Array.isArray(value)\n ? value.map(getDisplayedAnnotation).join(', ')\n : getDisplayedAnnotation(value as string | number | boolean)}\n </td>\n </tr>\n ))\n : ''}\n </>\n )\n // We also show the schema name if there is one (and we're in experimental mode)\n const valiationSchemaTableRow = (\n <>\n {boundSchema ? (\n <tr>\n <td>\n <b>Validation Schema</b>\n </td>\n <td>{boundSchema.jsonSchemaVersionInfo.schemaName}</td>\n </tr>\n ) : (\n ''\n )}\n </>\n )\n const annotationsTitle = schemaValidationResults\n ? `${schemaConformance} Annotations`\n : ''\n const annotationsHtml = (\n <div className=\"EntityBadgeTooltip\">\n <table>\n <tbody>\n {annotationsTableRows ? annotationsTableRows : ''}\n {valiationSchemaTableRow}\n </tbody>\n </table>\n </div>\n )\n\n return (\n <div className=\"EntityBadge\" ref={ref} style={{ flexWrap, justifyContent }}>\n {bundle && (\n <>\n <div onClick={e => e.stopPropagation()}>\n <EntityModal\n entityId={entityId}\n show={showModal}\n showTabs={false}\n onClose={() => setShowModal(false)}\n initialTab={'ANNOTATIONS'}\n />\n </div>\n {showIsPublicPrivate &&\n bundle.benefactorAcl &&\n isEntityPublic(\n bundle.benefactorAcl.resourceAccess,\n realmPrincipals,\n ) ? (\n <Tooltip title=\"Public\" enterNextDelay={100} placement=\"right\">\n <PublicTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'is-public-icon'}\n />\n </Tooltip>\n ) : null}\n {showIsPublicPrivate &&\n bundle.benefactorAcl &&\n !isEntityPublic(\n bundle.benefactorAcl.resourceAccess,\n realmPrincipals,\n ) ? (\n <Tooltip title=\"Private\" enterNextDelay={100} placement=\"right\">\n <LockTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'is-private-icon'}\n />\n </Tooltip>\n ) : null}\n {showHasLocalSharingSettings &&\n bundle.benefactorAcl &&\n entityId === bundle.benefactorAcl.id ? (\n <Tooltip\n title=\"Sharing Settings have been set\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <CheckTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'sharing-settings-icon'}\n />\n </Tooltip>\n ) : null}\n\n {showHasAnnotations &&\n !!(annotationsCount || schemaValidationResults) && (\n <HoverPopover\n title={annotationsTitle}\n popoverContent={annotationsHtml}\n placement=\"right\"\n maxWidth={500}\n actionButton={\n canOpenModal\n ? {\n content: 'Edit Annotations',\n onClick: () => setShowModal(true),\n closeOnClick: true,\n }\n : undefined\n }\n >\n <LocalOfferTwoTone\n aria-hidden={false}\n role={canOpenModal ? 'button' : 'img'}\n className={`EntityBadge__Badge ${schemaConformance}`}\n data-html={true}\n data-testid={'annotations-icon'}\n />\n </HoverPopover>\n )}\n {showHasWiki && bundle.rootWikiId && (\n <Tooltip title=\"Has a wiki\" enterNextDelay={100} placement=\"right\">\n <DescriptionTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'wiki-icon'}\n />\n </Tooltip>\n )}\n {showHasDiscussionThread &&\n !!bundle.threadCount &&\n !!(bundle.threadCount > 0) && (\n <Tooltip\n title=\"Has been mentioned in discussion\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <ChatBubbleTwoTone\n aria-hidden={false}\n role=\"img\"\n className=\"EntityBadge__Badge\"\n data-testid={'discussion-icon'}\n />\n </Tooltip>\n )}\n {showUnlink &&\n bundle.entityType === EntityType.link &&\n bundle.permissions?.canDelete && (\n <>\n <WarningDialog\n open={showUnlinkConfirmModal}\n title=\"Confirm Unlink\"\n content={\n 'Are you sure you want to remove this link? The original object will not be changed.'\n }\n confirmButtonText=\"Unlink\"\n confirmButtonColor=\"error\"\n onConfirm={() => {\n unlinkEntity(entityId)\n setShowUnlinkConfirmModal(false)\n }}\n onCancel={() => {\n setShowUnlinkConfirmModal(false)\n }}\n />\n <Tooltip\n title=\"Remove this link\"\n enterNextDelay={100}\n placement=\"right\"\n >\n <LinkOffTwoTone\n aria-hidden={false}\n role=\"button\"\n onClick={() => setShowUnlinkConfirmModal(true)}\n className=\"EntityBadge__Badge Unlink\"\n data-testid={'unlink-icon'}\n />\n </Tooltip>\n </>\n )}\n </>\n )}\n </div>\n )\n}\n\nexport default EntityBadgeIcons\n"],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ import { useState as y } from "react";
|
|
|
18
18
|
import { Fragment as ee, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
19
19
|
import { FileHandleAssociateType as S } from "@sage-bionetworks/synapse-types";
|
|
20
20
|
import { EntityType as C } from "@sage-bionetworks/synapse-client";
|
|
21
|
-
import
|
|
21
|
+
import w from "@mui/icons-material/DownloadOutlined";
|
|
22
22
|
import { createPortal as T } from "react-dom";
|
|
23
23
|
//#region src/components/EntityDownloadButton/EntityDownloadButton.tsx
|
|
24
24
|
var E = "import synapseclient\nsyn = synapseclient.Synapse()\nsyn.login(authToken=\"YOUR_TOKEN_HERE\")", D = "library(synapser)\nsynLogin(authToken=\"YOUR_TOKEN_HERE\")";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityDownloadButton.js","names":[],"sources":["../../../src/components/EntityDownloadButton/EntityDownloadButton.tsx"],"sourcesContent":["import { useGetEntity, useGetVersions } from '@/synapse-queries'\nimport {\n useAddFileToDownloadList,\n useGetAddToDownloadListStats,\n} from '@/synapse-queries/index'\nimport { useSynapseContext } from '@/utils'\nimport {\n entityTypeToFriendlyName,\n hasFilesInView,\n isContainerType,\n isDataset,\n isEntityView,\n isVersionableEntity,\n} from '@/utils/functions/EntityTypeUtils'\nimport { useDirectDownloadHandler } from '@/utils/hooks/useDirectDownloadHandler'\nimport { isFileEntity } from '@/utils/types/IsType'\nimport { DownloadOutlined as DownloadIcon } from '@mui/icons-material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n FileHandleAssociateType,\n QueryBundleRequest,\n} from '@sage-bionetworks/synapse-types'\nimport { RefObject, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { displayFilesWereAddedToDownloadListSuccess } from '../download_list/DownloadConfirmationUtils'\nimport { EntityDownloadConfirmation } from '../EntityDownloadConfirmation'\nimport { DropdownMenu, DropdownMenuItem } from '../menu/DropdownMenu'\nimport { ModalDownload } from '../ModalDownload/ModalDownload'\nimport { ProgrammaticInstructionsModal } from '../ProgrammaticInstructionsModal/ProgrammaticInstructionsModal'\nimport { displayToast } from '../ToastMessage/index'\n\n// WIP\n// Per Nick Grosenbacher: For this to be reusable, I think we would also need to accept versionNumber as a prop. Where we would have the following behavior:\n// - If the version number is present, use it\n// - If the version number is null, do not use it,\n// - If the version number is undefined, then we would try to do the 'smart' thing-- get the latest version for datasets, otherwise don't use it.\n\n// Have to keep these consts outside of getProgrammaticAccessCode because\n// they are being called in ProgrammaticTableDownload.tsx\n// Python client import and login instructions\nexport const PYTHON_CLIENT_IMPORT_AND_LOGIN = `import synapseclient\nsyn = synapseclient.Synapse()\nsyn.login(authToken=\"YOUR_TOKEN_HERE\")`\n\n// R client import and login instructions\nexport const R_CLIENT_IMPORT_AND_LOGIN = `library(synapser)\nsynLogin(authToken=\"YOUR_TOKEN_HERE\")`\n\ntype ProgrammaticAccessCode = {\n cliCode: string | undefined\n rCode: string | undefined\n pythonCode: string | undefined\n}\n\n// Generate programmatic access code snippet content based on entity type\nexport function getProgrammaticAccessCode(\n type: EntityType,\n entityId: string,\n version: number | undefined,\n): ProgrammaticAccessCode {\n const id =\n type === EntityType.dataset && version ? `${entityId}.${version}` : entityId\n switch (type) {\n case EntityType.file:\n case EntityType.folder:\n case EntityType.project:\n case EntityType.link:\n return {\n cliCode: `synapse get -r ${entityId}`,\n rCode: `${R_CLIENT_IMPORT_AND_LOGIN} \\n\n# Download file\nsynGet('${entityId}')`,\n pythonCode: `${PYTHON_CLIENT_IMPORT_AND_LOGIN} \\n\n# Download file\nsyn.get('${entityId}')`,\n }\n case EntityType.dockerrepo:\n return {\n cliCode: `docker login -u <synapse username> -p <synapse password> docker.synapse.org \\n\ndocker pull docker.synapse.org/${entityId}/myrepo`,\n rCode: undefined,\n pythonCode: undefined,\n }\n case EntityType.dataset:\n case EntityType.entityview:\n case EntityType.datasetcollection:\n case EntityType.table:\n case EntityType.materializedview:\n case EntityType.submissionview:\n case EntityType.virtualtable:\n case EntityType.recordset:\n return {\n cliCode: `synapse get -q \"SELECT * FROM ${id}\"`,\n rCode: `${R_CLIENT_IMPORT_AND_LOGIN} \\n\nquery <- synTableQuery(\"SELECT * FROM ${id}\")\nread.table(query$filepath, sep=\",\")`,\n pythonCode: `${PYTHON_CLIENT_IMPORT_AND_LOGIN} \\n\nquery = syn.tableQuery(\"SELECT * FROM ${id}\")\nquery.asDataFrame()`,\n }\n case EntityType.searchindex:\n return {\n cliCode: undefined,\n rCode: undefined,\n pythonCode: undefined,\n }\n default:\n throw new Error(`Unhandled EntityType: ${type}`)\n }\n}\n\nenum DownloadAction {\n downloadFile,\n addToCart,\n programmaticAccess,\n programmaticAccessDocker,\n exportTable,\n}\n\n// Function that creates menu items for download actions\nfunction getMenuItemForAction(\n entityId: string,\n entityName: string,\n entityType: EntityType,\n downloadAction: DownloadAction,\n setShowProgrammaticAccess: (show: boolean) => void,\n setShowExportMetadata: (show: boolean) => void,\n addFileToDownloadList: (params: {\n entityId: string\n entityVersionNumber: number | undefined\n }) => void,\n setShowDownloadConfirmation: (show: boolean) => void,\n versionNumber?: number,\n addToCartDisabled?: boolean,\n onDownloadFile?: () => void,\n isAuthenticated?: boolean,\n): DropdownMenuItem {\n switch (downloadAction) {\n case DownloadAction.downloadFile:\n return {\n text: 'Download File',\n onClick: () => {\n if (onDownloadFile) onDownloadFile()\n },\n tooltipText: isAuthenticated\n ? 'Download this file directly'\n : 'Sign in to download this file',\n disabled: !isAuthenticated,\n }\n case DownloadAction.addToCart:\n return {\n text: 'Add to Download List',\n disabled: addToCartDisabled,\n tooltipText: getAddToCartTooltip(entityType, !!addToCartDisabled),\n onClick: () => {\n if (\n entityType === EntityType.file ||\n entityType === EntityType.recordset\n ) {\n addFileToDownloadList({\n entityId,\n entityVersionNumber: versionNumber,\n })\n } else {\n setShowDownloadConfirmation(true)\n }\n },\n }\n case DownloadAction.programmaticAccess:\n return {\n text: 'Programmatic Access',\n onClick: () => {\n setShowProgrammaticAccess(true)\n },\n tooltipText: 'View programmatic access options',\n }\n case DownloadAction.programmaticAccessDocker:\n return {\n text: 'Programmatic Access (Docker)',\n onClick: () => {\n setShowProgrammaticAccess(true)\n },\n tooltipText: 'View programmatic options to pull Docker image',\n }\n case DownloadAction.exportTable:\n return {\n text: 'Export Table',\n onClick: () => {\n setShowExportMetadata(true)\n },\n tooltipText: 'Export table data',\n }\n }\n}\n\n/**\n * Determines the tooltip message based on the entity type and its \"add to cart\" status.\n */\nconst getAddToCartTooltip = (\n entityType: EntityType,\n isDisabled: boolean,\n): string => {\n if (!isDisabled) {\n return 'Add file(s) to your download list'\n }\n const entityFriendlyName = entityTypeToFriendlyName(entityType)\n return `This ${entityFriendlyName} has no accessible files`\n}\n\n// Function that returns DropdownMenuItem\nexport function getDownloadActionsForEntityType(\n type: EntityType,\n): DownloadAction[][] {\n switch (type) {\n case EntityType.file:\n case EntityType.recordset:\n return [\n [DownloadAction.downloadFile],\n [DownloadAction.addToCart, DownloadAction.programmaticAccess],\n ]\n case EntityType.project:\n case EntityType.folder:\n return [[DownloadAction.addToCart, DownloadAction.programmaticAccess]]\n case EntityType.dockerrepo:\n return [[DownloadAction.programmaticAccessDocker]]\n case EntityType.entityview:\n case EntityType.dataset:\n return [\n [\n DownloadAction.exportTable,\n DownloadAction.programmaticAccess,\n DownloadAction.addToCart,\n ],\n ]\n case EntityType.datasetcollection:\n case EntityType.table:\n case EntityType.materializedview:\n case EntityType.submissionview:\n case EntityType.virtualtable:\n return [[DownloadAction.exportTable, DownloadAction.programmaticAccess]]\n case EntityType.link:\n return [[DownloadAction.programmaticAccess]]\n case EntityType.searchindex:\n return []\n default:\n // this will fail if a new EntityType is added and not handled\n throw new Error(`Unhandled EntityType: ${type}`)\n }\n}\n\n// get the appropriate version number for download based on entity type\n// for datasets: returns the most recent released version if any exist, undefined otherwise\n// for other entities: returns the current version number\nfunction useGetLatestVersionNumber(entityId: string, entityType: EntityType) {\n // get entity data\n const { data: entityData, isLoading: entityDataLoading } =\n useGetEntity(entityId) // No version = latest\n\n // for datasets, check if any versions exist and use the most recent one\n const mustGetVersion = entityType === EntityType.dataset\n const { data: versionsData, isLoading: versionsLoading } = useGetVersions(\n entityId,\n 0,\n 1,\n {\n enabled: mustGetVersion,\n },\n )\n\n let latestVersionNumber: number | undefined\n\n if (mustGetVersion) {\n // for datasets, check if any versions exist\n if (versionsData?.results && versionsData.results.length > 0) {\n // Use the most recent released version (first in the list, since versions are returned in descending order)\n latestVersionNumber = versionsData.results[0].versionNumber\n } else {\n // no versions exist, use undefined (will use current/draft version)\n latestVersionNumber = undefined\n }\n } else {\n // for non-datasets, use the entity's version number\n latestVersionNumber =\n entityData && isVersionableEntity(entityData)\n ? entityData.versionNumber\n : undefined\n }\n\n const isLoading = entityDataLoading || versionsLoading\n\n return { latestVersionNumber, isLoading }\n}\n\n// create default queryBundleRequest with appropriate SQL\nfunction getDefaultQueryBundleRequestForEntity(\n entityId: string,\n versionNumber: number | undefined,\n entityType: EntityType,\n): QueryBundleRequest {\n let sql: string\n\n if (entityType === EntityType.dataset) {\n // for datasets:\n // - if a version number exists (released version), use it: syn123.5\n // - if no version number (no released versions), use current/draft: syn123\n sql = versionNumber\n ? `SELECT * FROM ${entityId}.${versionNumber}`\n : `SELECT * FROM ${entityId}`\n } else {\n // For non-datasets, always use entity ID without version\n sql = `SELECT * FROM ${entityId}`\n }\n\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: `${entityId}`,\n query: {\n sql: sql,\n },\n partMask: 0,\n }\n}\n\nexport function EntityDownloadButton(props: {\n entityId: string\n name: string\n entityType: EntityType\n downloadConfirmationContainer?: RefObject<HTMLElement | null>\n disabled?: boolean\n}) {\n // get the appropriate version number for the entity\n const { latestVersionNumber } = useGetLatestVersionNumber(\n props.entityId,\n props.entityType,\n )\n // create queryBundleRequest with appropriate SQL based on entity type and version\n const defaultQueryBundleRequest = getDefaultQueryBundleRequestForEntity(\n props.entityId,\n latestVersionNumber,\n props.entityType,\n )\n\n // Get context and download functionality\n const { downloadCartPageUrl, isAuthenticated } = useSynapseContext()\n const { mutate: addFileToDownloadList } = useAddFileToDownloadList({\n onSuccess: data => {\n if (data.numberOfFilesAdded > 0) {\n displayFilesWereAddedToDownloadListSuccess(downloadCartPageUrl)\n } else {\n displayToast('0 Files added to your Download List', 'info')\n }\n },\n onError: error => {\n displayToast(error.reason, 'danger')\n },\n })\n\n // state to manage programmatic access modal visibility\n const [showProgrammaticAccess, setShowProgrammaticAccess] =\n useState<boolean>(false)\n\n // state to manage download confirmation visibility and loading\n const [showDownloadConfirmation, setShowDownloadConfirmation] =\n useState<boolean>(false)\n const [downloadConfirmationLoading, setDownloadConfirmationLoading] =\n useState<boolean>(false)\n\n const handleCloseProgrammaticAccess = () => {\n setShowProgrammaticAccess(false)\n }\n\n const isFolderOrProject = isContainerType(props.entityType)\n\n const {\n data: folderOrProjectStats,\n isLoading: isLoadingFolderOrProjectStats,\n } = useGetAddToDownloadListStats(\n {\n concreteType:\n 'org.sagebionetworks.repo.model.download.AddToDownloadListStatsRequest',\n request: {\n parentId: props.entityId,\n concreteType:\n 'org.sagebionetworks.repo.model.download.AddToDownloadListRequest',\n recursive: true,\n },\n },\n {\n enabled: isFolderOrProject,\n },\n )\n\n const folderOrProjectHasNoFiles =\n isFolderOrProject &&\n (isLoadingFolderOrProjectStats || folderOrProjectStats?.fileCount === 0)\n\n const { data: entityData } = useGetEntity(props.entityId)\n\n const { downloadFile } = useDirectDownloadHandler()\n\n const fileHandleId =\n entityData && isFileEntity(entityData)\n ? entityData.dataFileHandleId\n : undefined\n\n const onDownloadFile = fileHandleId\n ? () => {\n void downloadFile({\n fileHandleId,\n associatedObjectId: props.entityId,\n associatedObjectType: FileHandleAssociateType.FileEntity,\n })\n }\n : undefined\n\n const entityViewHasNoFiles =\n entityData && isEntityView(entityData) && !hasFilesInView(entityData)\n\n const datasetHasNoFiles =\n entityData &&\n isDataset(entityData) &&\n (!entityData.items || entityData.items.length === 0)\n\n const addToCartDisabled =\n folderOrProjectHasNoFiles || entityViewHasNoFiles || datasetHasNoFiles\n\n // state to manage export metadata modal visibility\n const [showExportMetadata, setShowExportMetadata] = useState<boolean>(false)\n\n const handleCloseExportMetadata = () => {\n setShowExportMetadata(false)\n }\n\n // Create download menu items\n const downloadActions = getDownloadActionsForEntityType(props.entityType)\n const downloadMenuItems = downloadActions.map(actionGroup =>\n actionGroup.map(action =>\n getMenuItemForAction(\n props.entityId,\n props.name,\n props.entityType,\n action,\n setShowProgrammaticAccess,\n setShowExportMetadata,\n addFileToDownloadList,\n setShowDownloadConfirmation,\n latestVersionNumber,\n addToCartDisabled,\n onDownloadFile,\n isAuthenticated,\n ),\n ),\n )\n\n // Return programmatic access modal content\n const { cliCode, rCode, pythonCode } = getProgrammaticAccessCode(\n props.entityType,\n props.entityId,\n latestVersionNumber,\n )\n\n const downloadConfirmation = showDownloadConfirmation ? (\n <EntityDownloadConfirmation\n entityId={props.entityId}\n handleClose={() => setShowDownloadConfirmation(false)}\n onIsLoadingChange={setDownloadConfirmationLoading}\n />\n ) : null\n\n return (\n <>\n <DropdownMenu\n items={downloadMenuItems}\n dropdownButtonText=\"Download\"\n buttonTooltip=\"Download options for this entity\"\n buttonProps={{\n variant: 'outlined',\n startIcon: <DownloadIcon />,\n disabled: props.disabled || downloadConfirmationLoading,\n }}\n />\n {downloadConfirmation &&\n (props.downloadConfirmationContainer?.current\n ? createPortal(\n downloadConfirmation,\n props.downloadConfirmationContainer.current,\n )\n : downloadConfirmation)}\n <ProgrammaticInstructionsModal\n show={showProgrammaticAccess}\n title={`Programmatic Access: ${props.name}`}\n onClose={handleCloseProgrammaticAccess}\n pythonCode={pythonCode}\n rCode={rCode}\n cliCode={cliCode}\n helpUrl=\"https://help.synapse.org/docs/Synapse-Docker-Registry.2011037752.html#SynapseDockerRegistry-UsingDockerImagesStoredintheSynapseDockerRegistry\"\n />\n {showExportMetadata && (\n <ModalDownload\n queryBundleRequest={defaultQueryBundleRequest}\n onClose={handleCloseExportMetadata}\n />\n )}\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EntityDownloadButton.js","names":[],"sources":["../../../src/components/EntityDownloadButton/EntityDownloadButton.tsx"],"sourcesContent":["import { useGetEntity, useGetVersions } from '@/synapse-queries'\nimport {\n useAddFileToDownloadList,\n useGetAddToDownloadListStats,\n} from '@/synapse-queries/index'\nimport { useSynapseContext } from '@/utils'\nimport {\n entityTypeToFriendlyName,\n hasFilesInView,\n isContainerType,\n isDataset,\n isEntityView,\n isVersionableEntity,\n} from '@/utils/functions/EntityTypeUtils'\nimport { useDirectDownloadHandler } from '@/utils/hooks/useDirectDownloadHandler'\nimport { isFileEntity } from '@/utils/types/IsType'\nimport DownloadIcon from '@mui/icons-material/DownloadOutlined'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n FileHandleAssociateType,\n QueryBundleRequest,\n} from '@sage-bionetworks/synapse-types'\nimport { RefObject, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { displayFilesWereAddedToDownloadListSuccess } from '../download_list/DownloadConfirmationUtils'\nimport { EntityDownloadConfirmation } from '../EntityDownloadConfirmation'\nimport { DropdownMenu, DropdownMenuItem } from '../menu/DropdownMenu'\nimport { ModalDownload } from '../ModalDownload/ModalDownload'\nimport { ProgrammaticInstructionsModal } from '../ProgrammaticInstructionsModal/ProgrammaticInstructionsModal'\nimport { displayToast } from '../ToastMessage/index'\n\n// WIP\n// Per Nick Grosenbacher: For this to be reusable, I think we would also need to accept versionNumber as a prop. Where we would have the following behavior:\n// - If the version number is present, use it\n// - If the version number is null, do not use it,\n// - If the version number is undefined, then we would try to do the 'smart' thing-- get the latest version for datasets, otherwise don't use it.\n\n// Have to keep these consts outside of getProgrammaticAccessCode because\n// they are being called in ProgrammaticTableDownload.tsx\n// Python client import and login instructions\nexport const PYTHON_CLIENT_IMPORT_AND_LOGIN = `import synapseclient\nsyn = synapseclient.Synapse()\nsyn.login(authToken=\"YOUR_TOKEN_HERE\")`\n\n// R client import and login instructions\nexport const R_CLIENT_IMPORT_AND_LOGIN = `library(synapser)\nsynLogin(authToken=\"YOUR_TOKEN_HERE\")`\n\ntype ProgrammaticAccessCode = {\n cliCode: string | undefined\n rCode: string | undefined\n pythonCode: string | undefined\n}\n\n// Generate programmatic access code snippet content based on entity type\nexport function getProgrammaticAccessCode(\n type: EntityType,\n entityId: string,\n version: number | undefined,\n): ProgrammaticAccessCode {\n const id =\n type === EntityType.dataset && version ? `${entityId}.${version}` : entityId\n switch (type) {\n case EntityType.file:\n case EntityType.folder:\n case EntityType.project:\n case EntityType.link:\n return {\n cliCode: `synapse get -r ${entityId}`,\n rCode: `${R_CLIENT_IMPORT_AND_LOGIN} \\n\n# Download file\nsynGet('${entityId}')`,\n pythonCode: `${PYTHON_CLIENT_IMPORT_AND_LOGIN} \\n\n# Download file\nsyn.get('${entityId}')`,\n }\n case EntityType.dockerrepo:\n return {\n cliCode: `docker login -u <synapse username> -p <synapse password> docker.synapse.org \\n\ndocker pull docker.synapse.org/${entityId}/myrepo`,\n rCode: undefined,\n pythonCode: undefined,\n }\n case EntityType.dataset:\n case EntityType.entityview:\n case EntityType.datasetcollection:\n case EntityType.table:\n case EntityType.materializedview:\n case EntityType.submissionview:\n case EntityType.virtualtable:\n case EntityType.recordset:\n return {\n cliCode: `synapse get -q \"SELECT * FROM ${id}\"`,\n rCode: `${R_CLIENT_IMPORT_AND_LOGIN} \\n\nquery <- synTableQuery(\"SELECT * FROM ${id}\")\nread.table(query$filepath, sep=\",\")`,\n pythonCode: `${PYTHON_CLIENT_IMPORT_AND_LOGIN} \\n\nquery = syn.tableQuery(\"SELECT * FROM ${id}\")\nquery.asDataFrame()`,\n }\n case EntityType.searchindex:\n return {\n cliCode: undefined,\n rCode: undefined,\n pythonCode: undefined,\n }\n default:\n throw new Error(`Unhandled EntityType: ${type}`)\n }\n}\n\nenum DownloadAction {\n downloadFile,\n addToCart,\n programmaticAccess,\n programmaticAccessDocker,\n exportTable,\n}\n\n// Function that creates menu items for download actions\nfunction getMenuItemForAction(\n entityId: string,\n entityName: string,\n entityType: EntityType,\n downloadAction: DownloadAction,\n setShowProgrammaticAccess: (show: boolean) => void,\n setShowExportMetadata: (show: boolean) => void,\n addFileToDownloadList: (params: {\n entityId: string\n entityVersionNumber: number | undefined\n }) => void,\n setShowDownloadConfirmation: (show: boolean) => void,\n versionNumber?: number,\n addToCartDisabled?: boolean,\n onDownloadFile?: () => void,\n isAuthenticated?: boolean,\n): DropdownMenuItem {\n switch (downloadAction) {\n case DownloadAction.downloadFile:\n return {\n text: 'Download File',\n onClick: () => {\n if (onDownloadFile) onDownloadFile()\n },\n tooltipText: isAuthenticated\n ? 'Download this file directly'\n : 'Sign in to download this file',\n disabled: !isAuthenticated,\n }\n case DownloadAction.addToCart:\n return {\n text: 'Add to Download List',\n disabled: addToCartDisabled,\n tooltipText: getAddToCartTooltip(entityType, !!addToCartDisabled),\n onClick: () => {\n if (\n entityType === EntityType.file ||\n entityType === EntityType.recordset\n ) {\n addFileToDownloadList({\n entityId,\n entityVersionNumber: versionNumber,\n })\n } else {\n setShowDownloadConfirmation(true)\n }\n },\n }\n case DownloadAction.programmaticAccess:\n return {\n text: 'Programmatic Access',\n onClick: () => {\n setShowProgrammaticAccess(true)\n },\n tooltipText: 'View programmatic access options',\n }\n case DownloadAction.programmaticAccessDocker:\n return {\n text: 'Programmatic Access (Docker)',\n onClick: () => {\n setShowProgrammaticAccess(true)\n },\n tooltipText: 'View programmatic options to pull Docker image',\n }\n case DownloadAction.exportTable:\n return {\n text: 'Export Table',\n onClick: () => {\n setShowExportMetadata(true)\n },\n tooltipText: 'Export table data',\n }\n }\n}\n\n/**\n * Determines the tooltip message based on the entity type and its \"add to cart\" status.\n */\nconst getAddToCartTooltip = (\n entityType: EntityType,\n isDisabled: boolean,\n): string => {\n if (!isDisabled) {\n return 'Add file(s) to your download list'\n }\n const entityFriendlyName = entityTypeToFriendlyName(entityType)\n return `This ${entityFriendlyName} has no accessible files`\n}\n\n// Function that returns DropdownMenuItem\nexport function getDownloadActionsForEntityType(\n type: EntityType,\n): DownloadAction[][] {\n switch (type) {\n case EntityType.file:\n case EntityType.recordset:\n return [\n [DownloadAction.downloadFile],\n [DownloadAction.addToCart, DownloadAction.programmaticAccess],\n ]\n case EntityType.project:\n case EntityType.folder:\n return [[DownloadAction.addToCart, DownloadAction.programmaticAccess]]\n case EntityType.dockerrepo:\n return [[DownloadAction.programmaticAccessDocker]]\n case EntityType.entityview:\n case EntityType.dataset:\n return [\n [\n DownloadAction.exportTable,\n DownloadAction.programmaticAccess,\n DownloadAction.addToCart,\n ],\n ]\n case EntityType.datasetcollection:\n case EntityType.table:\n case EntityType.materializedview:\n case EntityType.submissionview:\n case EntityType.virtualtable:\n return [[DownloadAction.exportTable, DownloadAction.programmaticAccess]]\n case EntityType.link:\n return [[DownloadAction.programmaticAccess]]\n case EntityType.searchindex:\n return []\n default:\n // this will fail if a new EntityType is added and not handled\n throw new Error(`Unhandled EntityType: ${type}`)\n }\n}\n\n// get the appropriate version number for download based on entity type\n// for datasets: returns the most recent released version if any exist, undefined otherwise\n// for other entities: returns the current version number\nfunction useGetLatestVersionNumber(entityId: string, entityType: EntityType) {\n // get entity data\n const { data: entityData, isLoading: entityDataLoading } =\n useGetEntity(entityId) // No version = latest\n\n // for datasets, check if any versions exist and use the most recent one\n const mustGetVersion = entityType === EntityType.dataset\n const { data: versionsData, isLoading: versionsLoading } = useGetVersions(\n entityId,\n 0,\n 1,\n {\n enabled: mustGetVersion,\n },\n )\n\n let latestVersionNumber: number | undefined\n\n if (mustGetVersion) {\n // for datasets, check if any versions exist\n if (versionsData?.results && versionsData.results.length > 0) {\n // Use the most recent released version (first in the list, since versions are returned in descending order)\n latestVersionNumber = versionsData.results[0].versionNumber\n } else {\n // no versions exist, use undefined (will use current/draft version)\n latestVersionNumber = undefined\n }\n } else {\n // for non-datasets, use the entity's version number\n latestVersionNumber =\n entityData && isVersionableEntity(entityData)\n ? entityData.versionNumber\n : undefined\n }\n\n const isLoading = entityDataLoading || versionsLoading\n\n return { latestVersionNumber, isLoading }\n}\n\n// create default queryBundleRequest with appropriate SQL\nfunction getDefaultQueryBundleRequestForEntity(\n entityId: string,\n versionNumber: number | undefined,\n entityType: EntityType,\n): QueryBundleRequest {\n let sql: string\n\n if (entityType === EntityType.dataset) {\n // for datasets:\n // - if a version number exists (released version), use it: syn123.5\n // - if no version number (no released versions), use current/draft: syn123\n sql = versionNumber\n ? `SELECT * FROM ${entityId}.${versionNumber}`\n : `SELECT * FROM ${entityId}`\n } else {\n // For non-datasets, always use entity ID without version\n sql = `SELECT * FROM ${entityId}`\n }\n\n return {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId: `${entityId}`,\n query: {\n sql: sql,\n },\n partMask: 0,\n }\n}\n\nexport function EntityDownloadButton(props: {\n entityId: string\n name: string\n entityType: EntityType\n downloadConfirmationContainer?: RefObject<HTMLElement | null>\n disabled?: boolean\n}) {\n // get the appropriate version number for the entity\n const { latestVersionNumber } = useGetLatestVersionNumber(\n props.entityId,\n props.entityType,\n )\n // create queryBundleRequest with appropriate SQL based on entity type and version\n const defaultQueryBundleRequest = getDefaultQueryBundleRequestForEntity(\n props.entityId,\n latestVersionNumber,\n props.entityType,\n )\n\n // Get context and download functionality\n const { downloadCartPageUrl, isAuthenticated } = useSynapseContext()\n const { mutate: addFileToDownloadList } = useAddFileToDownloadList({\n onSuccess: data => {\n if (data.numberOfFilesAdded > 0) {\n displayFilesWereAddedToDownloadListSuccess(downloadCartPageUrl)\n } else {\n displayToast('0 Files added to your Download List', 'info')\n }\n },\n onError: error => {\n displayToast(error.reason, 'danger')\n },\n })\n\n // state to manage programmatic access modal visibility\n const [showProgrammaticAccess, setShowProgrammaticAccess] =\n useState<boolean>(false)\n\n // state to manage download confirmation visibility and loading\n const [showDownloadConfirmation, setShowDownloadConfirmation] =\n useState<boolean>(false)\n const [downloadConfirmationLoading, setDownloadConfirmationLoading] =\n useState<boolean>(false)\n\n const handleCloseProgrammaticAccess = () => {\n setShowProgrammaticAccess(false)\n }\n\n const isFolderOrProject = isContainerType(props.entityType)\n\n const {\n data: folderOrProjectStats,\n isLoading: isLoadingFolderOrProjectStats,\n } = useGetAddToDownloadListStats(\n {\n concreteType:\n 'org.sagebionetworks.repo.model.download.AddToDownloadListStatsRequest',\n request: {\n parentId: props.entityId,\n concreteType:\n 'org.sagebionetworks.repo.model.download.AddToDownloadListRequest',\n recursive: true,\n },\n },\n {\n enabled: isFolderOrProject,\n },\n )\n\n const folderOrProjectHasNoFiles =\n isFolderOrProject &&\n (isLoadingFolderOrProjectStats || folderOrProjectStats?.fileCount === 0)\n\n const { data: entityData } = useGetEntity(props.entityId)\n\n const { downloadFile } = useDirectDownloadHandler()\n\n const fileHandleId =\n entityData && isFileEntity(entityData)\n ? entityData.dataFileHandleId\n : undefined\n\n const onDownloadFile = fileHandleId\n ? () => {\n void downloadFile({\n fileHandleId,\n associatedObjectId: props.entityId,\n associatedObjectType: FileHandleAssociateType.FileEntity,\n })\n }\n : undefined\n\n const entityViewHasNoFiles =\n entityData && isEntityView(entityData) && !hasFilesInView(entityData)\n\n const datasetHasNoFiles =\n entityData &&\n isDataset(entityData) &&\n (!entityData.items || entityData.items.length === 0)\n\n const addToCartDisabled =\n folderOrProjectHasNoFiles || entityViewHasNoFiles || datasetHasNoFiles\n\n // state to manage export metadata modal visibility\n const [showExportMetadata, setShowExportMetadata] = useState<boolean>(false)\n\n const handleCloseExportMetadata = () => {\n setShowExportMetadata(false)\n }\n\n // Create download menu items\n const downloadActions = getDownloadActionsForEntityType(props.entityType)\n const downloadMenuItems = downloadActions.map(actionGroup =>\n actionGroup.map(action =>\n getMenuItemForAction(\n props.entityId,\n props.name,\n props.entityType,\n action,\n setShowProgrammaticAccess,\n setShowExportMetadata,\n addFileToDownloadList,\n setShowDownloadConfirmation,\n latestVersionNumber,\n addToCartDisabled,\n onDownloadFile,\n isAuthenticated,\n ),\n ),\n )\n\n // Return programmatic access modal content\n const { cliCode, rCode, pythonCode } = getProgrammaticAccessCode(\n props.entityType,\n props.entityId,\n latestVersionNumber,\n )\n\n const downloadConfirmation = showDownloadConfirmation ? (\n <EntityDownloadConfirmation\n entityId={props.entityId}\n handleClose={() => setShowDownloadConfirmation(false)}\n onIsLoadingChange={setDownloadConfirmationLoading}\n />\n ) : null\n\n return (\n <>\n <DropdownMenu\n items={downloadMenuItems}\n dropdownButtonText=\"Download\"\n buttonTooltip=\"Download options for this entity\"\n buttonProps={{\n variant: 'outlined',\n startIcon: <DownloadIcon />,\n disabled: props.disabled || downloadConfirmationLoading,\n }}\n />\n {downloadConfirmation &&\n (props.downloadConfirmationContainer?.current\n ? createPortal(\n downloadConfirmation,\n props.downloadConfirmationContainer.current,\n )\n : downloadConfirmation)}\n <ProgrammaticInstructionsModal\n show={showProgrammaticAccess}\n title={`Programmatic Access: ${props.name}`}\n onClose={handleCloseProgrammaticAccess}\n pythonCode={pythonCode}\n rCode={rCode}\n cliCode={cliCode}\n helpUrl=\"https://help.synapse.org/docs/Synapse-Docker-Registry.2011037752.html#SynapseDockerRegistry-UsingDockerImagesStoredintheSynapseDockerRegistry\"\n />\n {showExportMetadata && (\n <ModalDownload\n queryBundleRequest={defaultQueryBundleRequest}\n onClose={handleCloseExportMetadata}\n />\n )}\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -14,8 +14,8 @@ import { Box as ae, Chip as oe, IconButton as se, InputAdornment as f, Stack as
|
|
|
14
14
|
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
15
15
|
import { EntityType as _ } from "@sage-bionetworks/synapse-client";
|
|
16
16
|
import { useErrorHandler as ue } from "react-error-boundary";
|
|
17
|
-
import de from "@mui/icons-material/
|
|
18
|
-
import fe from "@mui/icons-material/
|
|
17
|
+
import de from "@mui/icons-material/Clear";
|
|
18
|
+
import fe from "@mui/icons-material/Search";
|
|
19
19
|
import { Map as pe } from "immutable";
|
|
20
20
|
import { ReflexContainer as me, ReflexElement as v, ReflexSplitter as he } from "react-reflex";
|
|
21
21
|
import "react-reflex/styles.css";
|
|
@@ -137,7 +137,7 @@ function x({ initialScope: _, projectId: x, initialContainer: S = null, selectMu
|
|
|
137
137
|
slotProps: { input: {
|
|
138
138
|
startAdornment: /* @__PURE__ */ h(f, {
|
|
139
139
|
position: "start",
|
|
140
|
-
children: /* @__PURE__ */ h(
|
|
140
|
+
children: /* @__PURE__ */ h(fe, {})
|
|
141
141
|
}),
|
|
142
142
|
endAdornment: R ? /* @__PURE__ */ h(f, {
|
|
143
143
|
position: "end",
|
|
@@ -148,7 +148,7 @@ function x({ initialScope: _, projectId: x, initialContainer: S = null, selectMu
|
|
|
148
148
|
},
|
|
149
149
|
"aria-label": "Clear Search",
|
|
150
150
|
disabled: !I && !P,
|
|
151
|
-
children: /* @__PURE__ */ h(
|
|
151
|
+
children: /* @__PURE__ */ h(de, {})
|
|
152
152
|
})
|
|
153
153
|
}) : void 0
|
|
154
154
|
} }
|
|
@@ -4,7 +4,7 @@ import { EntityFinderModal as t } from "./EntityFinderModal.js";
|
|
|
4
4
|
import { useState as n } from "react";
|
|
5
5
|
import { IconButton as r } from "@mui/material";
|
|
6
6
|
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
-
import
|
|
7
|
+
import s from "@mui/icons-material/SearchOutlined";
|
|
8
8
|
//#region src/components/EntityFinder/EntityIdTextField.tsx
|
|
9
9
|
function c(c) {
|
|
10
10
|
let { label: l, value: u, description: d, onChange: f, disabled: p, required: m, entityFinderModalProps: h } = c, [g, _] = n(!1);
|