diff --git a/test/unitylibs/scripts/transition-screen.test.js b/test/unitylibs/scripts/transition-screen.test.js
index f272c7c21..dffe01723 100644
--- a/test/unitylibs/scripts/transition-screen.test.js
+++ b/test/unitylibs/scripts/transition-screen.test.js
@@ -45,15 +45,17 @@ describe('TransitionScreen', () => {
});
describe('progressBarHandler', () => {
- it('should call updateProgressBar and recurse', () => {
+ beforeEach(() => {
splashScreenEl.innerHTML = `
0%
`;
+ });
+
+ it('should call updateProgressBar and recurse', () => {
const spy = sinon.spy(screen, 'updateProgressBar');
- // Use fake timers to control setTimeout
const clock = sinon.useFakeTimers();
screen.progressBarHandler(splashScreenEl, 10, 10, true);
clock.tick(20);
@@ -61,6 +63,48 @@ describe('TransitionScreen', () => {
spy.restore();
clock.restore();
});
+
+ it('should ignore stale callbacks after clearProgressBarHandler', () => {
+ const clock = sinon.useFakeTimers();
+ screen.progressBarHandler(splashScreenEl, 10, 10, true);
+ clock.tick(15);
+ screen.clearProgressBarHandler();
+ const valueBeforeStaleTick = splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value');
+ clock.tick(100);
+ expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal(valueBeforeStaleTick);
+ clock.restore();
+ });
+
+ it('should restart cleanly when initialized after cancel', () => {
+ const clock = sinon.useFakeTimers();
+ screen.progressBarHandler(splashScreenEl, 10, 10, true);
+ clock.tick(15);
+ screen.clearProgressBarHandler();
+ screen.progressBarHandler(splashScreenEl, 10, 10, true);
+ expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('0');
+ clock.tick(15);
+ expect(parseInt(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value'), 10)).to.be.greaterThan(0);
+ clock.restore();
+ });
+ });
+
+ describe('clearProgressBarHandler', () => {
+ it('should stop pending progress updates when updateProgressBar reaches 100', () => {
+ splashScreenEl.innerHTML = `
+
+ 0%
+
+
+ `;
+ const clock = sinon.useFakeTimers();
+ screen.progressBarHandler(splashScreenEl, 10, 10, true);
+ clock.tick(15);
+ screen.updateProgressBar(splashScreenEl, 100);
+ expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('100');
+ clock.tick(100);
+ expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('100');
+ clock.restore();
+ });
});
describe('handleSplashProgressBar', () => {
@@ -97,10 +141,23 @@ describe('TransitionScreen', () => {
document.body.innerHTML = '';
});
it('should hide splash and reset LOADER_LIMIT when displayOn is false', () => {
+ const clock = sinon.useFakeTimers();
+ splashScreenEl.innerHTML = `
+
+ 0%
+
+
+ `;
+ screen.progressBarHandler(splashScreenEl, 10, 10, true);
+ clock.tick(15);
screen.splashVisibilityController(false);
+ const valueAtCancel = splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value');
+ clock.tick(100);
expect(screen.LOADER_LIMIT).to.equal(95);
expect(splashScreenEl.classList.contains('show')).to.be.false;
expect(splashScreenEl.parentElement.classList.contains('hide-splash-overflow')).to.be.false;
+ expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal(valueAtCancel);
+ clock.restore();
});
it('should show splash and set aria-hidden when displayOn is true', () => {
const stub = sinon.stub(screen, 'progressBarHandler');
diff --git a/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css b/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css
index 59a6db0a1..fd4926916 100644
--- a/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css
+++ b/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css
@@ -78,8 +78,8 @@ display: flex;
flex-direction: column;
align-items: flex-start;
gap: 17px;
-border-right: 1px solid rgb(255 255 255 / 12%);
-padding-right: 16px;
+border-inline-end: 1px solid rgb(255 255 255 / 12%);
+padding-inline-end: 16px;
box-sizing: border-box;
}
@@ -134,6 +134,31 @@ width: min(339px, 100%);
max-width: 100%;
}
+.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content {
+min-width: 0;
+}
+
+.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-icon {
+flex: 1 1 0%;
+justify-content: flex-start;
+min-width: 0;
+width: auto;
+}
+
+.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-text {
+flex: 1 1 auto;
+max-width: 100%;
+min-width: 0;
+width: 100%;
+padding-inline: 0;
+overflow-wrap: break-word;
+text-align: center;
+}
+
+.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-text p {
+padding-top: 0;
+}
+
.unity-prompt-bar-upload.unity-enabled .pbu-right-section .unity-slf-prompt-label {
margin-bottom: 0px;
margin-inline: 0;
@@ -911,6 +936,11 @@ border: 2px solid;
background: rgb(64 105 253 / 14%);
}
+.pbu-drop-zone-wrap .drop-zone:focus-visible {
+outline: none;
+border: 2px solid #4069FD;
+}
+
.pbu-drop-zone-wrap .drop-zone.hidden {
display: none;
}
@@ -1138,11 +1168,11 @@ line-height: normal;
.unity-prompt-bar-upload.unity-enabled .pbu-left-section {
width: 100%;
max-width: 100%;
- border-right: none;
- padding-right: 0;
- padding-bottom: 16px;
+ border-inline-end: none;
+ padding-inline-end: 0;
+ padding-block-end: 16px;
margin-bottom: 0;
- border-bottom: 1px solid rgb(255 255 255 / 12%);
+ border-block-end: 1px solid rgb(255 255 255 / 12%);
}
.unity-prompt-bar-upload.unity-enabled .pbu-drop-zone-wrap {
diff --git a/unitylibs/scripts/transition-screen.js b/unitylibs/scripts/transition-screen.js
index f49cb7311..420ed8aba 100644
--- a/unitylibs/scripts/transition-screen.js
+++ b/unitylibs/scripts/transition-screen.js
@@ -20,6 +20,16 @@ export default class TransitionScreen {
this.isDesktop = isDesktop;
this.headingElements = [];
this.progressText = '';
+ this.progressBarTimeoutId = null;
+ this.progressBarGeneration = 0;
+ }
+
+ clearProgressBarHandler() {
+ if (this.progressBarTimeoutId != null) {
+ clearTimeout(this.progressBarTimeoutId);
+ this.progressBarTimeoutId = null;
+ }
+ this.progressBarGeneration += 1;
}
setProgressTextFromDOM() {
@@ -44,6 +54,7 @@ export default class TransitionScreen {
? this.progressText.replace('%', `${p}%`)
: `${p}%`;
if (status && status.textContent !== newStatus) status.textContent = newStatus;
+ if (percentage >= 100) this.clearProgressBarHandler();
}
static createProgressBar() {
@@ -63,13 +74,18 @@ export default class TransitionScreen {
const newDelay = Math.min(delay + 100, 2000);
const newI = Math.max(i - 5, 5);
const progressBar = s.querySelector('.spectrum-ProgressBar');
- if (initialize) this.updateProgressBar(s, 0);
- else {
+ if (initialize) {
+ this.clearProgressBarHandler();
+ this.updateProgressBar(s, 0);
+ } else {
const currentValue = parseInt(progressBar?.getAttribute('value'), 10);
if (currentValue === 100 || currentValue >= this.LOADER_LIMIT) return;
}
- setTimeout(() => {
+ const generation = this.progressBarGeneration;
+ this.progressBarTimeoutId = setTimeout(() => {
+ this.progressBarTimeoutId = null;
+ if (generation !== this.progressBarGeneration) return;
const v = initialize ? 0 : parseInt(progressBar.getAttribute('value'), 10);
if (v === 100) return;
this.updateProgressBar(s, v + newI);
@@ -213,6 +229,7 @@ export default class TransitionScreen {
splashVisibilityController(displayOn) {
if (!displayOn) {
+ this.clearProgressBarHandler();
this.LOADER_LIMIT = 95;
this.splashScreenEl.parentElement?.classList.remove('hide-splash-overflow');
this.splashScreenEl.classList.remove('show');