WIP manejo de errores y reintentos
This commit is contained in:
parent
761a076e52
commit
003ab3ea5d
@ -40,12 +40,7 @@ class AuthRepositoryImpl @Inject constructor(
|
|||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
|
|
||||||
when(res.code()){
|
emit(Resource.Error(Resource.Error.mapErrorCode(res.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,26 @@ package com.isolaatti.common
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
|
||||||
class ErrorMessageViewModel : ViewModel() {
|
class ErrorMessageViewModel : ViewModel() {
|
||||||
val error: MutableLiveData<Resource.Error.ErrorType> = MutableLiveData()
|
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||||
|
private val _retry: MutableSharedFlow<Boolean> = MutableSharedFlow()
|
||||||
|
val retry: SharedFlow<Boolean> get() = _retry.asSharedFlow()
|
||||||
|
private var handledCount = 0
|
||||||
|
|
||||||
|
suspend fun askRetry() {
|
||||||
|
_retry.emit(false)
|
||||||
|
handledCount = 0
|
||||||
|
_retry.emit(true)
|
||||||
|
}
|
||||||
|
suspend fun handleRetry() {
|
||||||
|
val subscribers = _retry.subscriptionCount.value
|
||||||
|
|
||||||
|
if(handledCount >= subscribers) {
|
||||||
|
_retry.emit(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -20,6 +20,9 @@ import com.isolaatti.home.HomeActivity
|
|||||||
import com.isolaatti.login.LogInActivity
|
import com.isolaatti.login.LogInActivity
|
||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
||||||
@ -27,9 +30,10 @@ abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
|||||||
val errorViewModel: ErrorMessageViewModel by viewModels()
|
val errorViewModel: ErrorMessageViewModel by viewModels()
|
||||||
|
|
||||||
private var snackbarNetworkStatus: Snackbar? = null
|
private var snackbarNetworkStatus: Snackbar? = null
|
||||||
|
private var snackbarError: Snackbar? = null
|
||||||
private var connectionHasBeenLost: Boolean = false
|
private var connectionHasBeenLost: Boolean = false
|
||||||
|
|
||||||
private val errorObserver: Observer<Resource.Error.ErrorType> = Observer {
|
private val errorObserver: Observer<Resource.Error.ErrorType?> = Observer {
|
||||||
when(it) {
|
when(it) {
|
||||||
Resource.Error.ErrorType.AuthError -> showReAuthDialog()
|
Resource.Error.ErrorType.AuthError -> showReAuthDialog()
|
||||||
Resource.Error.ErrorType.NetworkError -> showNetworkErrorMessage()
|
Resource.Error.ErrorType.NetworkError -> showNetworkErrorMessage()
|
||||||
@ -38,6 +42,7 @@ abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
|||||||
Resource.Error.ErrorType.OtherError -> showUnknownErrorMessage()
|
Resource.Error.ErrorType.OtherError -> showUnknownErrorMessage()
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val connectivityObserver: Observer<Boolean> = Observer { networkAvailable ->
|
private val connectivityObserver: Observer<Boolean> = Observer { networkAvailable ->
|
||||||
@ -52,21 +57,22 @@ abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
|||||||
snackbarNetworkStatus = Snackbar.make(view, R.string.network_conn_restored, Snackbar.LENGTH_SHORT)
|
snackbarNetworkStatus = Snackbar.make(view, R.string.network_conn_restored, Snackbar.LENGTH_SHORT)
|
||||||
snackbarNetworkStatus?.show()
|
snackbarNetworkStatus?.show()
|
||||||
connectionHasBeenLost = false
|
connectionHasBeenLost = false
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.askRetry()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val signInActivityResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
|
private val signInActivityResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
|
||||||
if(activityResult.resultCode == Activity.RESULT_OK) {
|
if(activityResult.resultCode == Activity.RESULT_OK) {
|
||||||
onRetry()
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.handleRetry()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when a refresh should be performed. For example,
|
|
||||||
* when sign in flow is started completed from here, it is needed to know
|
|
||||||
* when it is complete.
|
|
||||||
*/
|
|
||||||
abstract fun onRetry()
|
|
||||||
|
|
||||||
private val onAcceptReAuthClick = DialogInterface.OnClickListener { _, _ ->
|
private val onAcceptReAuthClick = DialogInterface.OnClickListener { _, _ ->
|
||||||
signInActivityResult.launch(Intent(this, LogInActivity::class.java))
|
signInActivityResult.launch(Intent(this, LogInActivity::class.java))
|
||||||
@ -78,22 +84,67 @@ abstract class IsolaattiBaseActivity : AppCompatActivity() {
|
|||||||
.setPositiveButton(R.string.accept, onAcceptReAuthClick)
|
.setPositiveButton(R.string.accept, onAcceptReAuthClick)
|
||||||
.setNegativeButton(R.string.close, null)
|
.setNegativeButton(R.string.close, null)
|
||||||
.show()
|
.show()
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNetworkErrorMessage() {
|
private fun showNetworkErrorMessage() {
|
||||||
Toast.makeText(this, R.string.network_error, Toast.LENGTH_SHORT).show()
|
val view: View = window.decorView.findViewById(android.R.id.content) ?: return
|
||||||
|
|
||||||
|
|
||||||
|
snackbarError = Snackbar.make(view, R.string.network_error, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(R.string.retry) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.askRetry()
|
||||||
|
}
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
snackbarError?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showServerErrorMessage() {
|
private fun showServerErrorMessage() {
|
||||||
|
val view: View = window.decorView.findViewById(android.R.id.content) ?: return
|
||||||
|
|
||||||
|
|
||||||
|
snackbarError = Snackbar.make(view, R.string.server_error, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(R.string.retry) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.askRetry()
|
||||||
|
}
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
snackbarError?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNotFoundErrorMessage() {
|
private fun showNotFoundErrorMessage() {
|
||||||
|
val view: View = window.decorView.findViewById(android.R.id.content) ?: return
|
||||||
|
|
||||||
|
|
||||||
|
snackbarError = Snackbar.make(view, R.string.not_found, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(R.string.retry) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.askRetry()
|
||||||
|
}
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
snackbarError?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUnknownErrorMessage() {
|
private fun showUnknownErrorMessage() {
|
||||||
|
val view: View = window.decorView.findViewById(android.R.id.content) ?: return
|
||||||
|
|
||||||
|
|
||||||
|
snackbarError = Snackbar.make(view, R.string.unknown_error, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(R.string.retry) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
errorViewModel.askRetry()
|
||||||
|
}
|
||||||
|
errorViewModel.error.postValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
snackbarError?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@ -2,44 +2,64 @@ package com.isolaatti.followers.data
|
|||||||
|
|
||||||
import com.isolaatti.followers.data.remote.FollowersApi
|
import com.isolaatti.followers.data.remote.FollowersApi
|
||||||
import com.isolaatti.followers.domain.FollowersRepository
|
import com.isolaatti.followers.domain.FollowersRepository
|
||||||
import com.isolaatti.profile.data.remote.ProfileListItemDto
|
|
||||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
import com.isolaatti.utils.IntIdentificationWrapper
|
import com.isolaatti.utils.IntIdentificationWrapper
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import retrofit2.awaitResponse
|
import retrofit2.awaitResponse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class FollowersRepositoryImpl @Inject constructor(private val followersApi: FollowersApi) : FollowersRepository {
|
class FollowersRepositoryImpl @Inject constructor(private val followersApi: FollowersApi) : FollowersRepository {
|
||||||
override fun getFollowersOfUser(userId: Int, after: Int): Flow<List<ProfileListItem>> = flow {
|
override fun getFollowersOfUser(userId: Int, after: Int): Flow<Resource<List<ProfileListItem>>> = flow {
|
||||||
|
try {
|
||||||
val response = followersApi.getFollowersOfUser(userId, after).awaitResponse()
|
val response = followersApi.getFollowersOfUser(userId, after).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
response.body()?.let { emit(response.body()!!.map { ProfileListItem.fromDto(it) }) }
|
response.body()?.let { emit(Resource.Success(response.body()!!.map { ProfileListItem.fromDto(it) })) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFollowingsOfUser(userId: Int, after: Int): Flow<List<ProfileListItem>> = flow {
|
override fun getFollowingsOfUser(userId: Int, after: Int): Flow<Resource<List<ProfileListItem>>> = flow {
|
||||||
|
try {
|
||||||
val response = followersApi.getFollowingsOfUser(userId, after).awaitResponse()
|
val response = followersApi.getFollowingsOfUser(userId, after).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
response.body()?.let { emit(response.body()!!.map { ProfileListItem.fromDto(it) }) }
|
response.body()?.let { emit(Resource.Success(response.body()!!.map { ProfileListItem.fromDto(it) })) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun followUser(userId: Int): Flow<Boolean> = flow {
|
override fun followUser(userId: Int): Flow<Resource<Boolean>> = flow {
|
||||||
|
try {
|
||||||
val response = followersApi.followUser(IntIdentificationWrapper(userId)).awaitResponse()
|
val response = followersApi.followUser(IntIdentificationWrapper(userId)).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
emit(true)
|
emit(Resource.Success(true))
|
||||||
} else {
|
} else {
|
||||||
emit(false)
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unfollowUser(userId: Int): Flow<Boolean> = flow {
|
override fun unfollowUser(userId: Int): Flow<Resource<Boolean>> = flow {
|
||||||
|
try {
|
||||||
val response = followersApi.unfollowUser(IntIdentificationWrapper(userId)).awaitResponse()
|
val response = followersApi.unfollowUser(IntIdentificationWrapper(userId)).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
emit(true)
|
emit(Resource.Success(true))
|
||||||
} else {
|
} else {
|
||||||
emit(false)
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package com.isolaatti.followers.domain
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class FollowUserUseCase @Inject constructor(private val followersRepository: FollowersRepository) {
|
|
||||||
fun follow(userId: Int): Flow<Boolean> {
|
|
||||||
return followersRepository.followUser(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unfollow(userId: Int): Flow<Boolean> {
|
|
||||||
return followersRepository.unfollowUser(userId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,12 @@
|
|||||||
package com.isolaatti.followers.domain
|
package com.isolaatti.followers.domain
|
||||||
|
|
||||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface FollowersRepository {
|
interface FollowersRepository {
|
||||||
fun getFollowersOfUser(userId: Int, after: Int): Flow<List<ProfileListItem>>
|
fun getFollowersOfUser(userId: Int, after: Int): Flow<Resource<List<ProfileListItem>>>
|
||||||
fun getFollowingsOfUser(userId: Int, after: Int): Flow<List<ProfileListItem>>
|
fun getFollowingsOfUser(userId: Int, after: Int): Flow<Resource<List<ProfileListItem>>>
|
||||||
fun followUser(userId: Int): Flow<Boolean>
|
fun followUser(userId: Int): Flow<Resource<Boolean>>
|
||||||
fun unfollowUser(userId: Int): Flow<Boolean>
|
fun unfollowUser(userId: Int): Flow<Resource<Boolean>>
|
||||||
}
|
}
|
||||||
@ -4,9 +4,9 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.isolaatti.followers.domain.FollowUserUseCase
|
|
||||||
import com.isolaatti.followers.domain.FollowersRepository
|
import com.isolaatti.followers.domain.FollowersRepository
|
||||||
import com.isolaatti.profile.domain.entity.ProfileListItem
|
import com.isolaatti.profile.domain.entity.ProfileListItem
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
@ -16,7 +16,7 @@ import kotlinx.coroutines.launch
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class FollowersViewModel @Inject constructor(private val followersRepository: FollowersRepository, private val followUserUseCase: FollowUserUseCase) : ViewModel() {
|
class FollowersViewModel @Inject constructor(private val followersRepository: FollowersRepository) : ViewModel() {
|
||||||
var userId: Int = 0
|
var userId: Int = 0
|
||||||
|
|
||||||
private val followersList: MutableList<ProfileListItem> = mutableListOf()
|
private val followersList: MutableList<ProfileListItem> = mutableListOf()
|
||||||
@ -29,6 +29,19 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo
|
|||||||
val followers: LiveData<List<ProfileListItem>> get() = _followers
|
val followers: LiveData<List<ProfileListItem>> get() = _followers
|
||||||
val followings: LiveData<List<ProfileListItem>> get() = _followings
|
val followings: LiveData<List<ProfileListItem>> get() = _followings
|
||||||
|
|
||||||
|
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||||
|
|
||||||
|
|
||||||
|
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||||
|
// caller should report as handled
|
||||||
|
fun retry() {
|
||||||
|
toRetry.forEach {
|
||||||
|
it.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRetry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getFollowersLastId(): Int {
|
private fun getFollowersLastId(): Int {
|
||||||
if(followersList.isEmpty()) {
|
if(followersList.isEmpty()) {
|
||||||
@ -53,11 +66,23 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
followersRepository.getFollowersOfUser(userId, getFollowersLastId()).onEach {
|
followersRepository.getFollowersOfUser(userId, getFollowersLastId()).onEach {
|
||||||
followersList.addAll(it)
|
when(it) {
|
||||||
_followers.postValue(followersList)
|
is Resource.Success -> {
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
if(it.data != null) {
|
||||||
|
followersList.addAll(it.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_followers.postValue(followersList)
|
||||||
|
}
|
||||||
|
is Resource.Error -> {
|
||||||
|
toRetry.add {
|
||||||
|
fetchFollowers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchFollowings() {
|
fun fetchFollowings() {
|
||||||
@ -67,8 +92,20 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
followersRepository.getFollowingsOfUser(userId, getFollowingsLastId()).onEach {
|
followersRepository.getFollowingsOfUser(userId, getFollowingsLastId()).onEach {
|
||||||
followingsList.addAll(it)
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
toRetry.add {
|
||||||
|
fetchFollowings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
|
if(it.data != null) {
|
||||||
|
followingsList.addAll(it.data)
|
||||||
_followings.postValue(followingsList)
|
_followings.postValue(followingsList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,10 +133,20 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo
|
|||||||
replaceOnLists(user)
|
replaceOnLists(user)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
followUserUseCase.follow(user.id).onEach {
|
followersRepository.followUser(user.id).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
toRetry.add {
|
||||||
|
followUser(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
user.following = true
|
user.following = true
|
||||||
user.updatingFollowState = false
|
user.updatingFollowState = false
|
||||||
replaceOnLists(user)
|
replaceOnLists(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,10 +157,21 @@ class FollowersViewModel @Inject constructor(private val followersRepository: Fo
|
|||||||
replaceOnLists(user)
|
replaceOnLists(user)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
followUserUseCase.unfollow(user.id).onEach {
|
followersRepository.unfollowUser(user.id).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
toRetry.add {
|
||||||
|
unfollowUser(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
user.following = false
|
user.following = false
|
||||||
user.updatingFollowState = false
|
user.updatingFollowState = false
|
||||||
replaceOnLists(user)
|
replaceOnLists(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import android.widget.TextView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
@ -45,6 +48,7 @@ import io.noties.markwon.Markwon
|
|||||||
import io.noties.markwon.MarkwonConfiguration
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
||||||
import io.noties.markwon.linkify.LinkifyPlugin
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
||||||
@ -57,7 +61,6 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||||
private val viewModel: FeedViewModel by activityViewModels()
|
private val viewModel: FeedViewModel by activityViewModels()
|
||||||
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
||||||
|
|
||||||
private var currentUserId = 0
|
private var currentUserId = 0
|
||||||
|
|
||||||
private lateinit var viewBinding: FragmentFeedBinding
|
private lateinit var viewBinding: FragmentFeedBinding
|
||||||
@ -220,9 +223,19 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
|
|||||||
viewModel.errorLoading.observe(viewLifecycleOwner) {
|
viewModel.errorLoading.observe(viewLifecycleOwner) {
|
||||||
errorViewModel.error.postValue(it)
|
errorViewModel.error.postValue(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.getProfile()
|
viewModel.getProfile()
|
||||||
|
|
||||||
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
errorViewModel.retry.collect {
|
||||||
|
viewModel.retry()
|
||||||
|
errorViewModel.handleRetry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLiked(postId: Long) = viewModel.likePost(postId)
|
override fun onLiked(postId: Long) = viewModel.likePost(postId)
|
||||||
|
|||||||
@ -15,10 +15,6 @@ import com.isolaatti.home.presentation.FeedViewModel
|
|||||||
class HomeActivity : IsolaattiBaseActivity() {
|
class HomeActivity : IsolaattiBaseActivity() {
|
||||||
private lateinit var viewBinding: ActivityHomeBinding
|
private lateinit var viewBinding: ActivityHomeBinding
|
||||||
private val feedViewModel: FeedViewModel by viewModels()
|
private val feedViewModel: FeedViewModel by viewModels()
|
||||||
override fun onRetry() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@ -25,6 +25,19 @@ class FeedViewModel @Inject constructor(
|
|||||||
private val postsRepository: PostsRepository
|
private val postsRepository: PostsRepository
|
||||||
) : PostListingViewModelBase() {
|
) : PostListingViewModelBase() {
|
||||||
|
|
||||||
|
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||||
|
|
||||||
|
|
||||||
|
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||||
|
// caller should report as handled
|
||||||
|
fun retry() {
|
||||||
|
toRetry.forEach {
|
||||||
|
it.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRetry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFeed(refresh: Boolean) {
|
override fun getFeed(refresh: Boolean) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
@ -49,7 +62,10 @@ class FeedViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
//errorLoading.postValue(feedDtoResource.errorType)
|
errorLoading.postValue(listResource.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getFeed(refresh)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -67,15 +83,24 @@ class FeedViewModel @Inject constructor(
|
|||||||
authRepository.getUserId().onEach { userId ->
|
authRepository.getUserId().onEach { userId ->
|
||||||
userId?.let {
|
userId?.let {
|
||||||
getProfileUseCase(userId).onEach { profile ->
|
getProfileUseCase(userId).onEach { profile ->
|
||||||
if (profile is Resource.Success) {
|
|
||||||
|
|
||||||
|
when(profile) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
errorLoading.postValue(profile.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
profile.data?.let { _userProfile.postValue(it) }
|
profile.data?.let { _userProfile.postValue(it) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,9 +21,6 @@ class PostViewerActivity : IsolaattiBaseActivity() {
|
|||||||
private lateinit var binding: ActivityPostViewerBinding
|
private lateinit var binding: ActivityPostViewerBinding
|
||||||
|
|
||||||
private var postId: Long? = null
|
private var postId: Long? = null
|
||||||
override fun onRetry() {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@ -14,21 +14,36 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class CommentsRepositoryImpl @Inject constructor(private val commentsApi: CommentsApi) :
|
class CommentsRepositoryImpl @Inject constructor(private val commentsApi: CommentsApi) :
|
||||||
CommentsRepository {
|
CommentsRepository {
|
||||||
override fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>> = flow {
|
override fun getComments(postId: Long, lastId: Long): Flow<Resource<MutableList<Comment>>> = flow {
|
||||||
|
try {
|
||||||
|
emit(Resource.Loading())
|
||||||
val response = commentsApi.getCommentsOfPosts(postId, lastId, 15).awaitResponse()
|
val response = commentsApi.getCommentsOfPosts(postId, lastId, 15).awaitResponse()
|
||||||
if(response.isSuccessful){
|
if(response.isSuccessful){
|
||||||
response.body()?.let { emit(Comment.fromCommentsDto(it).toMutableList()) }
|
response.body()?.let { emit(Resource.Success(Comment.fromCommentsDto(it).toMutableList())) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getComment(commentId: Long): Flow<CommentDto> = flow {
|
override fun getComment(commentId: Long): Flow<Resource<CommentDto>> = flow {
|
||||||
|
try {
|
||||||
|
emit(Resource.Loading())
|
||||||
val response = commentsApi.getComment(commentId).awaitResponse()
|
val response = commentsApi.getComment(commentId).awaitResponse()
|
||||||
if(response.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
response.body()?.let { emit(it) }
|
response.body()?.let { emit(Resource.Success(it)) }
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun postComment(content: String, audioId: String?, postId: Long): Flow<Resource<Comment>> = flow {
|
override fun postComment(content: String, audioId: String?, postId: Long): Flow<Resource<Comment>> = flow {
|
||||||
|
try {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
val commentToPostDto = CommentToPostDto(content, audioId)
|
val commentToPostDto = CommentToPostDto(content, audioId)
|
||||||
val response = commentsApi.postComment(postId, commentToPostDto).awaitResponse()
|
val response = commentsApi.postComment(postId, commentToPostDto).awaitResponse()
|
||||||
@ -38,11 +53,17 @@ class CommentsRepositoryImpl @Inject constructor(private val commentsApi: Commen
|
|||||||
emit(Resource.Success(Comment.fromCommentDto(responseBody)))
|
emit(Resource.Success(Comment.fromCommentDto(responseBody)))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
emit(Resource.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun editComment(commentId: Long, content: String, audioId: String?): Flow<Resource<Comment>> = flow {
|
override fun editComment(commentId: Long, content: String, audioId: String?): Flow<Resource<Comment>> = flow {
|
||||||
|
try {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
val commentToPostDto = CommentToPostDto(content, audioId)
|
val commentToPostDto = CommentToPostDto(content, audioId)
|
||||||
val response = commentsApi.editComment(commentId, commentToPostDto).awaitResponse()
|
val response = commentsApi.editComment(commentId, commentToPostDto).awaitResponse()
|
||||||
@ -53,8 +74,12 @@ class CommentsRepositoryImpl @Inject constructor(private val commentsApi: Commen
|
|||||||
emit(Resource.Success(Comment.fromCommentDto(responseBody)))
|
emit(Resource.Success(Comment.fromCommentDto(responseBody)))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
|
}
|
||||||
|
} catch(_: Exception) {
|
||||||
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
emit(Resource.Error())
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import com.isolaatti.utils.Resource
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface CommentsRepository {
|
interface CommentsRepository {
|
||||||
fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>>
|
fun getComments(postId: Long, lastId: Long): Flow<Resource<MutableList<Comment>>>
|
||||||
fun getComment(commentId: Long): Flow<CommentDto>
|
fun getComment(commentId: Long): Flow<Resource<CommentDto>>
|
||||||
fun postComment(content: String, audioId: String?, postId: Long): Flow<Resource<Comment>>
|
fun postComment(content: String, audioId: String?, postId: Long): Flow<Resource<Comment>>
|
||||||
fun editComment(commentId: Long, content: String, audioId: String?): Flow<Resource<Comment>>
|
fun editComment(commentId: Long, content: String, audioId: String?): Flow<Resource<Comment>>
|
||||||
fun deleteComment(commentId: Long): Flow<Resource<Boolean>>
|
fun deleteComment(commentId: Long): Flow<Resource<Boolean>>
|
||||||
|
|||||||
@ -2,10 +2,11 @@ package com.isolaatti.posting.comments.domain.use_case
|
|||||||
|
|
||||||
import com.isolaatti.posting.comments.domain.CommentsRepository
|
import com.isolaatti.posting.comments.domain.CommentsRepository
|
||||||
import com.isolaatti.posting.comments.domain.model.Comment
|
import com.isolaatti.posting.comments.domain.model.Comment
|
||||||
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetComments @Inject constructor(private val commentsRepository: CommentsRepository) {
|
class GetComments @Inject constructor(private val commentsRepository: CommentsRepository) {
|
||||||
operator fun invoke(postId: Long, lastId: Long? = null): Flow<MutableList<Comment>> =
|
operator fun invoke(postId: Long, lastId: Long? = null): Flow<Resource<MutableList<Comment>>> =
|
||||||
commentsRepository.getComments(postId, lastId ?: 0)
|
commentsRepository.getComments(postId, lastId ?: 0)
|
||||||
}
|
}
|
||||||
@ -34,7 +34,20 @@ class CommentsViewModel @Inject constructor(
|
|||||||
val noMoreContent: MutableLiveData<Boolean?> = MutableLiveData()
|
val noMoreContent: MutableLiveData<Boolean?> = MutableLiveData()
|
||||||
val commentToEdit: MutableLiveData<Comment?> = MutableLiveData()
|
val commentToEdit: MutableLiveData<Comment?> = MutableLiveData()
|
||||||
val finishedEditingComment: MutableLiveData<Boolean?> = MutableLiveData()
|
val finishedEditingComment: MutableLiveData<Boolean?> = MutableLiveData()
|
||||||
val error: MutableLiveData<Boolean> = MutableLiveData(false)
|
val error: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
|
||||||
|
|
||||||
|
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||||
|
|
||||||
|
|
||||||
|
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||||
|
// caller should report as handled
|
||||||
|
fun retry() {
|
||||||
|
toRetry.forEach {
|
||||||
|
it.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRetry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* postId to query comments for. First page will be fetched when set.
|
* postId to query comments for. First page will be fetched when set.
|
||||||
@ -54,17 +67,30 @@ class CommentsViewModel @Inject constructor(
|
|||||||
commentsList.clear()
|
commentsList.clear()
|
||||||
}
|
}
|
||||||
getComments(postId, lastId).onEach {
|
getComments(postId, lastId).onEach {
|
||||||
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
error.postValue(it.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getContent(refresh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
val eventType =
|
val eventType =
|
||||||
if ((commentsList.isNotEmpty())) UpdateEvent.UpdateType.COMMENT_PAGE_ADDED_BOTTOM else UpdateEvent.UpdateType.REFRESH
|
if ((commentsList.isNotEmpty())) UpdateEvent.UpdateType.COMMENT_PAGE_ADDED_BOTTOM else UpdateEvent.UpdateType.REFRESH
|
||||||
commentsList.addAll(it)
|
if(it.data == null) {
|
||||||
|
return@onEach
|
||||||
|
}
|
||||||
|
commentsList.addAll(it.data)
|
||||||
_comments.postValue(Pair(commentsList, UpdateEvent(eventType, null)))
|
_comments.postValue(Pair(commentsList, UpdateEvent(eventType, null)))
|
||||||
if (it.isEmpty()) {
|
if (it.data.isEmpty()) {
|
||||||
noMoreContent.postValue(true)
|
noMoreContent.postValue(true)
|
||||||
}
|
}
|
||||||
if (it.isNotEmpty()) {
|
if (it.data.isNotEmpty()) {
|
||||||
lastId = it.last().id
|
lastId = it.data.last().id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +110,13 @@ class CommentsViewModel @Inject constructor(
|
|||||||
|
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
commentPosted.postValue(false)
|
commentPosted.postValue(false)
|
||||||
error.postValue(true)
|
error.postValue(it.errorType)
|
||||||
|
|
||||||
|
|
||||||
|
// this is the original call, put to retry
|
||||||
|
toRetry.add {
|
||||||
|
postComment(content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
@ -111,7 +143,11 @@ class CommentsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
error.postValue(true)
|
error.postValue(commentResource.errorType)
|
||||||
|
|
||||||
|
toRetry.add {
|
||||||
|
editComment(newContent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Loading -> {}
|
is Resource.Loading -> {}
|
||||||
@ -129,7 +165,11 @@ class CommentsViewModel @Inject constructor(
|
|||||||
deleteCommentUseCase(commentId).onEach {
|
deleteCommentUseCase(commentId).onEach {
|
||||||
when(it) {
|
when(it) {
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
error.postValue(true)
|
error.postValue(it.errorType)
|
||||||
|
|
||||||
|
toRetry.add {
|
||||||
|
deleteComment(commentId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is Resource.Loading -> {}
|
is Resource.Loading -> {}
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
|||||||
@ -10,7 +10,10 @@ import androidx.core.view.doOnLayout
|
|||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||||
@ -19,6 +22,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
|
|||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.common.Dialogs
|
import com.isolaatti.common.Dialogs
|
||||||
|
import com.isolaatti.common.ErrorMessageViewModel
|
||||||
import com.isolaatti.databinding.BottomSheetPostCommentsBinding
|
import com.isolaatti.databinding.BottomSheetPostCommentsBinding
|
||||||
import com.isolaatti.posting.comments.domain.model.Comment
|
import com.isolaatti.posting.comments.domain.model.Comment
|
||||||
import com.isolaatti.posting.comments.presentation.CommentsRecyclerViewAdapter
|
import com.isolaatti.posting.comments.presentation.CommentsRecyclerViewAdapter
|
||||||
@ -38,6 +42,8 @@ import io.noties.markwon.Markwon
|
|||||||
import io.noties.markwon.MarkwonConfiguration
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
||||||
import io.noties.markwon.linkify.LinkifyPlugin
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedCallback {
|
class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedCallback {
|
||||||
@ -45,8 +51,8 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
|
|||||||
private lateinit var viewBinding: BottomSheetPostCommentsBinding
|
private lateinit var viewBinding: BottomSheetPostCommentsBinding
|
||||||
val viewModel: CommentsViewModel by viewModels()
|
val viewModel: CommentsViewModel by viewModels()
|
||||||
private lateinit var adapter: CommentsRecyclerViewAdapter
|
private lateinit var adapter: CommentsRecyclerViewAdapter
|
||||||
|
private val errorViewModel: ErrorMessageViewModel by activityViewModels()
|
||||||
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
private val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
|
||||||
|
|
||||||
private val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked ->
|
private val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked ->
|
||||||
if(optionClicked?.callerId == CALLER_ID) {
|
if(optionClicked?.callerId == CALLER_ID) {
|
||||||
@ -117,10 +123,7 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
|
|||||||
viewModel.commentPosted.observe(viewLifecycleOwner, commentPostedObserver)
|
viewModel.commentPosted.observe(viewLifecycleOwner, commentPostedObserver)
|
||||||
viewModel.commentToEdit.observe(viewLifecycleOwner, commentToEditObserver)
|
viewModel.commentToEdit.observe(viewLifecycleOwner, commentToEditObserver)
|
||||||
viewModel.error.observe(viewLifecycleOwner) {
|
viewModel.error.observe(viewLifecycleOwner) {
|
||||||
if(it == true) {
|
errorViewModel.error.postValue(it)
|
||||||
Toast.makeText(requireContext(), "error", Toast.LENGTH_SHORT).show()
|
|
||||||
viewModel.error.postValue(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
|
||||||
@ -200,6 +203,18 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
|
|||||||
// even if no change event is triggered
|
// even if no change event is triggered
|
||||||
viewBinding.submitCommentButton.isEnabled = !viewBinding.newCommentTextField.editText?.text.isNullOrBlank()
|
viewBinding.submitCommentButton.isEnabled = !viewBinding.newCommentTextField.editText?.text.isNullOrBlank()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// things to retry when user taps "Retry"
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
errorViewModel.retry.collect {
|
||||||
|
viewModel.retry()
|
||||||
|
errorViewModel.handleRetry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setObservers()
|
setObservers()
|
||||||
setListeners()
|
setListeners()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,10 +83,7 @@ class EditCommentDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner) {
|
viewModel.error.observe(viewLifecycleOwner) {
|
||||||
if(it) {
|
viewModel.error.postValue(it)
|
||||||
Toast.makeText(requireContext(), "error", Toast.LENGTH_SHORT).show()
|
|
||||||
viewModel.error.postValue(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.finishedEditingComment.observe(viewLifecycleOwner) {
|
viewModel.finishedEditingComment.observe(viewLifecycleOwner) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import com.isolaatti.posting.posts.domain.entity.Post
|
|||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import retrofit2.awaitResponse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, private val postApi: PostApi) : PostsRepository {
|
class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, private val postApi: PostApi) : PostsRepository {
|
||||||
@ -40,17 +41,12 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
try {
|
try {
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
val result = feedsApi.postsOfUser(userId, 20, lastId, olderFirst, gson.toJson(filter)).execute()
|
val response = feedsApi.postsOfUser(userId, 20, lastId, olderFirst, gson.toJson(filter)).awaitResponse()
|
||||||
if(result.isSuccessful) {
|
if(response.isSuccessful) {
|
||||||
emit(Resource.Success(result.body()?.let { Post.fromFeedDto(it) }))
|
emit(Resource.Success(response.body()?.let { Post.fromFeedDto(it) }))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
when(result.code()) {
|
emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
@ -59,17 +55,12 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
override fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>> = flow {
|
override fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>> = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
try {
|
try {
|
||||||
val result = postApi.makePost(createPostDto).execute()
|
val result = postApi.makePost(createPostDto).awaitResponse()
|
||||||
if(result.isSuccessful) {
|
if(result.isSuccessful) {
|
||||||
emit(Resource.Success(result.body()))
|
emit(Resource.Success(result.body()))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
when(result.code()) {
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
@ -78,17 +69,12 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
override fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>> = flow {
|
override fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>> = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
try {
|
try {
|
||||||
val result = postApi.editPost(editPostDto).execute()
|
val result = postApi.editPost(editPostDto).awaitResponse()
|
||||||
if(result.isSuccessful) {
|
if(result.isSuccessful) {
|
||||||
emit(Resource.Success(result.body()))
|
emit(Resource.Success(result.body()))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
when(result.code()) {
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
@ -102,12 +88,7 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
emit(Resource.Success(result.body()))
|
emit(Resource.Success(result.body()))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
when(result.code()) {
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
@ -121,12 +102,7 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
|
|||||||
emit(Resource.Success(result.body()))
|
emit(Resource.Success(result.body()))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
when(result.code()) {
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,15 @@ import android.os.Bundle
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
import com.isolaatti.common.IsolaattiBaseActivity
|
import com.isolaatti.common.IsolaattiBaseActivity
|
||||||
import com.isolaatti.databinding.ActivityCreatePostBinding
|
import com.isolaatti.databinding.ActivityCreatePostBinding
|
||||||
import com.isolaatti.posting.posts.presentation.CreatePostViewModel
|
import com.isolaatti.posting.posts.presentation.CreatePostViewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class CreatePostActivity : IsolaattiBaseActivity() {
|
class CreatePostActivity : IsolaattiBaseActivity() {
|
||||||
@ -34,13 +38,6 @@ class CreatePostActivity : IsolaattiBaseActivity() {
|
|||||||
val viewModel: CreatePostViewModel by viewModels()
|
val viewModel: CreatePostViewModel by viewModels()
|
||||||
var mode: Int = EXTRA_MODE_CREATE
|
var mode: Int = EXTRA_MODE_CREATE
|
||||||
var postId: Long = 0L
|
var postId: Long = 0L
|
||||||
override fun onRetry() {
|
|
||||||
if(mode == EXTRA_MODE_EDIT && postId != 0L) {
|
|
||||||
viewModel.editDiscussion(postId, binding.filledTextField.editText?.text.toString())
|
|
||||||
} else {
|
|
||||||
viewModel.postDiscussion(binding.filledTextField.editText?.text.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -62,9 +59,24 @@ class CreatePostActivity : IsolaattiBaseActivity() {
|
|||||||
setupUI()
|
setupUI()
|
||||||
setListeners()
|
setListeners()
|
||||||
setObservers()
|
setObservers()
|
||||||
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
errorViewModel.retry.collect {
|
||||||
|
if(!it) {
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode == EXTRA_MODE_EDIT && postId != 0L) {
|
||||||
|
viewModel.editDiscussion(postId, binding.filledTextField.editText?.text.toString())
|
||||||
|
} else {
|
||||||
|
viewModel.postDiscussion(binding.filledTextField.editText?.text.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupUI() {
|
private fun setupUI() {
|
||||||
|
|||||||
@ -6,24 +6,18 @@ import com.isolaatti.profile.domain.ProfileRepository
|
|||||||
import com.isolaatti.utils.Resource
|
import com.isolaatti.utils.Resource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import retrofit2.await
|
import retrofit2.awaitResponse
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ProfileRepositoryImpl @Inject constructor(private val profileApi: ProfileApi) : ProfileRepository {
|
class ProfileRepositoryImpl @Inject constructor(private val profileApi: ProfileApi) : ProfileRepository {
|
||||||
override fun getProfile(userId: Int): Flow<Resource<UserProfileDto>> = flow {
|
override fun getProfile(userId: Int): Flow<Resource<UserProfileDto>> = flow {
|
||||||
try {
|
try {
|
||||||
val result = profileApi.userProfile(userId).execute()
|
val result = profileApi.userProfile(userId).awaitResponse()
|
||||||
if(result.isSuccessful) {
|
if(result.isSuccessful) {
|
||||||
emit(Resource.Success(result.body()))
|
emit(Resource.Success(result.body()))
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
|
emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
|
||||||
when(result.code()) {
|
|
||||||
401 -> emit(Resource.Error(Resource.Error.ErrorType.AuthError))
|
|
||||||
404 -> emit(Resource.Error(Resource.Error.ErrorType.NotFoundError))
|
|
||||||
500 -> emit(Resource.Error(Resource.Error.ErrorType.ServerError))
|
|
||||||
else -> emit(Resource.Error(Resource.Error.ErrorType.OtherError))
|
|
||||||
}
|
|
||||||
} catch(_: Exception) {
|
} catch(_: Exception) {
|
||||||
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,10 +35,31 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
|
|||||||
|
|
||||||
val followingState: MutableLiveData<FollowingState> = MutableLiveData()
|
val followingState: MutableLiveData<FollowingState> = MutableLiveData()
|
||||||
|
|
||||||
|
private val toRetry: MutableList<Runnable> = mutableListOf()
|
||||||
|
|
||||||
|
|
||||||
|
// runs the lists of "Runnable" one by one and clears list. After this is executed,
|
||||||
|
// caller should report as handled
|
||||||
|
fun retry() {
|
||||||
|
toRetry.forEach {
|
||||||
|
it.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRetry.clear()
|
||||||
|
}
|
||||||
|
|
||||||
fun getProfile() {
|
fun getProfile() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
getProfileUseCase(profileId).onEach {
|
getProfileUseCase(profileId).onEach {
|
||||||
if(it is Resource.Success) {
|
when(it) {
|
||||||
|
is Resource.Error -> {
|
||||||
|
errorLoading.postValue(it.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Loading -> {}
|
||||||
|
is Resource.Success -> {
|
||||||
_profile.postValue(it.data!!)
|
_profile.postValue(it.data!!)
|
||||||
followingState.postValue(
|
followingState.postValue(
|
||||||
it.data.let {user->
|
it.data.let {user->
|
||||||
@ -51,6 +72,7 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +97,9 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
|
|||||||
|
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
errorLoading.postValue(feedDtoResource.errorType)
|
errorLoading.postValue(feedDtoResource.errorType)
|
||||||
|
toRetry.add {
|
||||||
|
getFeed(refresh)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
|
|||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
import com.isolaatti.R
|
import com.isolaatti.R
|
||||||
|
import com.isolaatti.common.IsolaattiBaseActivity
|
||||||
import com.isolaatti.databinding.ActivityProfileBinding
|
import com.isolaatti.databinding.ActivityProfileBinding
|
||||||
import com.isolaatti.posting.common.domain.OnUserInteractedCallback
|
import com.isolaatti.posting.common.domain.OnUserInteractedCallback
|
||||||
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
|
||||||
@ -42,7 +43,7 @@ import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAb
|
|||||||
import io.noties.markwon.linkify.LinkifyPlugin
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ProfileActivity : FragmentActivity() {
|
class ProfileActivity : IsolaattiBaseActivity() {
|
||||||
|
|
||||||
lateinit var viewBinding: ActivityProfileBinding
|
lateinit var viewBinding: ActivityProfileBinding
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,10 @@ import android.widget.Toast
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.isolaatti.BuildConfig
|
import com.isolaatti.BuildConfig
|
||||||
@ -43,6 +46,7 @@ import io.noties.markwon.Markwon
|
|||||||
import io.noties.markwon.MarkwonConfiguration
|
import io.noties.markwon.MarkwonConfiguration
|
||||||
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
import io.noties.markwon.image.destination.ImageDestinationProcessorRelativeToAbsolute
|
||||||
import io.noties.markwon.linkify.LinkifyPlugin
|
import io.noties.markwon.linkify.LinkifyPlugin
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ProfileMainFragment : Fragment() {
|
class ProfileMainFragment : Fragment() {
|
||||||
@ -306,5 +310,16 @@ class ProfileMainFragment : Fragment() {
|
|||||||
bind()
|
bind()
|
||||||
setObservers()
|
setObservers()
|
||||||
getData()
|
getData()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
errorViewModel.retry.collect {
|
||||||
|
viewModel.retry()
|
||||||
|
errorViewModel.handleRetry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,5 +7,15 @@ sealed class Resource<T> {
|
|||||||
enum class ErrorType {
|
enum class ErrorType {
|
||||||
NetworkError, AuthError, NotFoundError, ServerError, OtherError
|
NetworkError, AuthError, NotFoundError, ServerError, OtherError
|
||||||
}
|
}
|
||||||
|
companion object {
|
||||||
|
fun mapErrorCode(errorCode: Int): ErrorType {
|
||||||
|
return when(errorCode) {
|
||||||
|
401 -> ErrorType.AuthError
|
||||||
|
404 -> ErrorType.NotFoundError
|
||||||
|
505 -> ErrorType.ServerError
|
||||||
|
else -> ErrorType.OtherError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,4 +77,7 @@
|
|||||||
<string name="edit_comment">Edit comment</string>
|
<string name="edit_comment">Edit comment</string>
|
||||||
<string name="network_conn_lost">Network connection lost</string>
|
<string name="network_conn_lost">Network connection lost</string>
|
||||||
<string name="network_conn_restored">Network connection restored</string>
|
<string name="network_conn_restored">Network connection restored</string>
|
||||||
|
<string name="retry">Retry</string>
|
||||||
|
<string name="not_found">The resource you are trying to load could not be found</string>
|
||||||
|
<string name="unknown_error">An unkwnow error occurred</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user