2222import java .util .ArrayList ;
2323import java .util .concurrent .CompletableFuture ;
2424import java .util .concurrent .TimeUnit ;
25+ import java .util .function .Consumer ;
2526import life .qbic .application .commons .ApplicationException ;
2627import life .qbic .application .commons .FileNameFormatter ;
2728import life .qbic .datamanager .files .export .download .ByteArrayDownloadStreamProvider ;
3334import life .qbic .datamanager .views .general .download .DownloadComponent ;
3435import life .qbic .datamanager .views .notifications .CancelConfirmationDialogFactory ;
3536import life .qbic .datamanager .views .notifications .MessageSourceNotificationFactory ;
37+ import life .qbic .datamanager .views .notifications .Toast ;
3638import life .qbic .datamanager .views .projects .project .experiments .ExperimentMainLayout ;
3739import life .qbic .datamanager .views .projects .project .samples .BatchDetailsComponent .DeleteBatchEvent ;
3840import life .qbic .datamanager .views .projects .project .samples .BatchDetailsComponent .EditBatchEvent ;
5759import life .qbic .projectmanagement .domain .model .project .Project ;
5860import life .qbic .projectmanagement .domain .model .project .ProjectId ;
5961import life .qbic .projectmanagement .domain .model .sample .Sample ;
62+ import org .reactivestreams .Subscription ;
6063import org .springframework .beans .factory .annotation .Autowired ;
6164import org .springframework .util .MimeType ;
65+ import reactor .core .publisher .SignalType ;
6266
6367/**
6468 * Sample Information Main Component
@@ -86,9 +90,7 @@ public class SampleInformationMain extends Main implements BeforeEnterObserver {
8690 private final transient DeletionService deletionService ;
8791 private final transient SampleDetailsComponent sampleDetailsComponent ;
8892 private final BatchDetailsComponent batchDetailsComponent ;
89-
9093 private final DownloadComponent downloadComponent ;
91-
9294 private final Div content = new Div ();
9395 private final TextField searchField = new TextField ();
9496 private final Disclaimer noGroupsDefinedDisclaimer ;
@@ -99,7 +101,8 @@ public class SampleInformationMain extends Main implements BeforeEnterObserver {
99101 private final transient SampleValidationService sampleValidationService ;
100102 private final transient SampleRegistrationServiceV2 sampleRegistrationServiceV2 ;
101103 private final transient AsyncProjectService asyncProjectService ;
102- private final MessageSourceNotificationFactory messageSourceNotificationFactory ;
104+ private final MessageSourceNotificationFactory messageFactory ;
105+ private Toast pendingTask ;
103106 private transient Context context ;
104107
105108 public SampleInformationMain (@ Autowired ExperimentInformationService experimentInformationService ,
@@ -159,7 +162,7 @@ public SampleInformationMain(@Autowired ExperimentInformationService experimentI
159162 sampleDetailsComponent .getClass ().getSimpleName (),
160163 System .identityHashCode (sampleDetailsComponent )));
161164 add (downloadComponent );
162- this .messageSourceNotificationFactory = messageSourceNotificationFactory ;
165+ this .messageFactory = messageSourceNotificationFactory ;
163166 }
164167
165168 private static boolean noExperimentGroupsInExperiment (Experiment experiment ) {
@@ -202,13 +205,31 @@ private void downloadSampleMetadata() {
202205 .map (Experiment ::getName ).orElseThrow ();
203206
204207 asyncProjectService .sampleInformationTemplate (projectId .value (), experimentId .value (),
205- OPEN_XML ).doOnSuccess (resource ->
206- triggerDownload (resource ,
207- FileNameFormatter .formatWithTimestampedContext (LocalDate .now (), projectCode ,
208- experimentName ,
209- "sample information" ,
210- "xlsx" )
211- )).doOnError (this ::handleError ).subscribe ();
208+ OPEN_XML )
209+ .doOnSubscribe (showInProgress ("preparing sample information" ))
210+ .doOnSuccess (resource -> handleSuccess (projectCode , experimentName ).accept (resource ))
211+ .doOnError (this ::handleError )
212+ .doFinally (this ::closePendingTaskNotification )
213+ .subscribe ();
214+ }
215+
216+ private Consumer <DigitalObject > handleSuccess (String projectCode , String experimentName ) {
217+ return resource -> getUI ().ifPresent (ui -> ui .access (() -> {
218+ messageFactory .toast ("task.finished" , new Object []{"Preparation of sample information" }, getLocale ()).open ();
219+ triggerDownload (resource ,
220+ FileNameFormatter .formatWithTimestampedContext (LocalDate .now (), projectCode ,
221+ experimentName ,
222+ "sample information" ,
223+ "xlsx" ));
224+ }));
225+ }
226+
227+ private Consumer <? super Subscription > showInProgress (String preparingSampleInformation ) {
228+ return subscriber -> getUI ().ifPresent (ui -> ui .access (() -> {
229+ pendingTask = messageFactory .pendingTaskToast ("task.in-progress" ,
230+ new Object []{preparingSampleInformation }, getLocale ());
231+ pendingTask .open ();
232+ }));
212233 }
213234
214235 private void handleError (Throwable throwable ) {
@@ -226,7 +247,8 @@ private void handleUnexpectedError(Throwable throwable) {
226247 }
227248
228249 private void handleAccessDeniedError () {
229- getUI ().ifPresent (ui -> ui .access (() -> messageSourceNotificationFactory .toast ("access.denied.message" , new Object []{}, getLocale ()).open ()));
250+ getUI ().ifPresent (ui -> ui .access (
251+ () -> messageFactory .toast ("access.denied.message" , new Object []{}, getLocale ()).open ()));
230252 }
231253
232254 private void triggerDownload (DigitalObject resource , String filename ) {
@@ -261,7 +283,7 @@ private void onRegisterBatchClicked() {
261283 ProjectOverview projectOverview = projectInformationService .findOverview (projectId )
262284 .orElseThrow ();
263285 RegisterSampleBatchDialog registerSampleBatchDialog = new RegisterSampleBatchDialog (
264- sampleValidationService , asyncProjectService , messageSourceNotificationFactory , experimentId .value (),
286+ sampleValidationService , asyncProjectService , messageFactory , experimentId .value (),
265287 projectId .value (), projectOverview .projectCode ());
266288 UI ui = UI .getCurrent ();
267289 registerSampleBatchDialog .addConfirmListener (event -> {
@@ -393,7 +415,8 @@ private void onEditBatchClicked(EditBatchEvent editBatchEvent) {
393415 BatchId batchId = editBatchEvent .batchPreview ().batchId ();
394416 String batchLabel = editBatchEvent .batchPreview ().batchLabel ();
395417 var editSampleBatchDialog = new EditSampleBatchDialog (
396- sampleValidationService , asyncProjectService , messageSourceNotificationFactory , batchId , batchLabel , experimentId .value (),
418+ sampleValidationService , asyncProjectService , messageFactory , batchId , batchLabel ,
419+ experimentId .value (),
397420 projectId .value (), projectOverview .projectCode ());
398421 UI ui = UI .getCurrent ();
399422 editSampleBatchDialog .addConfirmListener (event -> {
@@ -472,6 +495,15 @@ private void onDeleteBatchClicked(DeleteBatchEvent deleteBatchEvent) {
472495 event -> batchDeletionConfirmationNotification .close ());
473496 }
474497
498+ private void closePendingTaskNotification (SignalType signalType ) {
499+ getUI ().ifPresent (ui -> ui .access (() -> {
500+ if (this .pendingTask == null ) {
501+ return ;
502+ }
503+ this .pendingTask .close ();
504+ }));
505+ }
506+
475507 /**
476508 * Callback executed before navigation to attaching Component chain is made.
477509 *
0 commit comments