Merge 577f8f6260bb37aecf25619d17e1f468efd437d1 into 1f24973b06ee77492742ba522002e028f38f4381

This commit is contained in:
Taksh Kothari 2026-11-08 11:45:00 +05:30 committed by GitHub
commit 3d36d5efb7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 2 deletions

View File

@ -89,7 +89,12 @@ internal object LiveLocationPrivacyGate {
fun update(enabled: Boolean) {
policy.update(enabled)
notifyRevoked()
// Revocation listeners cancel in-flight location work. Turning location
// *on* must not fire them — that used to kill the one-shot/refresh that
// Enable Location just started (#577).
if (!enabled) {
notifyRevoked()
}
}
fun invalidate() {

View File

@ -3,6 +3,7 @@ package com.bitchat.android.ui
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -17,6 +18,7 @@ import com.bitchat.android.core.ui.component.sheet.BitchatSheetTitle
import com.bitchat.android.geohash.GeohashChannelLevel
import com.bitchat.android.geohash.LocationChannelManager
import com.bitchat.android.R
import com.bitchat.android.geohash.LiveLocationPrivacyGate
/**
* Presenter component for LocationNotesSheet
@ -34,6 +36,8 @@ fun LocationNotesSheetPresenter(
val availableChannels by locationManager.availableChannels.collectAsStateWithLifecycle()
val permissionState by locationManager.permissionState.collectAsStateWithLifecycle()
val isLoadingLocation by locationManager.isLoadingLocation.collectAsStateWithLifecycle()
val appLocationEnabled by LiveLocationPrivacyGate.enabled.collectAsStateWithLifecycle()
val systemLocationEnabled by locationManager.systemLocationEnabled.collectAsStateWithLifecycle()
val nickname by viewModel.nickname.collectAsStateWithLifecycle()
// iOS pattern: notesGeohash ?? LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash
@ -51,7 +55,17 @@ fun LocationNotesSheetPresenter(
nickname = nickname,
onDismiss = onDismiss
)
} else if (permissionState == LocationChannelManager.PermissionState.AUTHORIZED && isLoadingLocation) {
} else if (permissionState == LocationChannelManager.PermissionState.AUTHORIZED &&
(isLoadingLocation || (appLocationEnabled && systemLocationEnabled))
) {
// Authorized with services on but no building channel yet — first
// install often has no cached last-fix, so treat this as acquiring
// rather than "unavailable" (#577). Kick a live refresh once.
LaunchedEffect(appLocationEnabled, systemLocationEnabled) {
if (appLocationEnabled && systemLocationEnabled) {
locationManager.beginLiveRefresh()
}
}
LocationNotesAcquiringSheet(onDismiss = onDismiss)
} else {
// No building geohash available - show error state (matches iOS)
@ -138,6 +152,7 @@ private fun LocationNotesErrorSheet(
// Then request location channels (which will also request permission if needed)
locationManager.enableLocationChannels()
locationManager.refreshChannels()
locationManager.beginLiveRefresh()
}) {
Text("Enable Location")
}

View File

@ -0,0 +1,32 @@
package com.bitchat.android.geohash
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class LiveLocationPrivacyGateTest {
@Test
fun `enabling location does not fire revocation listeners`() {
var revoked = 0
val listener = { revoked += 1 }
LiveLocationPrivacyGate.addRevocationListener(listener)
try {
LiveLocationPrivacyGate.update(false)
val afterDisable = revoked
assertTrue("disable should revoke", afterDisable >= 1)
LiveLocationPrivacyGate.update(true)
assertEquals(
"enable must not cancel in-flight location work",
afterDisable,
revoked
)
LiveLocationPrivacyGate.update(false)
assertTrue(revoked > afterDisable)
} finally {
LiveLocationPrivacyGate.removeRevocationListener(listener)
LiveLocationPrivacyGate.update(false)
}
}
}