fix: allow mojang skins to be draggable (#6365)

* fix: allow mojang skins to be draggable

* pnpm prepr
This commit is contained in:
Truman Gao
2026-06-11 13:02:28 -06:00
committed by GitHub
parent 36423eb5b5
commit 1cabfe3e85
2 changed files with 64 additions and 11 deletions
@@ -159,9 +159,7 @@ const sections = computed<SkinSection[]>(() => [
const draggableSavedSkins = ref<Skin[]>([])
const isDraggingSavedSkin = ref(false)
const canReorderSavedSkins = computed(() => draggableSavedSkins.value.length > 1)
const fixedSavedSkins = computed(() =>
props.savedSkins.filter((skin) => !canPersistSkinOrder(skin)),
)
const fixedSavedSkins = computed(() => props.savedSkins.filter((skin) => !canDragSavedSkin(skin)))
const sectionLayouts = computed(() => {
const layouts: Array<{ section: SkinSection; top: number; height: number; index: number }> = []
@@ -226,7 +224,7 @@ watch(
return
}
draggableSavedSkins.value = nextSkins.filter(canPersistSkinOrder)
draggableSavedSkins.value = nextSkins.filter(canDragSavedSkin)
},
{ immediate: true },
)
@@ -283,17 +281,17 @@ function savedSkinKey(skin: Skin) {
return skinKey(skin, 'saved-skin')
}
function canPersistSkinOrder(skin: Skin) {
return skin.source === 'custom'
function canDragSavedSkin(skin: Skin) {
return skin.source === 'custom' || skin.source === 'custom_external'
}
function doSkinOrdersMatch(firstSkins: Skin[], secondSkins: Skin[]) {
const persistedSecondSkins = secondSkins.filter(canPersistSkinOrder)
const draggableSecondSkins = secondSkins.filter(canDragSavedSkin)
return (
firstSkins.length === persistedSecondSkins.length &&
firstSkins.length === draggableSecondSkins.length &&
firstSkins.every(
(skin, index) => savedSkinKey(skin) === savedSkinKey(persistedSecondSkins[index]),
(skin, index) => savedSkinKey(skin) === savedSkinKey(draggableSecondSkins[index]),
)
)
}
@@ -306,7 +304,7 @@ function onSavedSkinDragEnd() {
isDraggingSavedSkin.value = false
if (doSkinOrdersMatch(draggableSavedSkins.value, props.savedSkins)) {
draggableSavedSkins.value = props.savedSkins.filter(canPersistSkinOrder)
draggableSavedSkins.value = props.savedSkins.filter(canDragSavedSkin)
return
}
+56 -1
View File
@@ -46,6 +46,7 @@ import {
get_normalized_skin_texture,
normalize_skin_texture,
remove_custom_skin,
save_custom_skin,
set_custom_skin_order,
} from '@/helpers/skins.ts'
import { hasPride26Badge } from '@/helpers/user-campaigns.ts'
@@ -399,6 +400,14 @@ function skinsMatch(a?: Skin | null, b?: Skin | null) {
)
}
function skinsMatchIgnoringSource(a?: Skin | null, b?: Skin | null) {
return (
a?.texture_key === b?.texture_key &&
a?.variant === b?.variant &&
(a?.cape_id ?? null) === (b?.cape_id ?? null)
)
}
function isSkinSelected(skin: Skin) {
return skinsMatch(selectedSkin.value, skin)
}
@@ -572,6 +581,8 @@ function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin)
async function reorderSavedSkins(orderedSkins: Skin[]) {
const previousSkins = skins.value
const previousSelectedSkin = selectedSkin.value
const previousOriginalSelectedSkin = originalSelectedSkin.value
const orderedTextureKeys = orderedSkins.map((skin) => skin.texture_key)
const orderedTextureKeySet = new Set(orderedTextureKeys)
const remainingSavedSkins = previousSkins.filter(
@@ -584,11 +595,22 @@ async function reorderSavedSkins(orderedSkins: Skin[]) {
generateSkinPreviews(skins.value, capes.value)
try {
const persistedSavedSkins = await preserveExternalSkins(nextSavedSkins)
if (persistedSavedSkins.some((skin, index) => skin !== nextSavedSkins[index])) {
skins.value = [...persistedSavedSkins, ...defaultSkins]
generateSkinPreviews(skins.value, capes.value)
}
await set_custom_skin_order(
nextSavedSkins.filter((skin) => skin.source === 'custom').map((skin) => skin.texture_key),
persistedSavedSkins
.filter((skin) => skin.source === 'custom')
.map((skin) => skin.texture_key),
)
} catch (error) {
skins.value = previousSkins
selectedSkin.value = previousSelectedSkin
originalSelectedSkin.value = previousOriginalSelectedSkin
generateSkinPreviews(skins.value, capes.value)
addNotification({
type: 'error',
@@ -599,6 +621,39 @@ async function reorderSavedSkins(orderedSkins: Skin[]) {
}
}
async function preserveExternalSkins(skinsToPersist: Skin[]) {
const preservedSkins: Skin[] = []
for (const skin of skinsToPersist) {
if (skin.source !== 'custom_external') {
preservedSkins.push(skin)
continue
}
const textureBlob = await normalize_skin_texture(skin.texture)
const capeId = skin.cape_id ? capes.value.find((cape) => cape.id === skin.cape_id) : undefined
const savedSkin = await save_custom_skin(skin, textureBlob, skin.variant, capeId, false)
const preservedSkin: Skin = {
...savedSkin,
source: 'custom',
is_equipped: skin.is_equipped,
}
if (skinsMatchIgnoringSource(selectedSkin.value, skin)) {
selectedSkin.value = preservedSkin
}
if (skinsMatchIgnoringSource(originalSelectedSkin.value, skin)) {
originalSelectedSkin.value = preservedSkin
void accountsCard.value?.setEquippedSkin(preservedSkin)
}
preservedSkins.push(preservedSkin)
}
return preservedSkins
}
function schedulePendingSkinRefresh() {
if (pendingSkinRefreshTimeout !== null) {
window.clearTimeout(pendingSkinRefreshTimeout)