Skip to content

Commit 6d1c136

Browse files
committed
chore(账号): 变更皮肤时,刷新整个衣橱 & 调整 UI
1 parent e48c815 commit 6d1c136

2 files changed

Lines changed: 50 additions & 53 deletions

File tree

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/AccountManageScreen.kt

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ import androidx.compose.foundation.layout.offset
3333
import androidx.compose.foundation.layout.padding
3434
import androidx.compose.foundation.lazy.LazyColumn
3535
import androidx.compose.foundation.lazy.items
36+
import androidx.compose.material3.CircularProgressIndicator
3637
import androidx.compose.material3.MaterialTheme
3738
import androidx.compose.material3.Surface
3839
import androidx.compose.material3.Text
3940
import androidx.compose.runtime.Composable
4041
import androidx.compose.runtime.CompositionLocalProvider
4142
import androidx.compose.runtime.LaunchedEffect
4243
import androidx.compose.runtime.getValue
43-
import androidx.compose.runtime.mutableStateMapOf
4444
import androidx.compose.runtime.mutableStateOf
4545
import androidx.compose.runtime.remember
4646
import androidx.compose.runtime.saveable.rememberSaveable
@@ -113,7 +113,6 @@ import com.movtery.zalithlauncher.viewmodel.ScreenBackStackViewModel
113113
* @property checkIfInWebScreen 检查当前是否在浏览器界面中(用于微软登录逻辑判断)
114114
* @property formatError 格式化异常为本地化字符串
115115
* @property submitError 提交错误到全局错误展示系统
116-
* @property refreshAvatarMap 维护各个账号头像是否需要刷新的状态映射
117116
*/
118117
private data class AccountActions(
119118
val onIntent: (AccountManageIntent) -> Unit,
@@ -123,7 +122,6 @@ private data class AccountActions(
123122
val checkIfInWebScreen: () -> Boolean,
124123
val formatError: (Context, Throwable) -> String,
125124
val submitError: (ErrorViewModel.ThrowableMessage) -> Unit,
126-
val refreshAvatarMap: MutableMap<String, Boolean>
127125
)
128126

129127
/**
@@ -148,9 +146,6 @@ fun AccountManageScreen(
148146
val profileUiState by viewModel.profileUiState.collectAsStateWithLifecycle()
149147
val operationUiState by viewModel.operationUiState.collectAsStateWithLifecycle()
150148

151-
// 头像刷新机制:ViewModel 发送 Effect,UI 层更新 refreshAvatarMap 触发局部重绘
152-
val refreshAvatarMap = remember { mutableStateMapOf<String, Boolean>() }
153-
154149
LaunchedEffect(Unit) {
155150
viewModel.effect.collect { effect ->
156151
when (effect) {
@@ -166,11 +161,6 @@ fun AccountManageScreen(
166161
}
167162
Toast.makeText(context, message, effect.duration).show()
168163
}
169-
170-
is AccountManageEffect.RefreshAvatar -> {
171-
val current = refreshAvatarMap[effect.accountUuid] ?: false
172-
refreshAvatarMap[effect.accountUuid] = !current
173-
}
174164
}
175165
}
176166
}
@@ -180,8 +170,7 @@ fun AccountManageScreen(
180170
backToMainScreen,
181171
openLink,
182172
backStackViewModel,
183-
submitError,
184-
refreshAvatarMap
173+
submitError
185174
) {
186175
AccountActions(
187176
onIntent = viewModel::onIntent,
@@ -191,7 +180,6 @@ fun AccountManageScreen(
191180
checkIfInWebScreen = { backStackViewModel.mainScreen.currentKey is NormalNavKey.WebScreen },
192181
formatError = { _, th -> viewModel.formatAccountError(th) },
193182
submitError = submitError,
194-
refreshAvatarMap = refreshAvatarMap
195183
)
196184
}
197185

@@ -289,35 +277,42 @@ private fun ActionsLayout(
289277
PlayerSkin(context)
290278
}
291279
var pageFinished by remember { mutableStateOf(false) }
292-
AndroidView(
280+
Box(
293281
modifier = Modifier
294282
.weight(1f)
295283
.fillMaxWidth(),
296-
factory = { context ->
297-
playerSkin.loadWebView(
298-
context = context,
299-
onPageFinished = {
300-
pageFinished = true
301-
playerSkin.startAnim(ModelAnimation.NewIdle)
302-
}
303-
)
304-
},
305-
update = {
306-
val account = currentAccount
307-
if (account != null && pageFinished) {
308-
runCatching {
309-
accountSkin?.inputStream().use { inputStream ->
310-
playerSkin.loadSkin(inputStream, account.skinModelType)
284+
contentAlignment = Alignment.Center
285+
) {
286+
AndroidView(
287+
modifier = Modifier.fillMaxSize(),
288+
factory = { context ->
289+
playerSkin.loadWebView(
290+
context = context,
291+
onPageFinished = {
292+
pageFinished = true
293+
playerSkin.startAnim(ModelAnimation.NewIdle)
311294
}
312-
}
313-
runCatching {
314-
accountCape?.inputStream().use { inputStream ->
315-
playerSkin.loadCape(inputStream)
295+
)
296+
},
297+
update = {
298+
if (currentAccount != null && pageFinished) {
299+
runCatching {
300+
accountSkin?.inputStream().use { inputStream ->
301+
playerSkin.loadSkin(inputStream, currentAccount.skinModelType)
302+
}
303+
}
304+
runCatching {
305+
accountCape?.inputStream().use { inputStream ->
306+
playerSkin.loadCape(inputStream)
307+
}
316308
}
317309
}
318310
}
311+
)
312+
if (!pageFinished) {
313+
CircularProgressIndicator()
319314
}
320-
)
315+
}
321316

322317
//添加账号
323318
ScalingActionButton(
@@ -697,7 +692,6 @@ private fun AccountsLayout(
697692
.padding(vertical = 6.dp),
698693
currentAccount = currentAccount,
699694
account = account,
700-
refreshKey = actions.refreshAvatarMap[account.uniqueUUID] ?: false,
701695
onSelected = { AccountsManager.setCurrentAccount(it) },
702696
openChangeSkinDialog = {
703697
if (!account.isAuthServerAccount()) {
@@ -797,7 +791,7 @@ private fun AccountSkinOperation(
797791
actions.onIntent(
798792
AccountManageIntent.ApplyMicrosoftCape(
799793
account,
800-
cape.id.takeIf { cape != EmptyCape },
794+
cape.takeIf { cape != EmptyCape },
801795
name,
802796
cape == EmptyCape
803797
)
@@ -866,7 +860,6 @@ private fun AccountManageContentPreview() {
866860
checkIfInWebScreen = { false },
867861
formatError = { _, _ -> "" },
868862
submitError = {},
869-
refreshAvatarMap = mutableMapOf()
870863
)
871864
)
872865
}

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/viewmodel/AccountManageViewModel.kt

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import com.movtery.zalithlauncher.game.account.yggdrasil.PlayerProfile
4949
import com.movtery.zalithlauncher.game.account.yggdrasil.cacheAllCapes
5050
import com.movtery.zalithlauncher.game.account.yggdrasil.changeCape
5151
import com.movtery.zalithlauncher.game.account.yggdrasil.executeWithAuthorization
52+
import com.movtery.zalithlauncher.game.account.yggdrasil.getFile
5253
import com.movtery.zalithlauncher.game.account.yggdrasil.getPlayerProfile
5354
import com.movtery.zalithlauncher.game.account.yggdrasil.uploadSkin
5455
import com.movtery.zalithlauncher.path.PathManager
@@ -130,7 +131,7 @@ sealed interface AccountManageIntent {
130131
/** 应用选中的微软披风 */
131132
data class ApplyMicrosoftCape(
132133
val account: Account,
133-
val capeId: String?,
134+
val cape: PlayerProfile.Cape?,
134135
val capeName: String,
135136
val isReset: Boolean
136137
) : AccountManageIntent
@@ -179,9 +180,6 @@ sealed class AccountManageEffect {
179180
val formatArgs: List<Any> = emptyList(),
180181
val duration: Int = Toast.LENGTH_SHORT
181182
) : AccountManageEffect()
182-
183-
/** 通知 UI 层对应账号的头像数据已更新,需要重新加载显示 */
184-
data class RefreshAvatar(val accountUuid: String) : AccountManageEffect()
185183
}
186184

187185
/**
@@ -345,13 +343,6 @@ class AccountManageViewModel @Inject constructor(
345343
}
346344
}
347345

348-
/** 内部方法:触发头像重载副作用 */
349-
private fun emitRefreshAvatar(accountUuid: String) {
350-
viewModelScope.launch {
351-
_effect.send(AccountManageEffect.RefreshAvatar(accountUuid))
352-
}
353-
}
354-
355346
/** 执行微软登录流程 */
356347
private fun performMicrosoftLogin(intent: AccountManageIntent.PerformMicrosoftLogin) {
357348
microsoftLogin(
@@ -486,7 +477,8 @@ class AccountManageViewModel @Inject constructor(
486477
/** 更改微软账号披风 */
487478
private fun applyMicrosoftCape(intent: AccountManageIntent.ApplyMicrosoftCape) {
488479
val account = intent.account
489-
val capeId = intent.capeId
480+
val cape = intent.cape
481+
val capeId = cape?.id
490482
val capeName = intent.capeName
491483
val isReset = intent.isReset
492484

@@ -503,6 +495,18 @@ class AccountManageViewModel @Inject constructor(
503495
AccountsManager.suspendSaveAccount(account)
504496
})
505497

498+
val capeFile = cape?.getFile(PathManager.DIR_ACCOUNT_CAPE)
499+
val targetCape = account.getCapeFile()
500+
if (capeFile?.exists() == true) {
501+
runCatching {
502+
FileUtils.deleteQuietly(targetCape)
503+
capeFile.copyTo(targetCape)
504+
}
505+
} else {
506+
FileUtils.deleteQuietly(targetCape)
507+
}
508+
AccountsManager.refreshWardrobe()
509+
506510
_accountCapeOpMap.update { capesMap ->
507511
if (!capesMap.containsKey(account.uniqueUUID)) return@update capesMap
508512
buildMap {
@@ -602,7 +606,7 @@ class AccountManageViewModel @Inject constructor(
602606
cacheFile.copyTo(skinFile, true)
603607
FileUtils.deleteQuietly(cacheFile)
604608
AccountsManager.suspendSaveAccount(account)
605-
emitRefreshAvatar(account.uniqueUUID)
609+
AccountsManager.refreshWardrobe()
606610
onIntent(
607611
AccountManageIntent.UpdateAccountSkinOp(
608612
account.uniqueUUID,
@@ -624,7 +628,7 @@ class AccountManageViewModel @Inject constructor(
624628
}, onError = { th ->
625629
FileUtils.deleteQuietly(cacheFile)
626630
emitError(context.getString(R.string.error_import_image), th.getMessageOrToString())
627-
emitRefreshAvatar(account.uniqueUUID)
631+
AccountsManager.refreshWardrobe()
628632
onIntent(
629633
AccountManageIntent.UpdateAccountSkinOp(
630634
account.uniqueUUID,
@@ -642,7 +646,7 @@ class AccountManageViewModel @Inject constructor(
642646
skinModelType = SkinModelType.NONE
643647
profileId = getLocalUUIDWithSkinModel(username, skinModelType)
644648
AccountsManager.suspendSaveAccount(this)
645-
emitRefreshAvatar(account.uniqueUUID)
649+
AccountsManager.refreshWardrobe()
646650
}
647651
}))
648652
onIntent(

0 commit comments

Comments
 (0)