fix(wavelog): the auto-upload switch had nothing behind it
Deleting the ten-minute polling loop left the "auto upload" switch in settings with no consumer - the operator could turn it on and nothing would ever act on it, which is worse than the loop it replaced. Uploading now happens when a contact is saved. That is what the switch always meant, and doing it at that moment means somebody is present to see the outcome: a partial failure says so, and the QSOs that did not go stay in the queue for a manual upload from settings. The loop reported nothing at all - a grid mismatch hit an empty if block and every other failure retried forever in the background. The upload goes through the view model rather than the composable, so the log screen still touches no repository.
This commit is contained in:
1 parent
4567f46867
commit
758dc6d567
5 files changed
+54
-7
No files matched your search
@@ -266,6 +266,7 @@
|
||||
<string name="wavelog_not_configured">未配置 WaveLog 服务器</string>
|
||||
<string name="wavelog_no_station">读不到站点信息 - 请检查站点 ID 与密钥权限</string>
|
||||
<string name="wavelog_upload_failed">上传失败</string>
|
||||
<string name="wavelog_upload_queued">部分联络未能上传, 仍在队列中</string>
|
||||
<string name="lotw_list_updated">LoTW 卫星列表已更新: %1$d 个</string>
|
||||
<string name="lotw_list_failed">LoTW 列表更新失败: %1$s</string>
|
||||
<string name="log_call_too_short">呼号太短</string>
|
||||
|
||||
@@ -297,6 +297,7 @@
|
||||
<string name="wavelog_not_configured">WaveLog server not configured</string>
|
||||
<string name="wavelog_no_station">Cannot read the station profile - check the station ID and key permissions</string>
|
||||
<string name="wavelog_upload_failed">Upload failed</string>
|
||||
<string name="wavelog_upload_queued">Some contacts could not be uploaded and stay in the queue</string>
|
||||
<string name="lotw_list_updated">LoTW satellite list updated: %1$d entries</string>
|
||||
<string name="lotw_list_failed">LoTW list update failed: %1$s</string>
|
||||
<string name="log_call_too_short">Callsign too short</string>
|
||||
|
||||
@@ -101,6 +101,7 @@ fun LogTab(
|
||||
txBaseFrequencyHz: Long? = null,
|
||||
aosTimeMs: Long = 0L,
|
||||
onLookupGrid: (String, (QrzGrid) -> Unit) -> Unit,
|
||||
onAutoUpload: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
@@ -177,6 +178,7 @@ fun LogTab(
|
||||
txBaseFrequencyHz = txBaseFrequencyHz,
|
||||
aosTimeMs = aosTimeMs,
|
||||
onLookupGrid = onLookupGrid,
|
||||
onAutoUpload = onAutoUpload,
|
||||
onSaved = { refreshTick++ }
|
||||
)
|
||||
}
|
||||
@@ -265,6 +267,7 @@ private fun ExpandedLogInput(
|
||||
txBaseFrequencyHz: Long? = null,
|
||||
aosTimeMs: Long = 0L,
|
||||
onLookupGrid: (String, (QrzGrid) -> Unit) -> Unit,
|
||||
onAutoUpload: () -> Unit,
|
||||
onSaved: () -> Unit
|
||||
) {
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
@@ -332,6 +335,9 @@ private fun ExpandedLogInput(
|
||||
null -> savedMsg
|
||||
}
|
||||
)
|
||||
// Upload now if the operator asked for it. Doing this as the contact is saved, rather than
|
||||
// on a timer, means the result can actually be shown to somebody.
|
||||
onAutoUpload()
|
||||
// Grid backfill. The view model owns the cookie and the request; this used to read
|
||||
// SharedPreferences through LocalContext right here, inside composition. Failures now say
|
||||
// something: an expired cookie was indistinguishable from a station with no grid filed.
|
||||
|
||||
@@ -59,6 +59,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.rtbishop.look4sat.core.domain.qrz.QrzGrid
|
||||
import com.rtbishop.look4sat.core.domain.wavelog.UploadOutcome
|
||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||
import com.rtbishop.look4sat.core.domain.repository.MutualPassData
|
||||
@@ -94,7 +95,17 @@ fun RadarDestination(navigateUp: () -> Unit) {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
// Captured as a value because `viewModel` also names the composable factory function, so
|
||||
// referring to it inside a lambda resolves to that instead of this local.
|
||||
val uploadQueuedMsg = stringResource(id = R.string.wavelog_upload_queued)
|
||||
val lookupGrid: (String, (QrzGrid) -> Unit) -> Unit = viewModel::lookupGrid
|
||||
// Reports through the shared toast so an automatic upload is not silent the way the deleted
|
||||
// ten-minute loop was.
|
||||
val autoUpload: () -> Unit = {
|
||||
viewModel.uploadIfAutomatic { outcome ->
|
||||
if (outcome is UploadOutcome.Done && outcome.failedCount > 0) {
|
||||
container.provideShowToast()(uploadQueuedMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
val mutualData by container.mutualPassData.collectAsStateWithLifecycle()
|
||||
val navigateUpAndClearMutual = {
|
||||
if (container.mutualPassData.value.endTime > 0L) {
|
||||
@@ -143,7 +154,8 @@ fun RadarDestination(navigateUp: () -> Unit) {
|
||||
it.wavelogUrl.isNotBlank() && it.wavelogApiKey.isNotBlank() && it.wavelogStationId.isNotBlank()
|
||||
},
|
||||
showToast = { msg -> container.provideShowToast()(msg) },
|
||||
onLookupGrid = lookupGrid
|
||||
onLookupGrid = lookupGrid,
|
||||
onAutoUpload = autoUpload
|
||||
)
|
||||
}
|
||||
|
||||
@@ -157,7 +169,8 @@ private fun RadarScreen(
|
||||
wavelogQueue: WavelogQueue,
|
||||
wavelogConfigured: Boolean,
|
||||
showToast: (String) -> Unit,
|
||||
onLookupGrid: (String, (QrzGrid) -> Unit) -> Unit
|
||||
onLookupGrid: (String, (QrzGrid) -> Unit) -> Unit,
|
||||
onAutoUpload: () -> Unit
|
||||
) {
|
||||
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
||||
val addToCalendar: () -> Unit = {
|
||||
@@ -211,11 +224,11 @@ private fun RadarScreen(
|
||||
}
|
||||
if (isVertical) {
|
||||
RadarCard(uiState, trackB, trackBPosition, Modifier.weight(1f))
|
||||
PagerCard(uiState, onAction, requestMicPermission, wavelogQueue, wavelogConfigured, showToast, onLookupGrid, Modifier.weight(1f))
|
||||
PagerCard(uiState, onAction, requestMicPermission, wavelogQueue, wavelogConfigured, showToast, onLookupGrid, onAutoUpload, Modifier.weight(1f))
|
||||
} else {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
RadarCard(uiState, trackB, trackBPosition, Modifier.weight(1f))
|
||||
PagerCard(uiState, onAction, requestMicPermission, wavelogQueue, wavelogConfigured, showToast, onLookupGrid, Modifier.weight(1f))
|
||||
PagerCard(uiState, onAction, requestMicPermission, wavelogQueue, wavelogConfigured, showToast, onLookupGrid, onAutoUpload, Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,6 +243,7 @@ private fun PagerCard(
|
||||
wavelogConfigured: Boolean,
|
||||
showToast: (String) -> Unit,
|
||||
onLookupGrid: (String, (QrzGrid) -> Unit) -> Unit,
|
||||
onAutoUpload: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val hasCalculatorPage = remember(uiState.transceivers.transmitters) {
|
||||
@@ -299,7 +313,8 @@ private fun PagerCard(
|
||||
showToast = showToast,
|
||||
txBaseFrequencyHz = uiState.radioControl.txBaseFrequencyHz,
|
||||
aosTimeMs = uiState.currentPass?.aosTime ?: 0L,
|
||||
onLookupGrid = onLookupGrid
|
||||
onLookupGrid = onLookupGrid,
|
||||
onAutoUpload = onAutoUpload
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.core.domain.qrz.IQrzGridLookup
|
||||
import com.rtbishop.look4sat.core.domain.qrz.QrzGrid
|
||||
import com.rtbishop.look4sat.core.domain.wavelog.UploadOutcome
|
||||
import com.rtbishop.look4sat.core.domain.wavelog.WavelogUploader
|
||||
import com.rtbishop.look4sat.core.domain.repository.IMainContainer
|
||||
import com.rtbishop.look4sat.core.domain.repository.IRadioTrackingService
|
||||
import com.rtbishop.look4sat.core.domain.repository.IReporter
|
||||
@@ -67,7 +69,8 @@ class RadarViewModel(
|
||||
private val cwDecoderFactory: () -> ICwDecoder,
|
||||
private val saveImage: ISaveImage,
|
||||
private val showToast: IShowToast,
|
||||
private val qrzGridLookup: IQrzGridLookup
|
||||
private val qrzGridLookup: IQrzGridLookup,
|
||||
private val wavelogUploader: WavelogUploader
|
||||
) : ViewModel() {
|
||||
|
||||
private var stationPos = settingsRepo.stationPosition.value
|
||||
@@ -547,6 +550,26 @@ class RadarViewModel(
|
||||
_uiState.update { it.copy(cw = it.cw.copy(status = CwStatus.Idle)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the queue now, if the operator asked for automatic uploads.
|
||||
*
|
||||
* Called when a contact is saved rather than on a timer. The ten-minute polling loop this
|
||||
* replaces ran in the background with no way to report anything, so a grid mismatch was
|
||||
* silently skipped and every other failure retried forever. Uploading as the contact is saved
|
||||
* means somebody is present to see the result, and a QSO that cannot go now waits in the queue
|
||||
* for a manual upload from settings.
|
||||
*/
|
||||
fun uploadIfAutomatic(onResult: (UploadOutcome?) -> Unit) {
|
||||
val settings = settingsRepo.otherSettings.value
|
||||
if (!settings.wavelogAutoUpload || settings.wavelogUrl.isBlank()) {
|
||||
onResult(null)
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
onResult(runCatching { wavelogUploader.uploadQueue() }.getOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a station's grid and hand the outcome back.
|
||||
*
|
||||
@@ -619,7 +642,8 @@ class RadarViewModel(
|
||||
cwDecoderFactory = { container.provideCwDecoder() },
|
||||
saveImage = container.provideSaveImage(),
|
||||
showToast = container.provideShowToast(),
|
||||
qrzGridLookup = container.provideQrzGridLookup()
|
||||
qrzGridLookup = container.provideQrzGridLookup(),
|
||||
wavelogUploader = container.provideWavelogUploader()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user