Skip to content
Draft
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 @@ -26,9 +26,14 @@ import org.phoenixframework.Socket
// expect val appVariant: AppVariant

class MainApplication : Application() {
private val socket = Socket(appVariant.socketUrl, decode = ::decodeMessage)

override fun onCreate() {
val locale = super.applicationContext.resources.getString(R.string.current_locale)
val socket =
Socket(
appVariant.socketUrl,
params = mapOf("locale" to locale),
decode = ::decodeMessage,
)
super.onCreate()
initKoin(
appVariant,
Expand Down
3 changes: 2 additions & 1 deletion iosApp/iosApp/ProductionAppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ struct ProductionAppView: View {
}

private static func initSocket() -> PhoenixSocket {
let socket = Socket(appVariant.socketUrl)
let locale = NSLocalizedString("key/current_locale", comment: "")
let socket = Socket(appVariant.socketUrl, paramsClosure: { ["locale": locale] })

// decreasing default from 5s
socket.reconnectAfter = { tries in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.mbta.tid.mbta_app.cache.MockKeyedCache
import com.mbta.tid.mbta_app.cache.ScheduleCache
import com.mbta.tid.mbta_app.model.AppVersion
import com.mbta.tid.mbta_app.model.ObjectCollectionBuilder
import com.mbta.tid.mbta_app.model.response.AlertsStreamDataResponse
import com.mbta.tid.mbta_app.model.response.AlertsStreamUpdateResponse
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.model.response.GlobalResponse
import com.mbta.tid.mbta_app.model.response.NearbyResponse
Expand Down Expand Up @@ -232,7 +232,7 @@ public class MockRepositories : IRepositories {
selectedTripId: String? = null,
selectedVehicleId: String? = null,
) {
alerts = MockAlertsRepository(AlertsStreamDataResponse(objects))
alerts = MockAlertsRepository(AlertsStreamUpdateResponse(objects))
global = MockGlobalRepository(GlobalResponse(objects))
nearby = MockNearbyRepository(NearbyResponse(objects))
predictions =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal constructor(
@SerialName("informed_entity") val informedEntity: List<InformedEntity>,
val lifecycle: Lifecycle,
val severity: Int,
val summaries: List<AlertSummaryEntity>? = null,
@SerialName("updated_at") val updatedAt: EasternTimeInstant,
// This field is not parsed from the Alert object from the backend, it is injected from
// global data in the AlertsUsecase if any informed entities apply to a facility.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.mbta.tid.mbta_app.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
public data class AlertSummaryEntity(
@SerialName("alert_id") val alertId: String,
@SerialName("route_id") val routeId: String?,
@SerialName("stop_id") val stopId: String?,
@SerialName("trip_id") val tripId: String?,
@SerialName("direction_id") val directionId: Int?,
val summary: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ private constructor(
public var header: String? = null
public var lifecycle: Alert.Lifecycle = Alert.Lifecycle.New
public var severity: Int = 0

public var summaries: List<AlertSummaryEntity>? = null
public var updatedAt: EasternTimeInstant =
EasternTimeInstant(Instant.fromEpochMilliseconds(0))
public var facilities: Map<String, Facility>? = null
Expand Down Expand Up @@ -153,6 +155,7 @@ private constructor(
informedEntity,
lifecycle,
severity,
summaries,
updatedAt,
facilities,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ import com.mbta.tid.mbta_app.model.Alert
import com.mbta.tid.mbta_app.model.ObjectCollectionBuilder
import kotlinx.serialization.Serializable

@Serializable
public data class AlertsStreamUpdateResponse(
internal val remove: List<String>,
internal val update: Map<String, Alert>,
) {
public constructor(objects: ObjectCollectionBuilder) : this(emptyList(), objects.alerts.toMap())

public fun mergeInto(alertsData: AlertsStreamDataResponse?): AlertsStreamDataResponse =
alertsData?.let {
AlertsStreamDataResponse(
it.alerts.filter { (key, _) -> !remove.contains(key) } + update
)
} ?: AlertsStreamDataResponse(update)
}

@Serializable
public data class AlertsStreamDataResponse(internal val alerts: Map<String, Alert>) {
public constructor(objects: ObjectCollectionBuilder) : this(objects.alerts.toMap())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.mbta.tid.mbta_app.phoenix

import com.mbta.tid.mbta_app.json
import com.mbta.tid.mbta_app.model.response.AlertsStreamDataResponse
import com.mbta.tid.mbta_app.model.response.AlertsStreamUpdateResponse

internal object AlertsChannel : ChannelSpec {
override val topic = "alerts:v2"
override val topic = "alerts:v3"

override val updateEvent = "stream_data"

override val params = emptyMap<String, Any>()

@Throws(IllegalArgumentException::class)
fun parseMessage(payload: String): AlertsStreamDataResponse {
fun parseMessage(payload: String): AlertsStreamUpdateResponse {
return json.decodeFromString(payload)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.mbta.tid.mbta_app.repositories

import co.touchlab.skie.configuration.annotations.DefaultArgumentInterop
import com.mbta.tid.mbta_app.model.response.AlertsStreamDataResponse
import com.mbta.tid.mbta_app.model.response.AlertsStreamUpdateResponse
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.network.PhoenixChannel
import com.mbta.tid.mbta_app.network.PhoenixSocket
Expand All @@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import org.koin.core.component.KoinComponent

public interface IAlertsRepository {
public fun connect(onReceive: (ApiResult<AlertsStreamDataResponse>) -> Unit)
public fun connect(onReceive: (ApiResult<AlertsStreamUpdateResponse>) -> Unit)

public fun disconnect()
}
Expand All @@ -23,21 +23,21 @@ internal class AlertsRepository(
ioDispatcher: CoroutineDispatcher,
) : IAlertsRepository, KoinComponent {
private val channelOwner =
ChannelOwner<AlertsStreamDataResponse>(
ChannelOwner<AlertsStreamUpdateResponse>(
socket,
ioDispatcher,
debugRepository,
errorBannerStateRepository,
)
internal var channel: PhoenixChannel? by channelOwner::channel

override fun connect(onReceive: (ApiResult<AlertsStreamDataResponse>) -> Unit) {
override fun connect(onReceive: (ApiResult<AlertsStreamUpdateResponse>) -> Unit) {
channelOwner.connect(
AlertsChannel,
AlertsChannel::parseMessage,
{
when (it) {
is ApiResult.Ok -> println("Received ${it.data.alerts.size} alerts")
is ApiResult.Ok -> println("Received ${it.data.update.size} alerts")
else -> {}
}
onReceive(it)
Expand All @@ -54,20 +54,20 @@ internal class AlertsRepository(
public class MockAlertsRepository
@DefaultArgumentInterop.Enabled
internal constructor(
private val result: ApiResult<AlertsStreamDataResponse>,
private val result: ApiResult<AlertsStreamUpdateResponse>,
private val onConnect: () -> Unit = {},
private val onDisconnect: () -> Unit = {},
) : IAlertsRepository {
@DefaultArgumentInterop.Enabled
public constructor(
response: AlertsStreamDataResponse = AlertsStreamDataResponse(emptyMap()),
response: AlertsStreamUpdateResponse = AlertsStreamUpdateResponse(emptyList(), emptyMap()),
onConnect: () -> Unit = {},
onDisconnect: () -> Unit = {},
) : this(ApiResult.Ok(response), onConnect, onDisconnect)

private var receiveCallback: ((ApiResult<AlertsStreamDataResponse>) -> Unit)? = null
private var receiveCallback: ((ApiResult<AlertsStreamUpdateResponse>) -> Unit)? = null

override fun connect(onReceive: (ApiResult<AlertsStreamDataResponse>) -> Unit) {
override fun connect(onReceive: (ApiResult<AlertsStreamUpdateResponse>) -> Unit) {
receiveCallback = onReceive
onConnect()
onReceive(result)
Expand All @@ -77,7 +77,7 @@ internal constructor(
onDisconnect()
}

internal fun receiveResult(result: ApiResult<AlertsStreamDataResponse>) {
internal fun receiveResult(result: ApiResult<AlertsStreamUpdateResponse>) {
receiveCallback?.let { it(result) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.mbta.tid.mbta_app.usecases

import co.touchlab.skie.configuration.annotations.DefaultArgumentInterop
import com.mbta.tid.mbta_app.model.response.AlertsStreamDataResponse
import com.mbta.tid.mbta_app.model.response.AlertsStreamUpdateResponse
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.repositories.IAlertsRepository
import com.mbta.tid.mbta_app.repositories.IGlobalRepository
Expand All @@ -21,17 +22,22 @@ constructor(
private val globalUpdateDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : KoinComponent {

private var lastOkResult: ApiResult.Ok<AlertsStreamDataResponse>? = null
private var currentAlerts: AlertsStreamDataResponse? = null

private var globalState = globalRepository.state
private var globalUpdateJob: Job? = null

public fun connect(onReceive: (ApiResult<AlertsStreamDataResponse>) -> Unit) {
fun injectAndReceive(result: ApiResult<AlertsStreamDataResponse>) {
fun injectAndReceive(result: ApiResult<AlertsStreamUpdateResponse>) {
val injectedResult =
if (result is ApiResult.Ok) {
lastOkResult = result
result.copy(result.data.injectFacilities(globalState.value))
} else result
when (result) {
is ApiResult.Ok ->
ApiResult.Ok(
result.data.mergeInto(currentAlerts).injectFacilities(globalState.value)
)

is ApiResult.Error -> ApiResult.Error(result.code, result.message)
}
onReceive(injectedResult)
}
alertsRepository.connect(::injectAndReceive)
Expand All @@ -40,8 +46,8 @@ constructor(
globalUpdateJob =
CoroutineScope(globalUpdateDispatcher).launch {
globalState.collect { global ->
lastOkResult?.let { result ->
onReceive(result.copy(result.data.injectFacilities(global)))
currentAlerts?.let { alerts ->
onReceive(ApiResult.Ok(alerts.injectFacilities(global)))
}
}
}
Expand Down
Loading