Skip to content

Commit 2c38cdd

Browse files
committed
chore(fix): update network client retry strategies and content wrapper defaults
1 parent 38bbd70 commit 2c38cdd

File tree

20 files changed

+99
-69
lines changed

20 files changed

+99
-69
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,5 @@ fabric.properties
242242
jacoco.exec
243243

244244
.idea/
245+
246+
.kotlin/

android-core/src/main/kotlin/co/anitrend/core/android/compose/design/ContentWrapper.kt

+42-35
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.size
1313
import androidx.compose.material3.CircularProgressIndicator
1414
import androidx.compose.material3.FilledTonalButton
1515
import androidx.compose.material3.MaterialTheme
16+
import androidx.compose.material3.Surface
1617
import androidx.compose.material3.Text
1718
import androidx.compose.runtime.Composable
1819
import androidx.compose.runtime.LaunchedEffect
@@ -74,25 +75,28 @@ private fun LoadingContent(
7475
config: IStateLayoutConfig,
7576
modifier: Modifier = Modifier,
7677
) {
77-
Column(
78-
modifier = modifier,
79-
verticalArrangement = Arrangement.Center,
80-
horizontalAlignment = Alignment.CenterHorizontally
81-
) {
82-
ContentImage(drawableResource = config.loadingDrawable)
83-
Spacer(modifier = Modifier.height(8.dp))
84-
Row(
85-
horizontalArrangement = Arrangement.spacedBy(16.dp),
86-
verticalAlignment = Alignment.CenterVertically,
78+
Surface {
79+
Column(
80+
modifier = modifier,
81+
verticalArrangement = Arrangement.Center,
82+
horizontalAlignment = Alignment.CenterHorizontally
8783
) {
88-
CircularProgressIndicator(
89-
modifier = Modifier.size(24.dp).align(alignment = Alignment.CenterVertically),
90-
color = MaterialTheme.colorScheme.surfaceVariant,
91-
strokeWidth = 4.dp,
92-
trackColor = MaterialTheme.colorScheme.secondary,
93-
)
94-
config.loadingMessage?.also {
95-
ContentText(text = stringResource(it))
84+
ContentImage(drawableResource = config.loadingDrawable)
85+
Spacer(modifier = Modifier.height(8.dp))
86+
Row(
87+
horizontalArrangement = Arrangement.spacedBy(8.dp),
88+
verticalAlignment = Alignment.CenterVertically,
89+
) {
90+
CircularProgressIndicator(
91+
modifier = Modifier.size(16.dp)
92+
.align(alignment = Alignment.CenterVertically),
93+
color = MaterialTheme.colorScheme.surfaceVariant,
94+
strokeWidth = 2.dp,
95+
trackColor = MaterialTheme.colorScheme.secondary,
96+
)
97+
config.loadingMessage?.also {
98+
ContentText(text = stringResource(it))
99+
}
96100
}
97101
}
98102
}
@@ -106,22 +110,24 @@ private fun ErrorContent(
106110
onClick: suspend () -> Unit,
107111
) {
108112
val scope = rememberCoroutineScope()
109-
Column(
110-
modifier = modifier,
111-
verticalArrangement = Arrangement.Center,
112-
horizontalAlignment = Alignment.CenterHorizontally
113-
) {
114-
ContentImage(drawableResource = config.errorDrawable)
115-
Spacer(modifier = Modifier.height(16.dp))
116-
state.details.message?.also {
117-
ContentText(text = it)
118-
}
119-
config.retryAction?.also {
113+
Surface {
114+
Column(
115+
modifier = modifier,
116+
verticalArrangement = Arrangement.Center,
117+
horizontalAlignment = Alignment.CenterHorizontally
118+
) {
119+
ContentImage(drawableResource = config.errorDrawable)
120120
Spacer(modifier = Modifier.height(16.dp))
121-
FilledTonalButton(
122-
onClick = { scope.launch { onClick() } },
123-
) {
124-
Text(text = stringResource(it))
121+
state.details.message?.also {
122+
ContentText(text = it)
123+
}
124+
config.retryAction?.also {
125+
Spacer(modifier = Modifier.height(16.dp))
126+
FilledTonalButton(
127+
onClick = { scope.launch { onClick() } },
128+
) {
129+
Text(text = stringResource(it))
130+
}
125131
}
126132
}
127133
}
@@ -174,7 +180,8 @@ fun <P: IParam> ContentWrapper(
174180
}
175181

176182

177-
@AniTrendPreview.Mobile
183+
@AniTrendPreview.Light
184+
@AniTrendPreview.Dark
178185
@Composable
179186
private fun ContentWrapperPreview(
180187
@PreviewParameter(provider = ContentWrapperPreviewParameter::class) loadState: LoadState
@@ -187,7 +194,7 @@ private fun ContentWrapperPreview(
187194
defaultMessage = co.anitrend.core.android.R.string.app_controller_message_missing_param,
188195
retryAction = co.anitrend.core.android.R.string.action_share
189196
)
190-
PreviewTheme(wrapInSurface = true) {
197+
PreviewTheme {
191198
when (loadState) {
192199
is LoadState.Error -> ErrorContent(
193200
config = config,

app-core/src/main/kotlin/co/anitrend/core/koin/Modules.kt

+12-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ import co.anitrend.core.coil.client.CoilRequestClient
3535
import co.anitrend.core.coil.fetch.RequestImageFetcher
3636
import co.anitrend.core.coil.mapper.RequestImageMapper
3737
import co.anitrend.data.android.koin.dataModules
38+
import co.anitrend.data.android.network.cache.CacheHelper
3839
import co.anitrend.data.android.network.model.NetworkMessage
40+
import co.anitrend.data.core.extensions.defaultBuilder
3941
import coil.ImageLoader
4042
import coil.decode.GifDecoder
4143
import coil.decode.ImageDecoderDecoder
@@ -45,6 +47,7 @@ import coil.disk.DiskCache
4547
import coil.memory.MemoryCache
4648
import io.wax911.emojify.EmojiManager
4749
import io.wax911.emojify.serializer.kotlinx.KotlinxDeserializer
50+
import okhttp3.CookieJar
4851
import okhttp3.OkHttpClient
4952
import okhttp3.logging.HttpLoggingInterceptor
5053
import org.koin.android.ext.koin.androidContext
@@ -113,9 +116,15 @@ private val configurationModule = module {
113116
val memoryLimit = if (isLowRamDevice) 0.15 else 0.35
114117
val storageController = get<IStorageController>()
115118

116-
val client = get<OkHttpClient.Builder> {
117-
parametersOf(HttpLoggingInterceptor.Level.BASIC)
118-
}.build()
119+
val client = defaultBuilder()
120+
.cookieJar(get<CookieJar>())
121+
.cache(
122+
CacheHelper.createCache(
123+
androidContext(),
124+
"coil-okhttp"
125+
)
126+
)
127+
.build()
119128

120129
val imageCache = storageController.getImageCache(
121130
context = androidContext()

app-data-android/src/main/kotlin/co/anitrend/data/android/controller/strategy/contract/ControllerStrategy.kt

+8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import co.anitrend.arch.request.callback.RequestCallback
2222
import co.anitrend.data.android.network.model.NetworkMessage
2323
import retrofit2.HttpException
2424
import java.net.SocketTimeoutException
25+
import java.net.UnknownHostException
2526

2627
/**
2728
* Contract for controller strategy
@@ -49,6 +50,13 @@ abstract class ControllerStrategy<D> {
4950
cause
5051
)
5152
}
53+
is UnknownHostException -> {
54+
RequestError(
55+
networkMessage.connectivityErrorTittle,
56+
networkMessage.connectivityErrorMessage,
57+
cause
58+
)
59+
}
5260
else -> {
5361
if (cause == null)
5462
RequestError(

app-data-android/src/main/kotlin/co/anitrend/data/android/controller/strategy/policy/OfflineStrategy.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class OfflineStrategy<D> private constructor(
4444
): D? {
4545
runCatching {
4646
block()
47-
}.onSuccess {result ->
47+
}.onSuccess { result ->
4848
callback.recordSuccess()
4949
return result
5050
}.onFailure { exception ->

app-data-android/src/main/kotlin/co/anitrend/data/android/network/client/DeferrableNetworkClient.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ abstract class DeferrableNetworkClient<T> : AbstractNetworkClient<Async<Response
5555
*/
5656
override fun defaultShouldRetry(exception: Throwable) = when (exception) {
5757
is HttpException -> exception.code() == 429
58-
is SocketTimeoutException,
59-
is IOException -> true
58+
is SocketTimeoutException -> true
6059
else -> false
6160
}
6261

app-data-android/src/main/kotlin/co/anitrend/data/android/network/client/OkHttpCallNetworkClient.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ abstract class OkHttpCallNetworkClient : AbstractNetworkClient<Call, Response>()
5252
*/
5353
override fun defaultShouldRetry(exception: Throwable) = when (exception) {
5454
is OkHttpException -> exception.code == 429
55-
is SocketTimeoutException,
56-
is IOException -> true
55+
is SocketTimeoutException -> true
5756
else -> false
5857
}
5958

@@ -118,4 +117,4 @@ abstract class OkHttpCallNetworkClient : AbstractNetworkClient<Call, Response>()
118117
maxAttempts,
119118
shouldRetry
120119
).bodyOrThrow()
121-
}
120+
}

app-data-android/src/main/kotlin/co/anitrend/data/android/network/client/RetrofitCallNetworkClient.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ internal abstract class RetrofitCallNetworkClient<T> : AbstractNetworkClient<Cal
4646
*/
4747
override fun defaultShouldRetry(exception: Throwable) = when (exception) {
4848
is HttpException -> exception.code() == 429
49-
is SocketTimeoutException,
50-
is IOException -> true
49+
is SocketTimeoutException -> true
5150
else -> false
5251
}
5352

@@ -112,4 +111,4 @@ internal abstract class RetrofitCallNetworkClient<T> : AbstractNetworkClient<Cal
112111
maxAttempts,
113112
shouldRetry
114113
).bodyOrThrow()
115-
}
114+
}

app-data-core/src/test/kotlin/co/anitrend/data/core/extensions/CommonExtensionsKtTest.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,31 @@ import org.junit.jupiter.api.Assertions.assertEquals
2222
import kotlin.test.Test
2323

2424
class CommonExtensionsKtTest {
25-
26-
//@Test
25+
@Test
2726
fun `test hash for word action`() {
2827
val given = "Action"
2928
val actual = given.toHashId()
3029
val expected = 1955883606L
3130
assertEquals(expected, actual)
3231
}
3332

34-
//@Test
33+
@Test
3534
fun `test hash for word adventure`() {
3635
val given = "Adventure"
3736
val actual = given.toHashId()
3837
val expected = 1309873904L
3938
assertEquals(expected, actual)
4039
}
4140

42-
//@Test
41+
@Test
4342
fun `test hash for word comedy`() {
4443
val given = "Comedy"
4544
val actual = given.toHashId()
4645
val expected = 2024011449L
4746
assertEquals(expected, actual)
4847
}
4948

50-
//@Test
49+
@Test
5150
fun `test hash for word drama`() {
5251
val given = "Drama"
5352
val actual = given.toHashId()
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
#edgeHost="https://api.anitrend.docker.localhost/graphql/"
2-
edgeHost="https://api.anitrend.co/graphql"
1+
edgeHost="https://api.anitrend.co"

app-data-edge/src/main/kotlin/co/anitrend/data/edge/core/api/factory/EdgeApiFactory.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ internal class EdgeApiFactory(
4545
}
4646

4747
companion object {
48-
const val BASE_ENDPOINT_PATH = "/graphql/"
48+
const val BASE_ENDPOINT_PATH = "/graphql"
4949
}
5050
}

app-data-edge/src/main/kotlin/co/anitrend/data/edge/genre/datasource/EdgeGenreLocalSource.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package co.anitrend.data.edge.genre.datasource
22

33
import androidx.room.Dao
44
import androidx.room.Query
5-
import co.anitrend.data.android.source.AbstractLocalSource
5+
import co.anitrend.data.android.source.local.AbstractLocalSource
66
import co.anitrend.data.edge.genre.entity.EdgeGenreEntity
77

88
@Dao

app-data/graphql.config.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ extensions:
88
user-agent: JS GraphQL
99
introspect: true
1010
AniTrend:
11-
url: https://graphql.anitrend.co/graphql/
11+
url: https://api.anitrend.co/graphql
1212
headers:
1313
User-Agent: "JS GraphQL"
1414
Accept: "*/*"
1515
HOST: "graphql.anitrend.co"
1616
Accept-Encoding: "gzip, deflate, br"
17-
Accept-Language: "en-GB,en-US;q=0.9,en;q=0.8"
1817
Content-Type: "application/json"
1918
introspect: true

app-data/src/main/kotlin/co/anitrend/data/auth/datasource/local/AuthLocalSource.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal abstract class AuthLocalSource : AbstractLocalSource<AuthEntity>() {
3939
delete from auth
4040
where user_id = :userId
4141
""")
42-
abstract fun clearByUserId(userId: Long)
42+
abstract suspend fun clearByUserId(userId: Long)
4343

4444
@Query("""
4545
select * from auth

common-media-ui/src/main/kotlin/co/anitrend/common/media/ui/compose/item/MediaCarouselItem.kt

-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ fun MediaCarouselItem(
112112
items(
113113
count = carouselItems.size,
114114
key = { carouselItems[it].hashCode() },
115-
contentType = { carouselItems[it].carouselType },
116115
) { index ->
117116
val carouselItem = carouselItems[index]
118117
CarouselHeader(

common-media-ui/src/main/kotlin/co/anitrend/common/media/ui/compose/item/MediaCompactItem.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ fun MediaCompactItemList(
150150
items(
151151
count = mediaItems.size,
152152
key = { mediaItems[it].hashCode() },
153-
contentType = { mediaItems[it].category.type }
153+
contentType = { mediaItems[it].category }
154154
) { index ->
155155
MediaCompactItem(
156156
media = mediaItems[index],
@@ -185,7 +185,13 @@ private fun MediaCompactItemPreview() {
185185
mediaPreferenceData = MediaPreferenceData(
186186
ScoreFormat.POINT_10_DECIMAL,
187187
),
188-
mediaItemClick = {}
188+
mediaItemClick = {},
189+
modifier = Modifier
190+
.padding(8.dp)
191+
.size(
192+
height = 265.dp,
193+
width = 150.dp,
194+
),
189195
)
190196
}
191197
}

feature-media-carousel/src/main/kotlin/co/anitrend/media/carousel/component/content/CarouselContent.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import co.anitrend.navigation.MediaDiscoverRouter
3636
import co.anitrend.navigation.MediaListEditorRouter
3737
import co.anitrend.navigation.MediaRouter
3838
import co.anitrend.navigation.extensions.asNavPayload
39+
import co.anitrend.navigation.extensions.nameOf
3940
import co.anitrend.navigation.extensions.startActivity
4041
import org.koin.androidx.viewmodel.ext.android.viewModel
4142
import timber.log.Timber
@@ -46,7 +47,9 @@ class CarouselContent(
4647
) : AniTrendComposition() {
4748

4849
private val viewModel by viewModel<CarouselViewModel>()
49-
private val param by argument<MediaCarouselRouter.MediaCarouselRouterParam>()
50+
private val param by argument<MediaCarouselRouter.MediaCarouselRouterParam>(
51+
key = nameOf<MediaCarouselRouter.MediaCarouselRouterParam>()
52+
)
5053

5154
private fun paramOrDefault(): MediaCarouselRouter.MediaCarouselRouterParam {
5255
return param ?: MediaCarouselRouter.MediaCarouselRouterParam(
@@ -89,9 +92,9 @@ class CarouselContent(
8992
) = composable(context = inflater.context) {
9093
AniTrendTheme3 {
9194
ContentWrapper(
92-
stateFlow = viewModelState().combinedLoadState,
95+
stateFlow = viewModelState().loadState,
9396
param = paramOrDefault(),
94-
onLoad = viewModelState()::invoke,
97+
onLoad = viewModel::invoke,
9598
onClick = viewModelState()::retry,
9699
) {
97100
CarouselScreen(

feature-media-carousel/src/main/kotlin/co/anitrend/media/carousel/component/viewmodel/CarouselViewModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ import androidx.lifecycle.viewModelScope
2121
import co.anitrend.core.component.viewmodel.AniTrendViewModel
2222
import co.anitrend.domain.carousel.model.CarouselParam
2323
import co.anitrend.media.carousel.component.viewmodel.state.CarouselState
24+
import co.anitrend.navigation.MediaCarouselRouter
2425
import kotlinx.coroutines.launch
2526

2627
class CarouselViewModel(
2728
override val state: CarouselState
2829
) : AniTrendViewModel() {
29-
operator fun invoke(param: CarouselParam.Find) {
30+
operator fun invoke(param: MediaCarouselRouter.MediaCarouselRouterParam) {
3031
viewModelScope.launch {
3132
state(param)
3233
}

feature-media-carousel/src/main/kotlin/co/anitrend/media/carousel/component/viewmodel/state/CarouselState.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ data class CarouselState(
2727
private val interactor: GetCarouselInteractor
2828
) : AniTrendViewModelState<List<MediaCarousel>>() {
2929

30-
operator fun invoke(param: MediaCarouselRouter.MediaCarouselRouterParam) {
30+
suspend operator fun invoke(param: MediaCarouselRouter.MediaCarouselRouterParam) {
3131
val input = CarouselParam.Find(
3232
season = param.season,
3333
seasonYear = param.seasonYear,

0 commit comments

Comments
 (0)