diff --git a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java index b512650a..f77af854 100644 --- a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java +++ b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java @@ -250,6 +250,10 @@ public enum I18n { ASSETS_RELOCATE_WIZARD_ERROR_TARGET_ALREADY_ASSET("assets.relocateWizard.error.targetAlreadyAsset"), ASSETS_RELOCATE_WIZARD_ERROR_TARGET_SAME("assets.relocateWizard.error.targetSame"), ASSETS_PACK_WIZARD_TITLE("assets.packWizard.title"), + ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_TITLE("assets.packWizard.step.deepSync.title"), + ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_DESCRIPTION("assets.packWizard.step.deepSync.description"), + ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_WAITING_TITLE("assets.packWizard.step.deepSyncWaiting.title"), + ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_WAITING_DESCRIPTION("assets.packWizard.step.deepSyncWaiting.description"), ASSETS_PACK_WIZARD_STEP_SUMMARY_TITLE("assets.packWizard.step.summary.title"), ASSETS_PACK_WIZARD_STEP_SUMMARY_DESCRIPTION("assets.packWizard.step.summary.description"), ASSETS_PACK_WIZARD_STEP_VALIDATION_TITLE("assets.packWizard.step.validation.title"), @@ -263,6 +267,7 @@ public enum I18n { ASSETS_PACK_WIZARD_BUTTON_CLOSE("assets.packWizard.button.close"), ASSETS_PACK_WIZARD_BUTTON_COPY_FAILURES("assets.packWizard.button.copyFailures"), ASSETS_PACK_WIZARD_LOADING_SUMMARY("assets.packWizard.loading.summary"), + ASSETS_PACK_WIZARD_LOADING_DEEP_SYNC("assets.packWizard.loading.deepSync"), ASSETS_PACK_WIZARD_LOADING_VALIDATION("assets.packWizard.loading.validation"), ASSETS_PACK_WIZARD_LOADING_PACKING("assets.packWizard.loading.packing"), ASSETS_PACK_WIZARD_LABEL_CANONICAL_ARTIFACT("assets.packWizard.label.canonicalArtifact"), @@ -285,7 +290,10 @@ public enum I18n { ASSETS_PACK_WIZARD_VALIDATION_EMPTY("assets.packWizard.validation.empty"), ASSETS_PACK_WIZARD_RESULT_NO_ARTIFACTS("assets.packWizard.result.noArtifacts"), ASSETS_PACK_WIZARD_RESULT_COPY_FAILURES_HINT("assets.packWizard.result.copyFailuresHint"), + ASSETS_PACK_WIZARD_DEEP_SYNC_NOTE("assets.packWizard.deepSync.note"), + ASSETS_PACK_WIZARD_DEEP_SYNC_COMPLETED("assets.packWizard.deepSync.completed"), ASSETS_PACK_WIZARD_ERROR_SUMMARY("assets.packWizard.error.summary"), + ASSETS_PACK_WIZARD_ERROR_DEEP_SYNC("assets.packWizard.error.deepSync"), ASSETS_PACK_WIZARD_ERROR_VALIDATION("assets.packWizard.error.validation"), ASSETS_PACK_WIZARD_ERROR_PACK("assets.packWizard.error.pack"), WORKSPACE_DEBUG("workspace.debug"), diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/PackWorkspaceWizard.java b/prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/PackWorkspaceWizard.java index 777f085a..ea3cce51 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/PackWorkspaceWizard.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/PackWorkspaceWizard.java @@ -15,6 +15,8 @@ import javafx.stage.Window; import p.packer.dtos.PackerEmittedArtifactDTO; import p.packer.dtos.PackerPackSummaryAssetDTO; import p.packer.dtos.PackerPackValidationAssetDTO; +import p.packer.messages.ListAssetsRequest; +import p.packer.messages.ListAssetsResult; import p.packer.messages.PackWorkspaceRequest; import p.packer.messages.PackWorkspaceResult; import p.packer.messages.PackWorkspaceSummaryRequest; @@ -32,6 +34,8 @@ import java.util.Objects; public final class PackWorkspaceWizard { private enum Phase { + DEEP_SYNC_INTRO, + DEEP_SYNC_WAITING, SUMMARY, VALIDATION, PACKING, @@ -50,7 +54,9 @@ public final class PackWorkspaceWizard { private final Button closeButton = new Button(); private final Button copyFailuresButton = new Button(); - private Phase phase = Phase.SUMMARY; + private Phase phase = Phase.DEEP_SYNC_INTRO; + private boolean deepSyncRunning; + private boolean deepSyncCompleted; private boolean loadingSummary; private boolean loadingValidation; private boolean packing; @@ -68,12 +74,11 @@ public final class PackWorkspaceWizard { stage.setScene(new Scene(buildRoot(), 760, 560)); stage.getScene().getStylesheets().add(Container.theme().getDefaultTheme()); stage.setOnCloseRequest(event -> { - if (packing) { + if (packing || deepSyncRunning) { event.consume(); } }); render(); - loadSummary(); } public static void showAndWait(Window owner, ProjectReference projectReference) { @@ -90,9 +95,9 @@ public final class PackWorkspaceWizard { backButton.getStyleClass().addAll("studio-button", "studio-button-secondary"); backButton.setOnAction(ignored -> goBack()); - nextButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_BUTTON_VALIDATE)); + nextButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_NEXT)); nextButton.getStyleClass().addAll("studio-button", "studio-button-primary"); - nextButton.setOnAction(ignored -> loadValidation()); + nextButton.setOnAction(ignored -> advancePrimaryFlow()); packButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_BUTTON_PACK)); packButton.getStyleClass().addAll("studio-button", "studio-button-primary"); @@ -120,12 +125,19 @@ public final class PackWorkspaceWizard { } private void render() { - final boolean busy = loadingSummary || loadingValidation || packing; - backButton.setDisable(busy || phase == Phase.SUMMARY); + final boolean busy = deepSyncRunning || loadingSummary || loadingValidation || packing; + backButton.setDisable(busy || phase == Phase.DEEP_SYNC_INTRO || phase == Phase.DEEP_SYNC_WAITING); - nextButton.setVisible(phase == Phase.SUMMARY); - nextButton.setManaged(phase == Phase.SUMMARY); - nextButton.setDisable(busy || summaryResult == null); + nextButton.setVisible(phase == Phase.DEEP_SYNC_INTRO + || phase == Phase.DEEP_SYNC_WAITING + || phase == Phase.SUMMARY); + nextButton.setManaged(nextButton.isVisible()); + nextButton.setDisable(switch (phase) { + case DEEP_SYNC_INTRO -> busy; + case DEEP_SYNC_WAITING -> deepSyncRunning; + case SUMMARY -> busy || summaryResult == null; + default -> true; + }); packButton.setVisible(phase == Phase.VALIDATION); packButton.setManaged(phase == Phase.VALIDATION); @@ -135,6 +147,8 @@ public final class PackWorkspaceWizard { copyFailuresButton.setManaged(phase == Phase.RESULT); switch (phase) { + case DEEP_SYNC_INTRO -> renderDeepSyncIntroPhase(); + case DEEP_SYNC_WAITING -> renderDeepSyncWaitingPhase(); case SUMMARY -> renderSummaryPhase(); case VALIDATION -> renderValidationPhase(); case PACKING -> renderPackingPhase(); @@ -142,6 +156,30 @@ public final class PackWorkspaceWizard { } } + private void renderDeepSyncIntroPhase() { + stepTitle.textProperty().unbind(); + stepDescription.textProperty().unbind(); + stepTitle.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_TITLE)); + stepDescription.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_DESCRIPTION)); + stepBody.getChildren().setAll( + AssetDetailsUiSupport.createSectionMessage(Container.i18n().text(I18n.ASSETS_PACK_WIZARD_DEEP_SYNC_NOTE))); + } + + private void renderDeepSyncWaitingPhase() { + stepTitle.textProperty().unbind(); + stepDescription.textProperty().unbind(); + stepTitle.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_WAITING_TITLE)); + stepDescription.textProperty().bind(Container.i18n().bind(I18n.ASSETS_PACK_WIZARD_STEP_DEEP_SYNC_WAITING_DESCRIPTION)); + if (deepSyncRunning) { + stepBody.getChildren().setAll(createBusyBox(I18n.ASSETS_PACK_WIZARD_LOADING_DEEP_SYNC)); + return; + } + final String message = deepSyncCompleted + ? Container.i18n().text(I18n.ASSETS_PACK_WIZARD_DEEP_SYNC_COMPLETED) + : Container.i18n().text(I18n.ASSETS_PACK_WIZARD_ERROR_DEEP_SYNC); + stepBody.getChildren().setAll(AssetDetailsUiSupport.createSectionMessage(message)); + } + private void renderSummaryPhase() { stepTitle.textProperty().unbind(); stepDescription.textProperty().unbind(); @@ -353,6 +391,65 @@ public final class PackWorkspaceWizard { } } + private void advancePrimaryFlow() { + switch (phase) { + case DEEP_SYNC_INTRO -> startDeepSync(); + case DEEP_SYNC_WAITING -> { + if (deepSyncCompleted) { + phase = Phase.SUMMARY; + feedbackLabel.setText(""); + if (summaryResult == null) { + loadSummary(); + } else { + render(); + } + } else if (!deepSyncRunning) { + startDeepSync(); + } + } + case SUMMARY -> loadValidation(); + default -> { + } + } + } + + private void startDeepSync() { + if (deepSyncRunning) { + return; + } + phase = Phase.DEEP_SYNC_WAITING; + deepSyncRunning = true; + deepSyncCompleted = false; + summaryResult = null; + validationResult = null; + packResult = null; + feedbackLabel.setText(""); + render(); + Container.backgroundTasks().submit(() -> { + try { + final ListAssetsResult result = Container.packer().workspaceService().listAssets( + new ListAssetsRequest(projectReference.toPackerProjectContext(), true)); + Platform.runLater(() -> applyDeepSyncResult(result)); + } catch (RuntimeException exception) { + Platform.runLater(() -> applyDeepSyncFailure(exception)); + } + }); + } + + private void applyDeepSyncResult(ListAssetsResult result) { + deepSyncRunning = false; + deepSyncCompleted = result.status() != PackerOperationStatus.FAILED; + feedbackLabel.setText(deepSyncCompleted ? "" : result.summary()); + render(); + } + + private void applyDeepSyncFailure(RuntimeException exception) { + deepSyncRunning = false; + deepSyncCompleted = false; + feedbackLabel.setText(extractMessage(exception, I18n.ASSETS_PACK_WIZARD_ERROR_DEEP_SYNC)); + render(); + } + private void loadSummary() { loadingSummary = true; feedbackLabel.setText(""); diff --git a/prometeu-studio/src/main/resources/i18n/messages.properties b/prometeu-studio/src/main/resources/i18n/messages.properties index 6e73fa22..026b09e2 100644 --- a/prometeu-studio/src/main/resources/i18n/messages.properties +++ b/prometeu-studio/src/main/resources/i18n/messages.properties @@ -241,6 +241,10 @@ assets.relocateWizard.error.outsideAssets=The planned target root must stay insi assets.relocateWizard.error.targetAlreadyAsset=The planned target root already contains asset.json. assets.relocateWizard.error.targetSame=The planned target root must differ from the current asset root. assets.packWizard.title=Pack Workspace +assets.packWizard.step.deepSync.title=Prepare Deep Sync +assets.packWizard.step.deepSync.description=Start with a deep sync so the pack wizard reads the latest packer snapshot before validation or packing. +assets.packWizard.step.deepSyncWaiting.title=Deep Sync +assets.packWizard.step.deepSyncWaiting.description=Wait while the packer refreshes the workspace snapshot for this wizard. assets.packWizard.step.summary.title=Pack Summary assets.packWizard.step.summary.description=Review the current build set before validating or packing it. assets.packWizard.step.validation.title=Validation @@ -254,6 +258,7 @@ assets.packWizard.button.pack=Pack assets.packWizard.button.close=Close assets.packWizard.button.copyFailures=Copy Failure Summary assets.packWizard.loading.summary=Loading pack summary... +assets.packWizard.loading.deepSync=Running deep sync... assets.packWizard.loading.validation=Running pack validation... assets.packWizard.loading.packing=Packing assets... assets.packWizard.label.canonicalArtifact=Canonical Artifact @@ -276,7 +281,10 @@ assets.packWizard.status.finished=Packing finished assets.packWizard.validation.empty=No validation entries are currently available. assets.packWizard.result.noArtifacts=No emitted artifacts were reported. assets.packWizard.result.copyFailuresHint=Failure summary export is reserved for a future iteration. +assets.packWizard.deepSync.note=Next starts a deep sync before the wizard shows the pack summary. +assets.packWizard.deepSync.completed=Deep sync finished. Continue to review the current pack summary. assets.packWizard.error.summary=Unable to load pack summary. +assets.packWizard.error.deepSync=Unable to complete deep sync for the current workspace. assets.packWizard.error.validation=Unable to validate the current pack set. assets.packWizard.error.pack=Unable to pack the current build set. workspace.debug=Debug