Skip to content

基于hilt与MVI架构重构AccountManageScreen#838

Open
youfeng11 wants to merge 18 commits intoZalithLauncher:mainfrom
youfeng11:main
Open

基于hilt与MVI架构重构AccountManageScreen#838
youfeng11 wants to merge 18 commits intoZalithLauncher:mainfrom
youfeng11:main

Conversation

@youfeng11
Copy link
Collaborator

  • 为项目引入hilt处理依赖注入
  • 基于MVI架构重构AccountManageScreen,以减少 UI 与业务逻辑耦合,提高代码可读性和可维护性
  • 添加了一个UI预览

…and Unified UI State

- Added Hilt dependency injection support across the project.
- Refactored `AccountManageViewModel` to use a single `AccountManageUiState` data class, following the Single Source of Truth principle.
- Simplified `AccountManageScreen` by moving logic into an `AccountActions` wrapper to reduce parameter overhead.
- Updated `ZLApplication` and `MainActivity` with Hilt entry point annotations.
- Streamlined business logic and error handling in `AccountManageViewModel`.
…ecture.

- Introduce `AccountManageIntent` to encapsulate user actions and `AccountManageEffect` for one-time UI events (toasts, errors).
- Refactor `AccountManageViewModel` to use a central `onIntent` handler and a `Channel` for side effects.
- Simplify `AccountActions` in the UI layer by passing a single `onIntent` callback.
- Move business logic execution and error handling into private ViewModel methods, improving separation of concerns.
- Replace several direct `ErrorViewModel` submissions with `LaunchedEffect` observers for a cleaner UI implementation.
…g stability.

- Wrap error submissions in `LaunchedEffect` across multiple operation handlers to prevent side effects during composition.
- Immediately reset `MicrosoftChangeSkinOperation` to `None` when starting a skin upload to ensure consistent state.
- Update `formatAccountError` to correctly distinguish between Ktor `ResponseException` and custom generic response exceptions.
- Refactor `AccountManageViewModel` and UI components with better code formatting and more robust context handling for error messages.
…nces.

- Use explicit imports for `Toast`, `AuthServer`, and `getLocalUUIDWithSkinModel` to replace fully qualified names.
- Improve code readability by using shortened class and function references in `AccountManageViewModel` and `AccountManageScreen`.
@youfeng11 youfeng11 marked this pull request as draft March 24, 2026 08:04
@youfeng11 youfeng11 marked this pull request as ready for review March 24, 2026 10:25
…ilt-injected ApplicationContext and improving side-effect handling.

- Inject `@ApplicationContext` into `AccountManageViewModel` to remove the need for passing `Context` through multiple `AccountManageIntent` data classes.
- Introduce `AccountManageEffect.RefreshAvatar` to handle avatar updates via UI-side `LaunchedEffect` instead of passing callbacks through intents.
- Refactor `AccountManageIntent` to simplify data structures for skin, cape, and login operations.
- Improve UI state management in `AccountManageScreen` by using a `refreshAvatarMap` to trigger localized avatar updates.
- Remove redundant intermediate operation states (`RunTask`) from `MicrosoftLoginOperation`, `MicrosoftChangeSkinOperation`, and `MicrosoftChangeCapeOperation`.
- Clean up `AccountActions` and dialog implementations to prevent potential memory leaks and improve separation of concerns.
- Add detailed KDoc for `AccountManageUiState`, `AccountManageIntent`, `AccountManageEffect`, and `AccountManageViewModel` to document the MVI architecture and flow.
- Add descriptive comments to business logic methods in `AccountManageViewModel.kt`.
- Add documentation for UI components and helper classes in `AccountManageScreen.kt`.
- Improve code readability by categorizing intents and effects within the ViewModel.
…state handling.

- Remove unnecessary `Context` parameter from `formatAccountError` as it is already available in the ViewModel.
- Use an import alias for `io.ktor.client.plugins.ResponseException` to improve code readability.
- Immediately reset the Microsoft cape operation state when starting a task to ensure the UI dialog closes promptly.
- Remove an redundant `Column` wrapper in `SimpleAlertDialog` within `AccountManageScreen.kt`.
- Remove an unused `LocalContext` reference in `OtherAccountManagement`.
- Clean up an unnecessary avatar refresh call after skin updates.
…hanism.

- Replace `mutableMapOf` and a manual `refreshKey` toggle with `mutableStateMapOf` to properly handle UI updates for avatar refreshes.
- Simplify state propagation by removing the unnecessary `refreshKey` parameter from `AccountManageContent` and related sub-composables.
- Ensure the avatar refresh logic correctly toggles the state within the observable map to trigger recomposition.
@MovTery MovTery self-requested a review March 25, 2026 15:02
@MovTery
Copy link
Contributor

MovTery commented Mar 25, 2026

是的,没错,从依托石山变成了另依托石山

Comment on lines +106 to +118
data class AccountManageUiState(
val accounts: List<Account> = emptyList(),
val currentAccount: Account? = null,
val authServers: List<AuthServer> = emptyList(),
val microsoftLoginOperation: MicrosoftLoginOperation = MicrosoftLoginOperation.None,
val microsoftChangeSkinOperation: MicrosoftChangeSkinOperation = MicrosoftChangeSkinOperation.None,
val microsoftChangeCapeOperation: MicrosoftChangeCapeOperation = MicrosoftChangeCapeOperation.None,
val localLoginOperation: LocalLoginOperation = LocalLoginOperation.None,
val otherLoginOperation: OtherLoginOperation = OtherLoginOperation.None,
val serverOperation: ServerOperation = ServerOperation.None,
val accountOperation: AccountOperation = AccountOperation.None,
val accountSkinOperationMap: Map<String, AccountSkinOperation> = emptyMap()
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在我看来,这个属于是过度追求统一管理了,因为viewModel本身就是以数据管理而存在的,强行将所有数据套进data class里,再装进viewModel里,是否有点多此一举?

Copy link
Collaborator Author

@youfeng11 youfeng11 Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样是为了让 ViewModel 只暴露一个统一的数据源,减少参数传递,方便管理

Comment on lines +231 to +281
private val _microsoftLoginOp =
MutableStateFlow<MicrosoftLoginOperation>(MicrosoftLoginOperation.None)
private val _microsoftSkinOp =
MutableStateFlow<MicrosoftChangeSkinOperation>(MicrosoftChangeSkinOperation.None)
private val _microsoftCapeOp =
MutableStateFlow<MicrosoftChangeCapeOperation>(MicrosoftChangeCapeOperation.None)
private val _localLoginOp = MutableStateFlow<LocalLoginOperation>(LocalLoginOperation.None)
private val _otherLoginOp = MutableStateFlow<OtherLoginOperation>(OtherLoginOperation.None)
private val _serverOp = MutableStateFlow<ServerOperation>(ServerOperation.None)
private val _accountOp = MutableStateFlow<AccountOperation>(AccountOperation.None)
private val _accountSkinOpMap = MutableStateFlow<Map<String, AccountSkinOperation>>(emptyMap())

private val _effect = Channel<AccountManageEffect>(Channel.BUFFERED)
val effect = _effect.receiveAsFlow()

/**
* 统一的 UI 状态流
* 使用 combine 组合了来自持久层 (AccountsManager) 的数据流与 View 层内部的交互状态流
*/
val uiState: StateFlow<AccountManageUiState> = combine(
AccountsManager.accountsFlow,
AccountsManager.currentAccountFlow,
AccountsManager.authServersFlow,
_microsoftLoginOp,
_microsoftSkinOp,
_microsoftCapeOp,
_localLoginOp,
_otherLoginOp,
_serverOp,
_accountOp,
_accountSkinOpMap
) { args ->
@Suppress("UNCHECKED_CAST")
AccountManageUiState(
accounts = args[0] as List<Account>,
currentAccount = args[1] as Account?,
authServers = args[2] as List<AuthServer>,
microsoftLoginOperation = args[3] as MicrosoftLoginOperation,
microsoftChangeSkinOperation = args[4] as MicrosoftChangeSkinOperation,
microsoftChangeCapeOperation = args[5] as MicrosoftChangeCapeOperation,
localLoginOperation = args[6] as LocalLoginOperation,
otherLoginOperation = args[7] as OtherLoginOperation,
serverOperation = args[8] as ServerOperation,
accountOperation = args[9] as AccountOperation,
accountSkinOperationMap = args[10] as Map<String, AccountSkinOperation>
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = AccountManageUiState()
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这一大堆我没看懂,为什么不直接这么做?

private val _uiState = MutableStateFlow<AccountManageUiState>(AccountManageUiState())
val uiState = _uiState.asStateFlow()

Copy link
Collaborator Author

@youfeng11 youfeng11 Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样不用手动update,可以减少状态不同步的风险

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants