Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
Expand All @@ -15,18 +16,32 @@ import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.android.util.SettingsCache
import com.mbta.tid.mbta_app.android.util.Typography
import com.mbta.tid.mbta_app.android.util.timer
import com.mbta.tid.mbta_app.repositories.IDebugRepository
import com.mbta.tid.mbta_app.repositories.Settings
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
import org.koin.compose.koinInject

@Composable
fun DebugView(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
fun DebugView(
modifier: Modifier = Modifier,
debugRepository: IDebugRepository = koinInject(),
content: @Composable ColumnScope.() -> Unit,
) {
val debugMode = SettingsCache.get(Settings.DevDebugMode)
if (!debugMode) return
val textColor = colorResource(R.color.text)
val color = colorResource(R.color.fill3)
val density = LocalDensity.current

val debugState by debugRepository.state.collectAsStateWithLifecycle()
val now by timer(updateInterval = 1.seconds)

Column(
modifier
.fillMaxWidth()
Expand All @@ -48,6 +63,20 @@ fun DebugView(modifier: Modifier = Modifier, content: @Composable ColumnScope.()
}
.padding(8.dp)
) {
ProvideTextStyle(Typography.footnote.copy(color = textColor)) { content() }
ProvideTextStyle(Typography.footnote.copy(color = textColor)) {
content()

debugState?.channelUpdates?.let {
Text("channel connections:")
it.entries
.sortedBy { it.key }
.forEach { (key, value) ->
val trimmedKey = if (key.length > 25) "${key.substring(0, 25)}..." else key
val duration =
(now - value).absoluteValue.toString(DurationUnit.SECONDS, decimals = 0)
Text("$trimmedKey last updated $duration ago")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.lifecycle.compose.LifecycleResumeEffect
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.android.component.ActionButton
import com.mbta.tid.mbta_app.android.component.ActionButtonKind
import com.mbta.tid.mbta_app.android.component.DebugView
import com.mbta.tid.mbta_app.android.component.ErrorBanner
import com.mbta.tid.mbta_app.android.component.NavTextButton
import com.mbta.tid.mbta_app.android.component.SheetHeader
Expand Down Expand Up @@ -152,6 +153,7 @@ fun FavoritesView(
}

ErrorBanner(errorBannerViewModel, modifier = Modifier.padding(top = 8.dp))
DebugView {}
RouteCardList(
routeCardData = routeCardData,
emptyView = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.mbta.tid.mbta_app.analytics.Analytics
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.android.component.DebugView
import com.mbta.tid.mbta_app.android.component.ErrorBanner
import com.mbta.tid.mbta_app.android.component.SheetHeader
import com.mbta.tid.mbta_app.android.component.routeCard.RouteCardList
Expand Down Expand Up @@ -86,6 +87,7 @@ fun NearbyTransitView(
),
)
ErrorBanner(errorBannerViewModel)
DebugView {}
LaunchedEffect(
stopIds,
globalResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ fun TripDetailsPage(
}
ErrorBanner(errorBannerViewModel, Modifier.padding(bottom = 8.dp))
DebugView {
Column(
Modifier.align(Alignment.CenterHorizontally),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(Modifier.align(Alignment.Start), horizontalAlignment = Alignment.Start) {
Text("trip id: ${filter.tripId}")
Text("vehicle id: ${filter.vehicleId ?: "null"}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,7 @@ fun StopDetailsFilteredView(

ErrorBanner(errorBannerViewModel, Modifier.padding(bottom = 16.dp))
DebugView {
Column(
Modifier.align(Alignment.CenterHorizontally),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(Modifier.align(Alignment.Start), horizontalAlignment = Alignment.Start) {
Text("stop id: $stopId")
Text("trip id: ${tripFilter?.tripId ?: "null"}")
Text("vehicle id: ${tripFilter?.vehicleId ?: "null"}")
Expand Down
34 changes: 32 additions & 2 deletions iosApp/iosApp/ComponentViews/DebugView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import Foundation
import Shared
import SwiftUI

struct DebugView<Content: View>: View {
Expand All @@ -15,6 +16,10 @@ struct DebugView<Content: View>: View {
let content: () -> Content

@EnvironmentObject var settingsCache: SettingsCache
@State var debugRepository = RepositoryDI().debug
@State private var debugState: DebugState? = nil
@State private var now = EasternTimeInstant.now()
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

var body: some View {
if settingsCache.get(.devDebugMode) {
Expand All @@ -23,9 +28,34 @@ struct DebugView<Content: View>: View {
.strokeBorder(Color(.text), style: .init(lineWidth: 2, dash: [10]))
VStack(alignment: .leading) {
content()
.font(Typography.footnote)
}.padding(8)

if let channelUpdates = debugState?.channelUpdates {
Text(verbatim: "channel connections:")
ForEach(channelUpdates.keys.sorted(), id: \.self) { key in
let trimmedKey = if key.count > 25 { "\(key.prefix(25))..." } else { key }
let updateDuration = if let updateTime = channelUpdates[key] {
Duration(
secondsComponent: abs(now.minus(updateTime).inWholeSeconds),
attosecondsComponent: 0
)
.formatted(.units(allowed: [.seconds], width: .narrow))
} else { "??" }

Text(verbatim: "\(trimmedKey) last updated \(updateDuration) ago")
}
}
}
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
.font(Typography.footnote)
.padding(8)
}
.task {
for await state in debugRepository.state {
debugState = state
}
}
.onReceive(timer) { input in now = input.toEasternInstant() }
.frame(maxWidth: .infinity)
.background(Color.fill3)
.padding(4)
Expand Down
1 change: 1 addition & 0 deletions iosApp/iosApp/Pages/Favorites/FavoritesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct FavoritesView: View {
}

ErrorBanner(errorBannerVM, padding: .init([.horizontal, .bottom], 16))
DebugView { EmptyView() }
RouteCardList(
routeCardData: favoritesVMState.routeCardData,
emptyView: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct NearbyTransitPage: View {
)
.padding(.bottom, 16)
ErrorBanner(errorBannerVM, padding: .init([.horizontal, .bottom], 16))
DebugView { EmptyView() }
NearbyTransitView(
location: $location,
setIsReturningFromBackground: { errorBannerVM.setIsLoadingWhenPredictionsStale(isLoading: $0) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ struct StopDetailsFilteredView: View {
)
ErrorBanner(errorBannerVM, padding: .init([.horizontal, .bottom], 16))
DebugView {
VStack {
VStack(alignment: .leading) {
Text(verbatim: "stop id: \(stopId)")
Text(verbatim: "trip id: \(tripFilter?.tripId ?? "nil")")
Text(verbatim: "vehicle id: \(tripFilter?.vehicleId ?? "nil")")
Expand Down
2 changes: 1 addition & 1 deletion iosApp/iosApp/Pages/TripDetails/TripDetailsPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct TripDetailsPage: View {
TripDetailsHeader(route: route, direction: direction, navCallbacks: navCallbacks)
ErrorBanner(errorBannerVM, padding: [.init(.bottom, 8), .init(.horizontal, 14)])
DebugView {
VStack {
VStack(alignment: .leading) {
Text(verbatim: "trip id: \(filter.tripId)")
Text(verbatim: "vehicle id: \(filter.vehicleId ?? "nil")")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.mbta.tid.mbta_app.repositories.IAccessibilityStatusRepository
import com.mbta.tid.mbta_app.repositories.IAlertsRepository
import com.mbta.tid.mbta_app.repositories.IConfigRepository
import com.mbta.tid.mbta_app.repositories.ICurrentAppVersionRepository
import com.mbta.tid.mbta_app.repositories.IDebugRepository
import com.mbta.tid.mbta_app.repositories.IErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.IFavoritesRepository
import com.mbta.tid.mbta_app.repositories.IGlobalRepository
Expand Down Expand Up @@ -65,6 +66,7 @@ public fun repositoriesModule(repositories: IRepositories): Module {
return module {
single<IConfigRepository> { repositories.config }
single<ITabPreferencesRepository> { repositories.tabPreferences }
single<IDebugRepository> { repositories.debug }
single<IErrorBannerStateRepository> { repositories.errorBanner }
single<IGlobalRepository> { repositories.global }
single<ILastLaunchedAppVersionRepository> { repositories.lastLaunchedAppVersion }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import com.mbta.tid.mbta_app.model.response.TripSchedulesResponse
import com.mbta.tid.mbta_app.model.response.VehicleStreamDataResponse
import com.mbta.tid.mbta_app.repositories.CachedSchedulesRepository
import com.mbta.tid.mbta_app.repositories.ConfigRepository
import com.mbta.tid.mbta_app.repositories.DebugRepository
import com.mbta.tid.mbta_app.repositories.ErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.FavoritesRepository
import com.mbta.tid.mbta_app.repositories.GlobalRepository
import com.mbta.tid.mbta_app.repositories.IAccessibilityStatusRepository
import com.mbta.tid.mbta_app.repositories.IAlertsRepository
import com.mbta.tid.mbta_app.repositories.IConfigRepository
import com.mbta.tid.mbta_app.repositories.ICurrentAppVersionRepository
import com.mbta.tid.mbta_app.repositories.IDebugRepository
import com.mbta.tid.mbta_app.repositories.IErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.IFavoritesRepository
import com.mbta.tid.mbta_app.repositories.IGlobalRepository
Expand Down Expand Up @@ -58,6 +60,7 @@ import com.mbta.tid.mbta_app.repositories.MockAccessibilityStatusRepository
import com.mbta.tid.mbta_app.repositories.MockAlertsRepository
import com.mbta.tid.mbta_app.repositories.MockConfigRepository
import com.mbta.tid.mbta_app.repositories.MockCurrentAppVersionRepository
import com.mbta.tid.mbta_app.repositories.MockDebugRepository
import com.mbta.tid.mbta_app.repositories.MockErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.MockFavoritesRepository
import com.mbta.tid.mbta_app.repositories.MockGlobalRepository
Expand Down Expand Up @@ -99,6 +102,7 @@ public interface IRepositories {
public val alerts: IAlertsRepository?
public val config: IConfigRepository
public val currentAppVersion: ICurrentAppVersionRepository?
public val debug: IDebugRepository
public val errorBanner: IErrorBannerStateRepository
public val favorites: IFavoritesRepository
public val global: IGlobalRepository
Expand Down Expand Up @@ -129,6 +133,7 @@ public class RepositoryDI : IRepositories, KoinComponent {
override val alerts: IAlertsRepository by inject()
override val config: IConfigRepository by inject()
override val currentAppVersion: ICurrentAppVersionRepository by inject()
override val debug: IDebugRepository by inject()
override val errorBanner: IErrorBannerStateRepository by inject()
override val favorites: IFavoritesRepository by inject()
override val global: IGlobalRepository by inject()
Expand Down Expand Up @@ -162,6 +167,7 @@ internal class RealRepositories : IRepositories {
override val alerts = null
override val config = ConfigRepository()
override val currentAppVersion = null
override val debug = DebugRepository()
override val errorBanner = ErrorBannerStateRepository()
override val favorites = FavoritesRepository()
override val global = GlobalRepository()
Expand Down Expand Up @@ -194,6 +200,7 @@ public class MockRepositories : IRepositories {
override var config: IConfigRepository = MockConfigRepository()
override var currentAppVersion: ICurrentAppVersionRepository =
MockCurrentAppVersionRepository(AppVersion(0u, 0u, 0u))
override val debug: IDebugRepository = MockDebugRepository()
override var errorBanner: IErrorBannerStateRepository = MockErrorBannerStateRepository()
override var favorites: IFavoritesRepository = MockFavoritesRepository()
override var global: IGlobalRepository = IdleGlobalRepository()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@ public fun makeNativeModule(
single<INetworkConnectivityMonitor> { networkConnectivityMonitor }
single<PhoenixSocket> { socket }
factory<IAlertsRepository> {
AlertsRepository(get(), get(), get(named("coroutineDispatcherIO")))
AlertsRepository(get(), get(), get(), get(named("coroutineDispatcherIO")))
}
factory<IPredictionsRepository> {
PredictionsRepository(get(), get(), get(named("coroutineDispatcherIO")))
PredictionsRepository(get(), get(), get(), get(named("coroutineDispatcherIO")))
}
factory<ITripPredictionsRepository> {
TripPredictionsRepository(get(), get(), get(named("coroutineDispatcherIO")))
TripPredictionsRepository(get(), get(), get(), get(named("coroutineDispatcherIO")))
}
factory<IVehicleRepository> {
VehicleRepository(get(), get(), get(named("coroutineDispatcherIO")))
VehicleRepository(get(), get(), get(), get(named("coroutineDispatcherIO")))
}
factory<IVehiclesRepository> {
VehiclesRepository(get(), get(), get(named("coroutineDispatcherIO")))
VehiclesRepository(get(), get(), get(), get(named("coroutineDispatcherIO")))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.mbta.tid.mbta_app.network.PhoenixChannel
import com.mbta.tid.mbta_app.network.PhoenixMessage
import com.mbta.tid.mbta_app.network.PhoenixSocket
import com.mbta.tid.mbta_app.network.receiveAll
import com.mbta.tid.mbta_app.repositories.IDebugRepository
import com.mbta.tid.mbta_app.repositories.IErrorBannerStateRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
Expand All @@ -16,12 +17,14 @@ import kotlinx.coroutines.sync.withLock
internal class ChannelOwner<MessageData : Any>(
socket: PhoenixSocket,
dispatcher: CoroutineDispatcher,
debugRepository: IDebugRepository,
errorBannerStateRepository: IErrorBannerStateRepository,
) {
internal val owner =
AsymmetricChannelOwner<MessageData, MessageData>(
socket,
dispatcher,
debugRepository,
errorBannerStateRepository,
)
internal var channel: PhoenixChannel?
Expand All @@ -43,6 +46,7 @@ internal class ChannelOwner<MessageData : Any>(
internal class AsymmetricChannelOwner<JoinData : Any, MessageData : Any>(
private val socket: PhoenixSocket,
private val dispatcher: CoroutineDispatcher,
private val debugRepository: IDebugRepository,
private val errorBannerStateRepository: IErrorBannerStateRepository,
) {
internal var channel: PhoenixChannel? = null
Expand All @@ -66,6 +70,9 @@ internal class AsymmetricChannelOwner<JoinData : Any, MessageData : Any>(
val errorMessage =
if (rawPayload != null) {
try {
CoroutineScope(dispatcher).launch {
debugRepository.setChannelSuccess(spec.topic)
}
return ApiResult.Ok(parse(rawPayload))
} catch (e: IllegalArgumentException) {
"Failed to parse ${message.subject} channel message: ${e.message}"
Expand Down Expand Up @@ -119,9 +126,17 @@ internal class AsymmetricChannelOwner<JoinData : Any, MessageData : Any>(
}
channel.onFailure {
handleResult(ApiResult.Error(message = "${SocketError.FAILURE} - $it"))
CoroutineScope(dispatcher).launch {
debugRepository.clearChannelStatus(spec.topic)
}
}

channel.onDetach { message -> println("leaving channel ${message.subject}") }
channel.onDetach { message ->
println("leaving channel ${message.subject}")
CoroutineScope(dispatcher).launch {
debugRepository.clearChannelStatus(spec.topic)
}
}
channel
.attach()
.receiveAll(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ public interface IAlertsRepository {

internal class AlertsRepository(
socket: PhoenixSocket,
debugRepository: IDebugRepository,
errorBannerStateRepository: IErrorBannerStateRepository,
ioDispatcher: CoroutineDispatcher,
) : IAlertsRepository, KoinComponent {
private val channelOwner =
ChannelOwner<AlertsStreamDataResponse>(socket, ioDispatcher, errorBannerStateRepository)
ChannelOwner<AlertsStreamDataResponse>(
socket,
ioDispatcher,
debugRepository,
errorBannerStateRepository,
)
internal var channel: PhoenixChannel? by channelOwner::channel

override fun connect(onReceive: (ApiResult<AlertsStreamDataResponse>) -> Unit) {
Expand Down
Loading
Loading