forked from Olympic/NTO-2025-Android-TeamTask
singleton (AuthRepository) repo refactor
This commit is contained in:
@@ -20,6 +20,6 @@ class App : Application() {
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
AuthRepository.init(applicationContext)
|
AuthRepository.getInstance(applicationContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package ru.myitschool.work.core
|
package ru.myitschool.work.core
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
|
|
||||||
const val HOST = "http://10.0.2.2:8080"
|
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 AUTH_URL = "/auth"
|
||||||
const val INFO_URL = "/info"
|
const val INFO_URL = "/info"
|
||||||
const val BOOKING_URL = "/booking"
|
const val BOOKING_URL = "/booking"
|
||||||
|
|||||||
@@ -7,42 +7,48 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import ru.myitschool.work.data.source.NetworkDataSource
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
|
||||||
object AuthRepository {
|
class AuthRepository private constructor(context: Context) {
|
||||||
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"
|
|
||||||
|
|
||||||
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 codeCache: String? = null
|
||||||
private var userCache: UserCache? = null
|
private var userCache: UserCache? = null
|
||||||
|
|
||||||
private val _isAuthorized = MutableStateFlow(false)
|
private val _isAuthorized = MutableStateFlow(false)
|
||||||
val isAuthorized: StateFlow<Boolean> = _isAuthorized.asStateFlow()
|
val isAuthorized: StateFlow<Boolean> = _isAuthorized.asStateFlow()
|
||||||
|
|
||||||
fun init(context: Context) {
|
init {
|
||||||
if (_context == null) {
|
val prefs = context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
||||||
_context = context.applicationContext
|
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)
|
if (codeCache != null && name != null) {
|
||||||
codeCache = prefs.getString(KEY_CODE, null)
|
userCache = UserCache(name, photo)
|
||||||
val name = prefs.getString(KEY_NAME, null)
|
_isAuthorized.value = true
|
||||||
val photo = prefs.getString(KEY_PHOTO, null)
|
|
||||||
|
|
||||||
if (codeCache != null && name != null) {
|
|
||||||
userCache = UserCache(name, photo)
|
|
||||||
_isAuthorized.value = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireContext(): Context {
|
private fun getPrefs() = context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
||||||
return _context ?: throw IllegalStateException(
|
|
||||||
"AuthRepository not inited"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPrefs() = requireContext().getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
|
|
||||||
|
|
||||||
suspend fun checkAndSave(text: String): Result<Boolean> {
|
suspend fun checkAndSave(text: String): Result<Boolean> {
|
||||||
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import ru.myitschool.work.data.model.BookingInfoResponse
|
|||||||
import ru.myitschool.work.data.source.AuthException
|
import ru.myitschool.work.data.source.AuthException
|
||||||
import ru.myitschool.work.data.source.NetworkDataSource
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
|
||||||
object BookRepository {
|
class BookRepository(private val authRepository: AuthRepository) {
|
||||||
|
|
||||||
suspend fun getAvailableBookings(): Result<BookingInfoResponse> {
|
suspend fun getAvailableBookings(): Result<BookingInfoResponse> {
|
||||||
val code = AuthRepository.getCurrentCode()
|
val code = authRepository.getCurrentCode()
|
||||||
return if (code != null) {
|
return if (code != null) {
|
||||||
NetworkDataSource.getBookingInfo(code)
|
NetworkDataSource.getBookingInfo(code)
|
||||||
} else {
|
} else {
|
||||||
@@ -15,7 +16,7 @@ object BookRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun book(date: String, placeId: Int): Result<Unit> {
|
suspend fun book(date: String, placeId: Int): Result<Unit> {
|
||||||
val code = AuthRepository.getCurrentCode()
|
val code = authRepository.getCurrentCode()
|
||||||
return if (code != null) {
|
return if (code != null) {
|
||||||
NetworkDataSource.book(code, date, placeId)
|
NetworkDataSource.book(code, date, placeId)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import ru.myitschool.work.data.model.UserInfoResponse
|
|||||||
import ru.myitschool.work.data.source.AuthException
|
import ru.myitschool.work.data.source.AuthException
|
||||||
import ru.myitschool.work.data.source.NetworkDataSource
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
|
||||||
object MainRepository {
|
class MainRepository(private val authRepository: AuthRepository) {
|
||||||
|
|
||||||
suspend fun getUserInfo(): Result<UserInfoResponse> {
|
suspend fun getUserInfo(): Result<UserInfoResponse> {
|
||||||
val code = AuthRepository.getCurrentCode()
|
val code = authRepository.getCurrentCode()
|
||||||
return if (code != null) {
|
return if (code != null) {
|
||||||
NetworkDataSource.getInfo(code)
|
NetworkDataSource.getInfo(code)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
@@ -24,8 +26,10 @@ fun AppNavHost(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
navController: NavHostController = rememberNavController()
|
navController: NavHostController = rememberNavController()
|
||||||
) {
|
) {
|
||||||
// authorized state follow
|
val context = LocalContext.current
|
||||||
val isAuthorized by AuthRepository.isAuthorized.collectAsState()
|
val authRepository = remember { AuthRepository.getInstance(context) }
|
||||||
|
|
||||||
|
val isAuthorized by authRepository.isAuthorized.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(isAuthorized) {
|
LaunchedEffect(isAuthorized) {
|
||||||
if (isAuthorized) {
|
if (isAuthorized) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -34,7 +35,7 @@ import ru.myitschool.work.ui.nav.MainScreenDestination
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AuthScreen(
|
fun AuthScreen(
|
||||||
viewModel: AuthViewModel = viewModel(),
|
viewModel: AuthViewModel = viewModel(factory = AuthViewModelFactory(LocalContext.current)),
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsState()
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ru.myitschool.work.ui.screen.auth
|
package ru.myitschool.work.ui.screen.auth
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -12,10 +14,11 @@ import kotlinx.coroutines.launch
|
|||||||
import ru.myitschool.work.data.repo.AuthRepository
|
import ru.myitschool.work.data.repo.AuthRepository
|
||||||
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
|
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())
|
private val _uiState = MutableStateFlow<AuthState>(AuthState.Data())
|
||||||
val uiState: StateFlow<AuthState> = _uiState.asStateFlow()
|
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 {
|
sealed interface AuthAction {
|
||||||
object NavigateToMain : AuthAction
|
object NavigateToMain : AuthAction
|
||||||
}
|
}
|
||||||
@@ -19,13 +19,14 @@ import androidx.navigation.NavController
|
|||||||
import ru.myitschool.work.R
|
import ru.myitschool.work.R
|
||||||
import ru.myitschool.work.core.TestIds
|
import ru.myitschool.work.core.TestIds
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BookScreen(
|
fun BookScreen(
|
||||||
viewModel: BookViewModel = viewModel(),
|
viewModel: BookViewModel = viewModel(factory = BookViewModelFactory(LocalContext.current)),
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsState()
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ru.myitschool.work.ui.screen.book
|
package ru.myitschool.work.ui.screen.book
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -9,14 +11,14 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.data.repo.AuthRepository
|
||||||
import ru.myitschool.work.data.repo.BookRepository
|
import ru.myitschool.work.data.repo.BookRepository
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
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)
|
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
|
||||||
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
@@ -179,4 +181,16 @@ class BookViewModel : ViewModel() {
|
|||||||
sealed interface BookAction {
|
sealed interface BookAction {
|
||||||
object NavigateBack : BookAction
|
object NavigateBack : BookAction
|
||||||
object NavigateBackWithRefresh : 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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ import ru.myitschool.work.ui.nav.BookScreenDestination
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(
|
||||||
viewModel: MainViewModel = viewModel(),
|
viewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current)),
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val state by viewModel.uiState.collectAsState()
|
val state by viewModel.uiState.collectAsState()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ru.myitschool.work.ui.screen.main
|
package ru.myitschool.work.ui.screen.main
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
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.AuthRepository
|
||||||
import ru.myitschool.work.data.repo.MainRepository
|
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)
|
private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
|
||||||
val uiState: StateFlow<MainState> = _uiState.asStateFlow()
|
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 {
|
sealed interface MainAction {
|
||||||
object NavigateToAuth : MainAction
|
object NavigateToAuth : MainAction
|
||||||
object NavigateToBooking : MainAction
|
object NavigateToBooking : MainAction
|
||||||
|
|||||||
Reference in New Issue
Block a user