android: stabilize DI graph and production api config
Some checks are pending
CI / test (push) Has started running
Some checks are pending
CI / test (push) Has started running
This commit is contained in:
@@ -57,3 +57,10 @@
|
||||
- Added unit test for realtime event parsing (`RealtimeEventParserTest`).
|
||||
- Added DAO test (`ChatDaoTest`) using in-memory Room + Robolectric.
|
||||
- Updated Android checklist status in `docs/android-checklist.md`.
|
||||
|
||||
### Step 10 - Build stabilization fixes
|
||||
- Switched Android API base URL to `https://chat.daemonlord.ru/`.
|
||||
- Added cleartext traffic flag in manifest for local/dev compatibility.
|
||||
- Fixed Hilt dependency cycle by separating refresh `AuthApiService` with a dedicated qualifier.
|
||||
- Added `CoroutineDispatcher` DI provider and qualifier for repositories.
|
||||
- Fixed Material3 experimental API opt-in and removed deprecated `StateFlow.distinctUntilChanged()` usage.
|
||||
|
||||
@@ -17,7 +17,7 @@ android {
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
buildConfigField("String", "API_BASE_URL", "\"http://10.0.2.2:8000/\"")
|
||||
buildConfigField("String", "API_BASE_URL", "\"https://chat.daemonlord.ru/\"")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
<?xml version=1.0 encoding=utf-8?>
|
||||
<manifest xmlns:android=http://schemas.android.com/apk/res/android>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name=android.permission.INTERNET />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=.MessengerApplication
|
||||
android:allowBackup=true
|
||||
android:icon=@android:drawable/sym_def_app_icon
|
||||
android:label=@string/app_name
|
||||
android:roundIcon=@android:drawable/sym_def_app_icon
|
||||
android:supportsRtl=true
|
||||
android:theme=@android:style/Theme.Material.Light.NoActionBar>
|
||||
android:name=".MessengerApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@android:drawable/sym_def_app_icon"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@android:drawable/sym_def_app_icon"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar">
|
||||
<activity
|
||||
android:name=.MainActivity
|
||||
android:exported=true>
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name=android.intent.action.MAIN />
|
||||
<category android:name=android.intent.category.LAUNCHER />
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
@@ -9,6 +9,7 @@ import ru.daemonlord.messenger.core.token.TokenBundle
|
||||
import ru.daemonlord.messenger.core.token.TokenRepository
|
||||
import ru.daemonlord.messenger.data.auth.api.AuthApiService
|
||||
import ru.daemonlord.messenger.data.auth.dto.RefreshTokenRequestDto
|
||||
import ru.daemonlord.messenger.di.RefreshAuthApi
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -16,6 +17,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class TokenRefreshAuthenticator @Inject constructor(
|
||||
private val tokenRepository: TokenRepository,
|
||||
@RefreshAuthApi
|
||||
private val refreshAuthApiService: AuthApiService,
|
||||
) : Authenticator {
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.daemonlord.messenger.data.auth.repository
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import ru.daemonlord.messenger.core.token.TokenBundle
|
||||
@@ -10,6 +9,7 @@ import ru.daemonlord.messenger.data.auth.api.AuthApiService
|
||||
import ru.daemonlord.messenger.data.auth.dto.AuthUserDto
|
||||
import ru.daemonlord.messenger.data.auth.dto.LoginRequestDto
|
||||
import ru.daemonlord.messenger.data.auth.dto.RefreshTokenRequestDto
|
||||
import ru.daemonlord.messenger.di.IoDispatcher
|
||||
import ru.daemonlord.messenger.domain.auth.model.AuthUser
|
||||
import ru.daemonlord.messenger.domain.auth.repository.AuthRepository
|
||||
import ru.daemonlord.messenger.domain.common.AppError
|
||||
@@ -22,7 +22,7 @@ import javax.inject.Singleton
|
||||
class NetworkAuthRepository @Inject constructor(
|
||||
private val authApiService: AuthApiService,
|
||||
private val tokenRepository: TokenRepository,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : AuthRepository {
|
||||
|
||||
override suspend fun login(email: String, password: String): AppResult<AuthUser> = withContext(ioDispatcher) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.daemonlord.messenger.data.chat.repository
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
@@ -14,6 +13,7 @@ import ru.daemonlord.messenger.data.chat.local.dao.ChatDao
|
||||
import ru.daemonlord.messenger.data.chat.mapper.toChatEntity
|
||||
import ru.daemonlord.messenger.data.chat.mapper.toDomain
|
||||
import ru.daemonlord.messenger.data.chat.mapper.toUserShortEntityOrNull
|
||||
import ru.daemonlord.messenger.di.IoDispatcher
|
||||
import ru.daemonlord.messenger.domain.chat.model.ChatItem
|
||||
import ru.daemonlord.messenger.domain.chat.repository.ChatRepository
|
||||
import ru.daemonlord.messenger.domain.common.AppError
|
||||
@@ -26,7 +26,7 @@ import javax.inject.Singleton
|
||||
class NetworkChatRepository @Inject constructor(
|
||||
private val chatApiService: ChatApiService,
|
||||
private val chatDao: ChatDao,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ChatRepository {
|
||||
|
||||
override fun observeChats(archived: Boolean): Flow<List<ChatItem>> {
|
||||
|
||||
@@ -4,6 +4,8 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -60,6 +62,7 @@ object NetworkModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@RefreshAuthApi
|
||||
fun provideRefreshApiService(
|
||||
@RefreshClient refreshClient: OkHttpClient,
|
||||
json: Json,
|
||||
@@ -73,6 +76,11 @@ object NetworkModule {
|
||||
.create(AuthApiService::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IoDispatcher
|
||||
fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApiClient(
|
||||
|
||||
@@ -5,3 +5,11 @@ import javax.inject.Qualifier
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class RefreshClient
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class RefreshAuthApi
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class IoDispatcher
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.AssistChipDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Tab
|
||||
@@ -45,6 +46,7 @@ fun ChatListRoute(
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun ChatListScreen(
|
||||
state: ChatListUiState,
|
||||
onTabSelected: (ChatTab) -> Unit,
|
||||
|
||||
@@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -59,7 +58,7 @@ class ChatListViewModel @Inject constructor(
|
||||
.flatMapLatest { tab ->
|
||||
observeChatsUseCase(archived = tab == ChatTab.ARCHIVED)
|
||||
}
|
||||
.combine(searchQuery.distinctUntilChanged()) { chats, query ->
|
||||
.combine(searchQuery) { chats, query ->
|
||||
chats.filterByQuery(query)
|
||||
}
|
||||
.collectLatest { filtered ->
|
||||
|
||||
Reference in New Issue
Block a user