feat: complete hosted mod sync and launcher interface updates
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Add pack sync markers, tagged mod updates, parallel progress, JWT downloads and retry recovery. Include pending onboarding, about scene, compatibility data pack and download fixes.
This commit is contained in:
@ -143,6 +143,7 @@ import { useScrollIndicator } from '../../composables/scroll-indicator'
|
||||
import { injectModalBehavior } from '../../providers'
|
||||
import { commonMessages } from '../../utils/common-messages'
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import { createModalTransition } from './modal-transition'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@ -220,6 +221,7 @@ const headerId = `${modalId}-header`
|
||||
const closeLabel = computed(() => formatMessage(commonMessages.closeButton))
|
||||
|
||||
const open = ref(false)
|
||||
const transition = createModalTransition()
|
||||
const visible = ref(false)
|
||||
const stackDepth = ref(0)
|
||||
const modalBodyRef = ref<HTMLElement | null>(null)
|
||||
@ -237,6 +239,8 @@ function getFocusableElements(): HTMLElement[] {
|
||||
}
|
||||
|
||||
function show(event?: MouseEvent) {
|
||||
const current = transition.begin(true)
|
||||
if (!current) return
|
||||
props.onShow?.()
|
||||
const wasEmpty = modalStackSize() === 0
|
||||
stackDepth.value = modalStackSize()
|
||||
@ -255,8 +259,10 @@ function show(event?: MouseEvent) {
|
||||
mouseY.value = Math.round(window.innerHeight / 2)
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (!current()) return
|
||||
visible.value = true
|
||||
nextTick(() => {
|
||||
if (!current()) return
|
||||
const focusable = getFocusableElements()
|
||||
if (focusable.length > 0) {
|
||||
focusable[0].focus()
|
||||
@ -271,6 +277,8 @@ async function hide() {
|
||||
if (props.disableClose) {
|
||||
return
|
||||
}
|
||||
const current = transition.begin(false)
|
||||
if (!current) return
|
||||
props.onHide?.()
|
||||
resetMousePosition()
|
||||
visible.value = false
|
||||
@ -289,9 +297,10 @@ async function hide() {
|
||||
await new Promise<void>((resolve) => {
|
||||
setTimeout(resolve, 300)
|
||||
})
|
||||
if (!current()) return
|
||||
open.value = false
|
||||
await nextTick()
|
||||
props.onAfterHide?.()
|
||||
if (current()) props.onAfterHide?.()
|
||||
}
|
||||
|
||||
async function scrollToBottom(behavior: ScrollBehavior = 'smooth') {
|
||||
@ -336,7 +345,9 @@ function resetMousePosition() {
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (open.value) {
|
||||
const wasActive = transition.isActive()
|
||||
transition.dispose()
|
||||
if (wasActive) {
|
||||
popModal()
|
||||
window.removeEventListener('keydown', handleWindowKeyDown)
|
||||
window.removeEventListener('mousedown', updateMousePosition)
|
||||
|
||||
28
packages/ui/src/components/modal/modal-transition.test.ts
Normal file
28
packages/ui/src/components/modal/modal-transition.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { createModalTransition } from './modal-transition.ts'
|
||||
|
||||
test('fast reopen invalidates the previous closing animation', () => {
|
||||
const transition = createModalTransition()
|
||||
assert.equal(transition.begin(false), undefined)
|
||||
const opening = transition.begin(true)!
|
||||
assert.equal(transition.begin(true), undefined)
|
||||
const closing = transition.begin(false)!
|
||||
assert.equal(opening(), false)
|
||||
assert.equal(transition.begin(false), undefined)
|
||||
const reopened = transition.begin(true)!
|
||||
assert.equal(closing(), false)
|
||||
assert.equal(reopened(), true)
|
||||
assert.equal(transition.isActive(), true)
|
||||
})
|
||||
|
||||
test('closing before the opening animation and unmount invalidate stale callbacks', () => {
|
||||
const transition = createModalTransition()
|
||||
const opening = transition.begin(true)!
|
||||
const closing = transition.begin(false)!
|
||||
assert.equal(opening(), false)
|
||||
assert.equal(closing(), true)
|
||||
assert.equal(transition.isActive(), false)
|
||||
transition.dispose()
|
||||
assert.equal(closing(), false)
|
||||
})
|
||||
18
packages/ui/src/components/modal/modal-transition.ts
Normal file
18
packages/ui/src/components/modal/modal-transition.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export function createModalTransition() {
|
||||
let active = false
|
||||
let revision = 0
|
||||
function begin(next: boolean): (() => boolean) | undefined {
|
||||
if (active === next) return undefined
|
||||
active = next
|
||||
const current = ++revision
|
||||
return () => current === revision
|
||||
}
|
||||
return {
|
||||
begin,
|
||||
isActive: () => active,
|
||||
dispose: () => {
|
||||
active = false
|
||||
revision++
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user