singleton (AuthRepository) repo refactor

This commit is contained in:
solovushka56
2025-12-11 19:29:55 +03:00
parent b74a74f3bd
commit 87bbfcc96c
12 changed files with 104 additions and 44 deletions

View File

@@ -20,6 +20,6 @@ class App : Application() {
super.onCreate()
instance = this
AuthRepository.init(applicationContext)
AuthRepository.getInstance(applicationContext)
}
}

View File

@@ -1,7 +1,10 @@
package ru.myitschool.work.core
object Constants {
const val HOST = "http://10.0.2.2:8080"
// const val HOST = "http://127.0.0.1:8080"
const val AUTH_URL = "/auth"
const val INFO_URL = "/info"
const val BOOKING_URL = "/booking"

View File

@@ -7,42 +7,48 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import ru.myitschool.work.data.source.NetworkDataSource
object AuthRepository {
private const val PREFS_NAME = "auth_prefs"
private const val KEY_CODE = "auth_code"
private const val KEY_NAME = "user_name"
private const val KEY_PHOTO = "user_photo"
class AuthRepository private constructor(context: Context) {
private var _context: Context? = null
companion object {
@Volatile
private var INSTANCE: AuthRepository? = null
fun getInstance(context: Context): AuthRepository {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: AuthRepository(context.applicationContext).also { INSTANCE = it }
}
}
fun clearInstance() {
INSTANCE = null
}
}
private val PREFS_NAME = "auth_prefs"
private val KEY_CODE = "auth_code"
private val KEY_NAME = "user_name"
private val KEY_PHOTO = "user_photo"
private val context: Context = context.applicationContext
private var codeCache: String? = null
private var userCache: UserCache? = null
private val _isAuthorized = MutableStateFlow(false)
val isAuthorized: StateFlow<Boolean> = _isAuthorized.asStateFlow()
fun init(context: Context) {
if (_context == null) {
_context = context.applicationContext
init {
val prefs = context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
codeCache = prefs.getString(KEY_CODE, null)
val name = prefs.getString(KEY_NAME, null)
val photo = prefs.getString(KEY_PHOTO, null)
val prefs = _context!!.getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
codeCache = prefs.getString(KEY_CODE, null)
val name = prefs.getString(KEY_NAME, null)
val photo = prefs.getString(KEY_PHOTO, null)
if (codeCache != null && name != null) {
userCache = UserCache(name, photo)
_isAuthorized.value = true
}
if (codeCache != null && name != null) {
userCache = UserCache(name, photo)
_isAuthorized.value = true
}
}
private fun requireContext(): Context {
return _context ?: throw IllegalStateException(
"AuthRepository not inited"
)
}
private fun getPrefs() = requireContext().getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
private fun getPrefs() = context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
suspend fun checkAndSave(text: String): Result<Boolean> {
return NetworkDataSource.checkAuth(text).onSuccess { success ->

View File

@@ -4,9 +4,10 @@ import ru.myitschool.work.data.model.BookingInfoResponse
import ru.myitschool.work.data.source.AuthException
import ru.myitschool.work.data.source.NetworkDataSource
object BookRepository {
class BookRepository(private val authRepository: AuthRepository) {
suspend fun getAvailableBookings(): Result<BookingInfoResponse> {
val code = AuthRepository.getCurrentCode()
val code = authRepository.getCurrentCode()
return if (code != null) {
NetworkDataSource.getBookingInfo(code)
} else {
@@ -15,7 +16,7 @@ object BookRepository {
}
suspend fun book(date: String, placeId: Int): Result<Unit> {
val code = AuthRepository.getCurrentCode()
val code = authRepository.getCurrentCode()
return if (code != null) {
NetworkDataSource.book(code, date, placeId)
} else {

View File

@@ -4,9 +4,10 @@ import ru.myitschool.work.data.model.UserInfoResponse
import ru.myitschool.work.data.source.AuthException
import ru.myitschool.work.data.source.NetworkDataSource
object MainRepository {
class MainRepository(private val authRepository: AuthRepository) {
suspend fun getUserInfo(): Result<UserInfoResponse> {
val code = AuthRepository.getCurrentCode()
val code = authRepository.getCurrentCode()
return if (code != null) {
NetworkDataSource.getInfo(code)
} else {

View File

@@ -6,7 +6,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@@ -24,8 +26,10 @@ fun AppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController()
) {
// authorized state follow
val isAuthorized by AuthRepository.isAuthorized.collectAsState()
val context = LocalContext.current
val authRepository = remember { AuthRepository.getInstance(context) }
val isAuthorized by authRepository.isAuthorized.collectAsState()
LaunchedEffect(isAuthorized) {
if (isAuthorized) {

View File

@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
@@ -34,7 +35,7 @@ import ru.myitschool.work.ui.nav.MainScreenDestination
@Composable
fun AuthScreen(
viewModel: AuthViewModel = viewModel(),
viewModel: AuthViewModel = viewModel(factory = AuthViewModelFactory(LocalContext.current)),
navController: NavController
) {
val state by viewModel.uiState.collectAsState()

View File

@@ -1,6 +1,8 @@
package ru.myitschool.work.ui.screen.auth
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -12,10 +14,11 @@ import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
private val authRepo = AuthRepository
private val checkAndSaveAuthCodeUseCase = CheckAndSaveAuthCodeUseCase(authRepo)
class AuthViewModel : ViewModel() {
class AuthViewModel(
private val authRepository: AuthRepository,
private val checkAndSaveAuthCodeUseCase: CheckAndSaveAuthCodeUseCase
) : ViewModel() {
private val _uiState = MutableStateFlow<AuthState>(AuthState.Data())
val uiState: StateFlow<AuthState> = _uiState.asStateFlow()
@@ -68,6 +71,18 @@ class AuthViewModel : ViewModel() {
}
}
class AuthViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AuthViewModel::class.java)) {
val authRepository = AuthRepository.getInstance(context)
val useCase = CheckAndSaveAuthCodeUseCase(authRepository)
return AuthViewModel(authRepository, useCase) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
sealed interface AuthAction {
object NavigateToMain : AuthAction
}

View File

@@ -19,13 +19,14 @@ import androidx.navigation.NavController
import ru.myitschool.work.R
import ru.myitschool.work.core.TestIds
import androidx.compose.material3.Icon
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BookScreen(
viewModel: BookViewModel = viewModel(),
viewModel: BookViewModel = viewModel(factory = BookViewModelFactory(LocalContext.current)),
navController: NavController
) {
val state by viewModel.uiState.collectAsState()

View File

@@ -1,6 +1,8 @@
package ru.myitschool.work.ui.screen.book
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -9,14 +11,14 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.data.repo.BookRepository
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
private val bookRepo = BookRepository
class BookViewModel : ViewModel() {
class BookViewModel(private val bookRepo: BookRepository) : ViewModel() {
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
@@ -180,3 +182,15 @@ sealed interface BookAction {
object NavigateBack : BookAction
object NavigateBackWithRefresh : BookAction
}
class BookViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(BookViewModel::class.java)) {
val authRepository = AuthRepository.getInstance(context)
val bookRepository = BookRepository(authRepository)
return BookViewModel(bookRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

View File

@@ -46,7 +46,7 @@ import ru.myitschool.work.ui.nav.BookScreenDestination
@Composable
fun MainScreen(
viewModel: MainViewModel = viewModel(),
viewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current)),
navController: NavController
) {
val state by viewModel.uiState.collectAsState()

View File

@@ -1,6 +1,8 @@
package ru.myitschool.work.ui.screen.main
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -14,10 +16,11 @@ import java.time.format.DateTimeFormatter
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.data.repo.MainRepository
private val authRepo = AuthRepository
private val mainRepo = MainRepository
class MainViewModel : ViewModel() {
class MainViewModel(private val authRepo: AuthRepository,
private val mainRepo: MainRepository)
: ViewModel() {
private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
val uiState: StateFlow<MainState> = _uiState.asStateFlow()
@@ -95,6 +98,17 @@ class MainViewModel : ViewModel() {
}
}
class MainViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
val authRepository = AuthRepository.getInstance(context)
val mainRepository = MainRepository(authRepository)
return MainViewModel(authRepository, mainRepository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
sealed interface MainAction {
object NavigateToAuth : MainAction
object NavigateToBooking : MainAction