Skip to content

Unwrap ProxyDesign#143

Merged
Goooler merged 1 commit intotrunkfrom
unwrap-proxy-design
Apr 26, 2026
Merged

Unwrap ProxyDesign#143
Goooler merged 1 commit intotrunkfrom
unwrap-proxy-design

Conversation

@Goooler
Copy link
Copy Markdown
Owner

@Goooler Goooler commented Apr 26, 2026

Refs #119.

@Goooler Goooler force-pushed the unwrap-proxy-design branch 3 times, most recently from 01293ee to c45e95c Compare April 26, 2026 12:02
@Goooler Goooler force-pushed the unwrap-proxy-design branch from c45e95c to c78c0dc Compare April 26, 2026 12:05
@Goooler Goooler merged commit ea6354f into trunk Apr 26, 2026
2 checks passed
@Goooler Goooler deleted the unwrap-proxy-design branch April 26, 2026 12:05
@Goooler Goooler requested a review from Copilot April 27, 2026 00:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the Proxy page to “unwrap” the old Design-based implementation into a Compose screen backed by a dedicated ViewModel (per #119).

Changes:

  • Introduces ProxyViewModel to own proxy UI state, events, and reload/selection operations.
  • Replaces ProxyDesign/DesignActivity flow with a Compose ProxyScreen hosted by ProxyActivity.
  • Removes the now-unused ProxyState model.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
app/src/main/kotlin/com/github/kr328/clash/proxy/vm/ProxyViewModel.kt New ViewModel containing proxy state, event handling, and remote query/patch logic.
app/src/main/kotlin/com/github/kr328/clash/proxy/ui/ProxyScreen.kt Converts Proxy UI to a ViewModel-driven Compose screen and removes the old ProxyDesign implementation.
app/src/main/kotlin/com/github/kr328/clash/proxy/ProxyActivity.kt Switches activity hosting from DesignActivity to Compose setContent { ProxyScreen(...) }.
app/src/main/kotlin/com/github/kr328/clash/model/ProxyState.kt Removes obsolete Compose state wrapper used by the previous design.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +191 to +198
val sources =
withContext(Dispatchers.Default) {
group.proxies.map { proxy ->
UiState.ProxyItemSource(
proxy = proxy,
linkIndex = if (proxy.type.group) names.indexOf(proxy.name) else -1,
)
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

Building ProxyItemSource uses names.indexOf(proxy.name) for every group proxy. This makes source generation O(groupNames * proxies) per reload and can become noticeable with larger lists. Consider precomputing a Map<String, Int> (name→index) once per reload and doing O(1) lookups when setting linkIndex.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +62
override fun onStart(owner: LifecycleOwner) {
broadcastEventsJob?.cancel()
broadcastEventsJob = viewModelScope.launch {
Remote.broadcasts.event.collect { event ->
when (event) {
Broadcasts.Event.ProfileLoaded -> {
val newNames = withClash { queryProxyGroupNames(uiStore.proxyExcludeNotSelectable) }
if (newNames != uiState.value.groupNames) {
eventState.value = EventState.ReLaunch
}
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

ProfileLoaded handler compares newNames against uiState.value.groupNames, but groupNames is still empty until fetchInitialState() completes. If ProfileLoaded arrives early, this can spuriously set ReLaunch (potentially causing a relaunch loop). Consider fetching initial state before starting the broadcast collection, or guard/queue ProfileLoaded events until initialization has populated groupNames (even if empty).

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +69
override fun onStart(owner: LifecycleOwner) {
broadcastEventsJob?.cancel()
broadcastEventsJob = viewModelScope.launch {
Remote.broadcasts.event.collect { event ->
when (event) {
Broadcasts.Event.ProfileLoaded -> {
val newNames = withClash { queryProxyGroupNames(uiStore.proxyExcludeNotSelectable) }
if (newNames != uiState.value.groupNames) {
eventState.value = EventState.ReLaunch
}
}
else -> Unit
}
}
}

viewModelScope.launch { fetchInitialState() }
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

fetchInitialState() is launched every onStart() without cancellation or an “already initialized” guard. Since it triggers reloadAll(), this can cause repeated expensive reloads (and possible overlapping loads) whenever the activity stops/starts. Consider tracking a fetchJob (cancel+replace) or skipping the initial fetch if state is already loaded, similar to MainViewModel.fetchJob (app/.../MainViewModel.kt:34,111-114).

Copilot uses AI. Check for mistakes.
Copilot AI added a commit that referenced this pull request Apr 27, 2026
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