Skip to content

Commit

Permalink
Revert "feat(ErrorBanner): offline banner (#478)"
Browse files Browse the repository at this point in the history
This reverts commit 10d2bad.
  • Loading branch information
KaylaBrady authored Oct 22, 2024
1 parent 10d2bad commit bf5b059
Show file tree
Hide file tree
Showing 18 changed files with 15 additions and 209 deletions.
2 changes: 0 additions & 2 deletions androidApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".MainApplication"
Expand Down
4 changes: 0 additions & 4 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
6E04E32F2BE95A8D006F8131 /* NearbyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E04E32E2BE95A8D006F8131 /* NearbyViewModel.swift */; };
6E20278E2BD989630037554F /* DummyTestAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E20278D2BD989630037554F /* DummyTestAppView.swift */; };
6E2027902BD989AC0037554F /* ProductionAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E20278F2BD989AC0037554F /* ProductionAppView.swift */; };
6E2D6CA12CC2EDD700959605 /* ErrorBannerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2D6CA02CC2EDD700959605 /* ErrorBannerViewModelTests.swift */; };
6E35D4CE2B72C74500A2BF95 /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = 6E35D4CD2B72C74500A2BF95 /* MapboxMaps */; };
6E35D4D02B72C7B700A2BF95 /* HomeMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E35D4CF2B72C7B700A2BF95 /* HomeMapView.swift */; };
6E35D4D32B72CD3900A2BF95 /* HomeMapViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E35D4D22B72CD3900A2BF95 /* HomeMapViewTests.swift */; };
Expand Down Expand Up @@ -250,7 +249,6 @@
6E04E32E2BE95A8D006F8131 /* NearbyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbyViewModel.swift; sourceTree = "<group>"; };
6E20278D2BD989630037554F /* DummyTestAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyTestAppView.swift; sourceTree = "<group>"; };
6E20278F2BD989AC0037554F /* ProductionAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductionAppView.swift; sourceTree = "<group>"; };
6E2D6CA02CC2EDD700959605 /* ErrorBannerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorBannerViewModelTests.swift; sourceTree = "<group>"; };
6E35D4CF2B72C7B700A2BF95 /* HomeMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMapView.swift; sourceTree = "<group>"; };
6E35D4D22B72CD3900A2BF95 /* HomeMapViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMapViewTests.swift; sourceTree = "<group>"; };
6E3C8D7D2C11FDA80059C28C /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -596,7 +594,6 @@
isa = PBXGroup;
children = (
6EFEE42A2BEC100100810319 /* NearbyViewModelTests.swift */,
6E2D6CA02CC2EDD700959605 /* ErrorBannerViewModelTests.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand Down Expand Up @@ -1295,7 +1292,6 @@
8C5F47662C40842200FB71DA /* TripDetailsStopViewTests.swift in Sources */,
8CDF2C342BE9357E007FC912 /* OptionalNavigationLinkTests.swift in Sources */,
8CA1FB772BF813F500384658 /* TripDetailsStopListSplitViewTests.swift in Sources */,
6E2D6CA12CC2EDD700959605 /* ErrorBannerViewModelTests.swift in Sources */,
8CB823DB2BC5F053002C87E0 /* StopDetailsRoutesViewTests.swift in Sources */,
6E4EACFC2B7A82AC0011AB8B /* MockLocationFetcher.swift in Sources */,
8CE36C932CADEDD300D77F22 /* FetchApiTests.swift in Sources */,
Expand Down
6 changes: 0 additions & 6 deletions iosApp/iosApp/ComponentViews/ErrorBanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ struct ErrorBanner: View {
}
.frame(minHeight: minHeight)
}
case .networkError:
ErrorCard { HStack {
Image(systemName: "wifi.slash")
Text("Unable to connect")
Spacer()
}}
case nil:
// for some reason, .collect on an EmptyView doesn't work
EmptyView()
Expand Down
8 changes: 4 additions & 4 deletions iosApp/iosApp/ComponentViews/ErrorCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@

import SwiftUI

struct ErrorCard<Content: View>: View {
@ViewBuilder let details: Content
struct ErrorCard: View {
var details: Text
var button: (() -> AnyView)?

init(_ details: () -> Content) {
init(_ details: () -> Text) {
self.details = details()
button = nil
}

init(details: Content, button: (() -> AnyView)? = nil) {
init(details: Text, button: (() -> AnyView)? = nil) {
self.details = details
self.button = button
}
Expand Down
3 changes: 0 additions & 3 deletions iosApp/iosApp/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,6 @@
},
"trains" : {
"comment" : "trains"
},
"Unable to connect" : {

},
"Unruly Passenger" : {
"comment" : "Possible alert cause"
Expand Down
15 changes: 3 additions & 12 deletions iosApp/iosApp/ViewModels/ErrorBannerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,19 @@ class ErrorBannerViewModel: ObservableObject {
@Published
var loadingWhenPredictionsStale: Bool

// option for testing
var skipListeningForStateChanges = false

init(
errorRepository: IErrorBannerStateRepository = RepositoryDI().errorBanner,
initialLoadingWhenPredictionsStale: Bool = false,
skipListeningForStateChanges: Bool = false
initialLoadingWhenPredictionsStale: Bool = false
) {
self.errorRepository = errorRepository
loadingWhenPredictionsStale = initialLoadingWhenPredictionsStale
errorState = self.errorRepository.state.value
self.skipListeningForStateChanges = skipListeningForStateChanges
}

@MainActor
func activate() async {
errorRepository.subscribeToNetworkStatusChanges()

if !skipListeningForStateChanges {
for await errorState in errorRepository.state {
self.errorState = errorState
}
for await errorState in errorRepository.state {
self.errorState = errorState
}
}

Expand Down
7 changes: 1 addition & 6 deletions iosApp/iosAppTests/Utils/FetchApiTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,7 @@ final class FetchApiTests: XCTestCase {
onRefreshAfterError: { expRefresh.fulfill() }
)
XCTAssertNotNil(errorBannerRepo.state.value)

if let action = errorBannerRepo.state.value?.action {
action()
} else {
XCTFail("data error missing action")
}
errorBannerRepo.state.value?.action()
await fulfillment(of: [expRefresh], timeout: 1)
}

Expand Down
26 changes: 0 additions & 26 deletions iosApp/iosAppTests/ViewModels/ErrorBannerViewModelTests.swift

This file was deleted.

8 changes: 0 additions & 8 deletions iosApp/iosAppTests/Views/ErrorBannerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,6 @@ final class ErrorBannerTests: XCTestCase {
wait(for: [callsAction], timeout: 1)
}

@MainActor func testWhenNetworkError() throws {
let sut = ErrorBanner(.init(
errorRepository: MockErrorBannerStateRepository(state: .NetworkError()),
initialLoadingWhenPredictionsStale: true
))
XCTAssertNotNil(try sut.inspect().find(text: "Unable to connect"))
}

@MainActor func testLoadingWhenPredictionsStale() throws {
let sut = ErrorBanner(.init(
errorRepository: MockErrorBannerStateRepository(state: .StalePredictions(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package com.mbta.tid.mbta_app

import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor
import com.mbta.tid.mbta_app.network.NetworkConnectivityMonitor
import com.mbta.tid.mbta_app.utils.AndroidSystemPaths
import com.mbta.tid.mbta_app.utils.SystemPaths
import org.koin.dsl.module

fun platformModule() = module {
includes(
module { single { createDataStore(get()) } },
module { single<SystemPaths> { AndroidSystemPaths(get()) } },
module { single<INetworkConnectivityMonitor> { NetworkConnectivityMonitor(get()) } }
module { single<SystemPaths> { AndroidSystemPaths(get()) } }
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class RepositoryDI : IRepositories, KoinComponent {
class RealRepositories : IRepositories {
// initialize repositories with platform-specific dependencies as null.
// instantiate the real repositories in makeNativeModule

override val alerts = null
override val appCheck = null
override val config = ConfigRepository()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import kotlinx.datetime.Instant

sealed class ErrorBannerState {
/** What to do when the button in the error banner is pressed */
abstract val action: (() -> Unit)?
abstract val action: () -> Unit

data class StalePredictions(val lastUpdated: Instant, override val action: () -> Unit) :
ErrorBannerState() {
fun minutesAgo() = (Clock.System.now() - lastUpdated).inWholeMinutes
}

data class DataError(override val action: () -> Unit) : ErrorBannerState()

data class NetworkError(override val action: (() -> Unit)?) : ErrorBannerState()
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
package com.mbta.tid.mbta_app.repositories

import com.mbta.tid.mbta_app.model.ErrorBannerState
import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

sealed class NetworkStatus {
data object Connected : NetworkStatus()

data object Disconnected : NetworkStatus()
}

abstract class IErrorBannerStateRepository(initialState: ErrorBannerState? = null) : KoinComponent {

private val networkConnectivityMonitor: INetworkConnectivityMonitor by inject()

/*
Registers platform-specific observer of network status changes.
*/
open fun subscribeToNetworkStatusChanges() {
this.networkConnectivityMonitor.registerListener(
onNetworkAvailable = { setNetworkStatus(NetworkStatus.Connected) },
onNetworkLost = { setNetworkStatus(NetworkStatus.Disconnected) }
)
}

abstract class IErrorBannerStateRepository
protected constructor(initialState: ErrorBannerState? = null) {
protected val flow = MutableStateFlow(initialState)
val state = flow.asStateFlow()

private var networkStatus: NetworkStatus? = null

private var predictionsStale: ErrorBannerState.StalePredictions? = null
private val dataErrors = mutableMapOf<String, ErrorBannerState.DataError>()

protected open fun updateState() {
flow.value =
when {
networkStatus == NetworkStatus.Disconnected -> ErrorBannerState.NetworkError(null)
dataErrors.isNotEmpty() ->
// encapsulate all the different error actions within one error
ErrorBannerState.DataError { dataErrors.values.forEach { it.action() } }
Expand All @@ -64,11 +41,6 @@ abstract class IErrorBannerStateRepository(initialState: ErrorBannerState? = nul
updateState()
}

private fun setNetworkStatus(newStatus: NetworkStatus) {
networkStatus = newStatus
updateState()
}

fun setDataError(key: String, action: () -> Unit) {
dataErrors[key] = ErrorBannerState.DataError(action)
updateState()
Expand All @@ -88,15 +60,8 @@ abstract class IErrorBannerStateRepository(initialState: ErrorBannerState? = nul

class ErrorBannerStateRepository : IErrorBannerStateRepository(), KoinComponent

class MockErrorBannerStateRepository(
state: ErrorBannerState? = null,
onSubscribeToNetworkChanges: (() -> Unit)? = null
) : IErrorBannerStateRepository(state) {
private val onSubscribeToNetworkChanges = onSubscribeToNetworkChanges
class MockErrorBannerStateRepository(state: ErrorBannerState? = null) :
IErrorBannerStateRepository(state) {
val mutableFlow
get() = flow

override fun subscribeToNetworkStatusChanges() {
onSubscribeToNetworkChanges?.invoke()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package com.mbta.tid.mbta_app.repositories

import com.mbta.tid.mbta_app.model.ErrorBannerState
import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor
import dev.mokkery.MockMode
import dev.mokkery.matcher.any
import dev.mokkery.mock
import dev.mokkery.verify
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
Expand All @@ -19,13 +13,8 @@ import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.dsl.module

class ErrorBannerStateRepositoryTest {
@AfterTest fun `stop koin`() = run { stopKoin() }

@Test
fun `initial state is null`() = runBlocking {
val repo = ErrorBannerStateRepository()
Expand Down Expand Up @@ -123,18 +112,4 @@ class ErrorBannerStateRepositoryTest {

assertNull(channel.receive())
}

@Test
fun `subscribe to connectivity changes`() {

val mockNetworkMonitor = mock<INetworkConnectivityMonitor>(MockMode.autofill)

startKoin { modules(module { single<INetworkConnectivityMonitor> { mockNetworkMonitor } }) }

val repo = ErrorBannerStateRepository()

repo.subscribeToNetworkStatusChanges()

verify { mockNetworkMonitor.registerListener(any(), any()) }
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package com.mbta.tid.mbta_app

import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor
import com.mbta.tid.mbta_app.network.NetworkConnectivityMonitor
import com.mbta.tid.mbta_app.utils.IOSSystemPaths
import com.mbta.tid.mbta_app.utils.SystemPaths
import org.koin.dsl.module

fun platformModule() = module {
includes(
module { single { createDataStore() } },
module { single<SystemPaths> { IOSSystemPaths() } },
module { single<INetworkConnectivityMonitor> { NetworkConnectivityMonitor() } }
module { single<SystemPaths> { IOSSystemPaths() } }
)
}
Loading

0 comments on commit bf5b059

Please sign in to comment.