This commit is contained in:
erike 2023-08-27 20:00:43 -06:00
parent f91fa2d27d
commit 633b366baf
29 changed files with 253 additions and 100 deletions

View File

@ -7,6 +7,7 @@ import com.isolaatti.auth.data.remote.AuthApi
import com.isolaatti.auth.domain.AuthRepository import com.isolaatti.auth.domain.AuthRepository
import com.isolaatti.connectivity.RetrofitClient import com.isolaatti.connectivity.RetrofitClient
import com.isolaatti.database.AppDatabase import com.isolaatti.database.AppDatabase
import com.isolaatti.settings.domain.UserIdSetting
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -21,7 +22,7 @@ class Module {
} }
@Provides @Provides
fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi, keyValueDao: KeyValueDao): AuthRepository { fun provideAuthRepository(tokenStorage: TokenStorage, authApi: AuthApi, userIdSetting: UserIdSetting): AuthRepository {
return AuthRepositoryImpl(tokenStorage, authApi, keyValueDao) return AuthRepositoryImpl(tokenStorage, authApi, userIdSetting)
} }
} }

View File

@ -7,6 +7,7 @@ import com.isolaatti.auth.data.local.TokenStorage
import com.isolaatti.auth.data.remote.AuthApi import com.isolaatti.auth.data.remote.AuthApi
import com.isolaatti.auth.data.remote.Credential import com.isolaatti.auth.data.remote.Credential
import com.isolaatti.auth.domain.AuthRepository import com.isolaatti.auth.domain.AuthRepository
import com.isolaatti.settings.domain.UserIdSetting
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
@ -17,11 +18,8 @@ import javax.inject.Inject
class AuthRepositoryImpl @Inject constructor( class AuthRepositoryImpl @Inject constructor(
private val tokenStorage: TokenStorage, private val tokenStorage: TokenStorage,
private val authApi: AuthApi, private val authApi: AuthApi,
private val keyValueDao: KeyValueDao private val userIdSetting: UserIdSetting
) : AuthRepository { ) : AuthRepository {
companion object {
val KEY_USERID = "user_id"
}
override fun authWithEmailAndPassword( override fun authWithEmailAndPassword(
email: String, email: String,
password: String password: String
@ -37,7 +35,7 @@ class AuthRepositoryImpl @Inject constructor(
return@flow return@flow
} }
tokenStorage.storeToken(dto) tokenStorage.storeToken(dto)
keyValueDao.setValue(KeyValueEntity(KEY_USERID, dto.userId.toString())) userIdSetting.setUserId(dto.userId)
emit(Resource.Success(true)) emit(Resource.Success(true))
return@flow return@flow
} }
@ -66,7 +64,7 @@ class AuthRepositoryImpl @Inject constructor(
} }
override fun getUserId(): Flow<Int?> = flow { override fun getUserId(): Flow<Int?> = flow {
emit(keyValueDao.getValue(KEY_USERID).toIntOrNull()) emit(userIdSetting.getUserId())
} }
} }

View File

@ -25,6 +25,7 @@ import com.isolaatti.picture_viewer.ui.PictureViewerActivity
import com.isolaatti.posting.PostViewerActivity import com.isolaatti.posting.PostViewerActivity
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
import com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
@ -160,7 +161,7 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
.usePlugin(PicassoImagesPluginDef.picassoImagePlugin) .usePlugin(PicassoImagesPluginDef.picassoImagePlugin)
.usePlugin(LinkifyPlugin.create()) .usePlugin(LinkifyPlugin.create())
.build() .build()
adapter = PostsRecyclerViewAdapter(markwon, this, null) adapter = PostsRecyclerViewAdapter(markwon, this)
viewBinding.feedRecyclerView.adapter = adapter viewBinding.feedRecyclerView.adapter = adapter
viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext()) viewBinding.feedRecyclerView.layoutManager = LinearLayoutManager(requireContext())
@ -227,8 +228,8 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId) override fun onUnLiked(postId: Long) = viewModel.unLikePost(postId)
override fun onOptions(postId: Long) { override fun onOptions(post: Ownable) {
optionsViewModel.setOptions(Options.myPostOptions, CALLER_ID, postId) optionsViewModel.setOptions(Options.POST_OPTIONS, CALLER_ID, post)
val modalBottomSheet = BottomSheetPostOptionsFragment() val modalBottomSheet = BottomSheetPostOptionsFragment()
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG) modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
} }

View File

@ -2,7 +2,6 @@ package com.isolaatti.home.presentation
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.isolaatti.auth.domain.AuthRepository import com.isolaatti.auth.domain.AuthRepository
import com.isolaatti.posting.posts.domain.PostsRepository import com.isolaatti.posting.posts.domain.PostsRepository
@ -31,12 +30,16 @@ class FeedViewModel @Inject constructor(
if (refresh) { if (refresh) {
posts.value = null posts.value = null
} }
postsRepository.getFeed(getLastId()).onEach { feedDtoResource -> postsRepository.getFeed(getLastId()).onEach { listResource ->
when (feedDtoResource) { when (listResource) {
is Resource.Success -> { is Resource.Success -> {
loadingPosts.postValue(false) loadingPosts.postValue(false)
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null))) posts.postValue(Pair(postsList?.apply {
noMoreContent.postValue(feedDtoResource.data?.moreContent == false) addAll(listResource.data ?: listOf())
} ?: listResource.data,
UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null)))
noMoreContent.postValue(listResource.data?.size == 0)
} }
is Resource.Loading -> { is Resource.Loading -> {
@ -45,7 +48,7 @@ class FeedViewModel @Inject constructor(
} }
is Resource.Error -> { is Resource.Error -> {
errorLoading.postValue(feedDtoResource.errorType) //errorLoading.postValue(feedDtoResource.errorType)
} }
} }
isLoadingFromScrolling = false isLoadingFromScrolling = false

View File

@ -3,8 +3,8 @@ package com.isolaatti.posting.comments.data.repository
import com.isolaatti.posting.comments.data.remote.CommentDto import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.data.remote.CommentToPostDto import com.isolaatti.posting.comments.data.remote.CommentToPostDto
import com.isolaatti.posting.comments.data.remote.CommentsApi import com.isolaatti.posting.comments.data.remote.CommentsApi
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
import com.isolaatti.posting.comments.domain.CommentsRepository import com.isolaatti.posting.comments.domain.CommentsRepository
import com.isolaatti.posting.comments.domain.model.Comment
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
@ -12,10 +12,10 @@ 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<FeedCommentsDto> = flow { override fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>> = flow {
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(it) } response.body()?.let { emit(Comment.fromCommentsDto(it).toMutableList()) }
} }
} }

View File

@ -2,11 +2,11 @@ package com.isolaatti.posting.comments.domain
import com.isolaatti.posting.comments.data.remote.CommentDto import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.data.remote.CommentToPostDto import com.isolaatti.posting.comments.data.remote.CommentToPostDto
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto import com.isolaatti.posting.comments.domain.model.Comment
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface CommentsRepository { interface CommentsRepository {
fun getComments(postId: Long, lastId: Long): Flow<FeedCommentsDto> fun getComments(postId: Long, lastId: Long): Flow<MutableList<Comment>>
fun getComment(commentId: Long): Flow<CommentDto> fun getComment(commentId: Long): Flow<CommentDto>
fun postComment(commentToPostDto: CommentToPostDto, postId: Long): Flow<Boolean> fun postComment(commentToPostDto: CommentToPostDto, postId: Long): Flow<Boolean>
} }

View File

@ -1,12 +1,30 @@
package com.isolaatti.posting.comments.domain.model package com.isolaatti.posting.comments.domain.model
import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
import com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.posts.data.remote.FeedDto
data class Comment( data class Comment(
val id: Long, val id: Long,
val textContent: String, val textContent: String,
val userId: Int, override val userId: Int,
val postId: Long, val postId: Long,
val date: String, val date: String,
val responseForCommentId: Long?, val username: String
val linkedDiscussionId: Long?, ) : Ownable {
val linkedCommentId: Long? companion object {
) fun fromCommentsDto(dtoList: FeedCommentsDto): List<Comment> {
return dtoList.data.map {
Comment(
id = it.comment.id,
textContent = it.comment.textContent,
userId = it.comment.userId,
postId = it.comment.postId,
date = it.comment.date,
username = it.username
)
}
}
}
}

View File

@ -1,12 +1,11 @@
package com.isolaatti.posting.comments.domain.use_case package com.isolaatti.posting.comments.domain.use_case
import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.data.remote.FeedCommentsDto
import com.isolaatti.posting.comments.domain.CommentsRepository import com.isolaatti.posting.comments.domain.CommentsRepository
import com.isolaatti.posting.comments.domain.model.Comment
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<FeedCommentsDto> = operator fun invoke(postId: Long, lastId: Long? = null): Flow<MutableList<Comment>> =
commentsRepository.getComments(postId, lastId ?: 0) commentsRepository.getComments(postId, lastId ?: 0)
} }

View File

@ -13,6 +13,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.databinding.BottomSheetPostCommentsBinding import com.isolaatti.databinding.BottomSheetPostCommentsBinding
import com.isolaatti.posting.common.domain.OnUserInteractedCallback import com.isolaatti.posting.common.domain.OnUserInteractedCallback
import com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
@ -115,8 +116,8 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
} }
override fun onOptions(postId: Long) { override fun onOptions(comment: Ownable) {
optionsViewModel.setOptions(Options.myCommentOptions, CALLER_ID) optionsViewModel.setOptions(Options.POST_OPTIONS, CALLER_ID, comment)
val fragment = BottomSheetPostOptionsFragment() val fragment = BottomSheetPostOptionsFragment()
fragment.show(parentFragmentManager, BottomSheetPostOptionsFragment.TAG) fragment.show(parentFragmentManager, BottomSheetPostOptionsFragment.TAG)
} }

View File

@ -5,12 +5,13 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.isolaatti.databinding.CommentLayoutBinding import com.isolaatti.databinding.CommentLayoutBinding
import com.isolaatti.posting.comments.data.remote.CommentDto import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.domain.model.Comment
import com.isolaatti.posting.common.domain.OnUserInteractedCallback import com.isolaatti.posting.common.domain.OnUserInteractedCallback
import com.isolaatti.utils.UrlGen import com.isolaatti.utils.UrlGen
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
class CommentsRecyclerViewAdapter(private var list: List<CommentDto>, private val markwon: Markwon, private val callback: OnUserInteractedCallback) : RecyclerView.Adapter<CommentsRecyclerViewAdapter.CommentViewHolder>() { class CommentsRecyclerViewAdapter(private var list: List<Comment>, private val markwon: Markwon, private val callback: OnUserInteractedCallback) : RecyclerView.Adapter<CommentsRecyclerViewAdapter.CommentViewHolder>() {
inner class CommentViewHolder(val viewBinding: CommentLayoutBinding) : RecyclerView.ViewHolder(viewBinding.root) inner class CommentViewHolder(val viewBinding: CommentLayoutBinding) : RecyclerView.ViewHolder(viewBinding.root)
@ -23,21 +24,21 @@ class CommentsRecyclerViewAdapter(private var list: List<CommentDto>, private va
override fun onBindViewHolder(holder: CommentViewHolder, position: Int) { override fun onBindViewHolder(holder: CommentViewHolder, position: Int) {
val comment = list[position] val comment = list[position]
holder.viewBinding.textViewDate.text = comment.comment.date holder.viewBinding.textViewDate.text = comment.date
markwon.setMarkdown(holder.viewBinding.postContent, comment.comment.textContent) markwon.setMarkdown(holder.viewBinding.postContent, comment.textContent)
holder.viewBinding.textViewUsername.text = comment.username holder.viewBinding.textViewUsername.text = comment.username
holder.viewBinding.textViewUsername.setOnClickListener { holder.viewBinding.textViewUsername.setOnClickListener {
callback.onProfileClick(comment.comment.userId) callback.onProfileClick(comment.userId)
} }
holder.viewBinding.moreButton.setOnClickListener { holder.viewBinding.moreButton.setOnClickListener {
callback.onOptions(comment.comment.id) callback.onOptions(comment)
} }
Picasso.get() Picasso.get()
.load(UrlGen.userProfileImage(comment.comment.userId)) .load(UrlGen.userProfileImage(comment.userId))
.into(holder.viewBinding.avatarPicture) .into(holder.viewBinding.avatarPicture)
} }
fun submitList(commentDtoList: List<CommentDto>) { fun submitList(commentDtoList: List<Comment>) {
val lastIndex = if(list.count() - 1 < 1) 0 else list.count() - 1 val lastIndex = if(list.count() - 1 < 1) 0 else list.count() - 1
list = commentDtoList list = commentDtoList
notifyItemRangeChanged(lastIndex, commentDtoList.count()) notifyItemRangeChanged(lastIndex, commentDtoList.count())

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.isolaatti.posting.comments.data.remote.CommentDto import com.isolaatti.posting.comments.data.remote.CommentDto
import com.isolaatti.posting.comments.domain.model.Comment
import com.isolaatti.posting.comments.domain.use_case.GetComments import com.isolaatti.posting.comments.domain.use_case.GetComments
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -16,9 +17,9 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class CommentsViewModel @Inject constructor(private val getComments: GetComments) : ViewModel() { class CommentsViewModel @Inject constructor(private val getComments: GetComments) : ViewModel() {
private val _comments: MutableLiveData<List<CommentDto>> = MutableLiveData() private val _comments: MutableLiveData<List<Comment>> = MutableLiveData()
val comments: LiveData<List<CommentDto>> get() = _comments val comments: LiveData<List<Comment>> get() = _comments
/** /**
* postId to query comments for. First page will be fetched when set. * postId to query comments for. First page will be fetched when set.
@ -36,10 +37,10 @@ class CommentsViewModel @Inject constructor(private val getComments: GetComments
viewModelScope.launch { viewModelScope.launch {
getComments(postId, lastId).onEach { getComments(postId, lastId).onEach {
val newList = _comments.value?.toMutableList() ?: mutableListOf() val newList = _comments.value?.toMutableList() ?: mutableListOf()
newList.addAll(it.data) newList.addAll(it)
_comments.postValue(newList) _comments.postValue(newList)
if(it.data.isNotEmpty()){ if(it.isNotEmpty()){
lastId = it.data.last().comment.id lastId = it.last().id
} }
}.flowOn(Dispatchers.IO).launchIn(this) }.flowOn(Dispatchers.IO).launchIn(this)
@ -49,8 +50,8 @@ class CommentsViewModel @Inject constructor(private val getComments: GetComments
/** /**
* Use when new comment has been posted * Use when new comment has been posted
*/ */
fun putCommentAtTheBeginning(commentDto: CommentDto) { fun putCommentAtTheBeginning(commentDto: Comment) {
val newList: MutableList<CommentDto> = mutableListOf(commentDto) val newList: MutableList<Comment> = mutableListOf(commentDto)
newList.addAll(_comments.value ?: mutableListOf()) newList.addAll(_comments.value ?: mutableListOf())
_comments.postValue(newList) _comments.postValue(newList)
} }

View File

@ -1,7 +1,7 @@
package com.isolaatti.posting.common.domain package com.isolaatti.posting.common.domain
interface OnUserInteractedCallback { interface OnUserInteractedCallback {
fun onOptions(postId: Long) fun onOptions(postId: Ownable)
fun onProfileClick(userId: Int) fun onProfileClick(userId: Int)
fun onLoadMore() fun onLoadMore()
} }

View File

@ -0,0 +1,5 @@
package com.isolaatti.posting.common.domain
interface Ownable {
val userId: Int
}

View File

@ -21,6 +21,8 @@ data class Options(
} }
companion object { companion object {
const val POST_OPTIONS = 1
val noOptions = Options(0, listOf()) val noOptions = Options(0, listOf())
val myPostOptions = Options(R.string.post_options_title, listOf( val myPostOptions = Options(R.string.post_options_title, listOf(
Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE), Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE),

View File

@ -3,10 +3,21 @@ package com.isolaatti.posting.common.options_bottom_sheet.presentation
import androidx.lifecycle.LiveData 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 com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked import com.isolaatti.posting.common.options_bottom_sheet.domain.OptionClicked
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options.Companion.POST_OPTIONS
import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.settings.domain.UserIdSetting
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
class BottomSheetPostOptionsViewModel : ViewModel() { @HiltViewModel
class BottomSheetPostOptionsViewModel @Inject constructor(private val userIdSetting: UserIdSetting) : ViewModel() {
private val _options: MutableLiveData<Options> = MutableLiveData() private val _options: MutableLiveData<Options> = MutableLiveData()
val options: LiveData<Options> get() = _options val options: LiveData<Options> get() = _options
@ -21,10 +32,28 @@ class BottomSheetPostOptionsViewModel : ViewModel() {
_optionClicked.postValue(null) _optionClicked.postValue(null)
} }
fun setOptions(options: Options, callerId: Int, payload: Any? = null) { fun setOptions(options: Int, callerId: Int, payload: Ownable? = null) {
_options.postValue(options) viewModelScope.launch {
_callerId = callerId CoroutineScope(Dispatchers.IO).launch {
_payload = payload when(options) {
POST_OPTIONS -> {
userIdSetting.getUserId()?.let { userId ->
if(userId == payload?.userId) {
_options.postValue(Options.myPostOptions)
} else {
_options.postValue(Options.postOptions)
}
_callerId = callerId
_payload = payload
}
}
}
}
}
} }

View File

@ -10,18 +10,19 @@ import com.isolaatti.posting.posts.data.remote.FeedsApi
import com.isolaatti.posting.posts.data.remote.PostApi import com.isolaatti.posting.posts.data.remote.PostApi
import com.isolaatti.posting.posts.data.remote.PostDeletedDto import com.isolaatti.posting.posts.data.remote.PostDeletedDto
import com.isolaatti.posting.posts.domain.PostsRepository import com.isolaatti.posting.posts.domain.PostsRepository
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 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 {
override fun getFeed(lastId: Long): Flow<Resource<FeedDto>> = flow { override fun getFeed(lastId: Long): Flow<Resource<MutableList<Post>>> = flow {
emit(Resource.Loading()) emit(Resource.Loading())
try { try {
val result = feedsApi.getChronology(lastId, 20).execute() val result = feedsApi.getChronology(lastId, 20).execute()
if(result.isSuccessful) { if(result.isSuccessful) {
emit(Resource.Success(result.body())) emit(Resource.Success(result.body()?.let { Post.fromFeedDto(it) }))
return@flow return@flow
} }
when(result.code()) { when(result.code()) {
@ -35,13 +36,13 @@ class PostsRepositoryImpl @Inject constructor(private val feedsApi: FeedsApi, pr
} }
} }
override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> = flow { override fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>> = flow {
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 result = feedsApi.postsOfUser(userId, 20, lastId, olderFirst, gson.toJson(filter)).execute()
if(result.isSuccessful) { if(result.isSuccessful) {
emit(Resource.Success(result.body())) emit(Resource.Success(result.body()?.let { Post.fromFeedDto(it) }))
return@flow return@flow
} }
when(result.code()) { when(result.code()) {

View File

@ -5,14 +5,15 @@ import com.isolaatti.posting.posts.data.remote.EditPostDto
import com.isolaatti.posting.posts.data.remote.FeedDto import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.data.remote.FeedFilterDto import com.isolaatti.posting.posts.data.remote.FeedFilterDto
import com.isolaatti.posting.posts.data.remote.PostDeletedDto import com.isolaatti.posting.posts.data.remote.PostDeletedDto
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
interface PostsRepository { interface PostsRepository {
fun getFeed(lastId: Long): Flow<Resource<FeedDto>> fun getFeed(lastId: Long): Flow<Resource<MutableList<Post>>>
fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> fun getProfilePosts(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>>
fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>> fun makePost(createPostDto: CreatePostDto): Flow<Resource<FeedDto.PostDto>>
fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>> fun editPost(editPostDto: EditPostDto): Flow<Resource<FeedDto.PostDto>>

View File

@ -0,0 +1,40 @@
package com.isolaatti.posting.posts.domain.entity
import com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.posts.data.remote.FeedDto
data class Post(
val id: Long,
var textContent: String,
override val userId: Int,
val privacy: Int,
val date: String,
var audioId: String?,
val squadId: String?,
var numberOfLikes: Int,
var numberOfComments: Int,
val userName: String,
val squadName: String?,
var liked: Boolean
) : Ownable {
companion object {
fun fromFeedDto(feedDto: FeedDto): MutableList<Post> {
return feedDto.data.map {
Post(
id = it.post.id,
userId = it.post.userId,
textContent = it.post.textContent,
privacy = it.post.privacy,
date = it.post.date,
audioId = it.post.audioId,
squadId = it.post.squadId,
numberOfComments = it.numberOfComments,
numberOfLikes = it.numberOfLikes,
userName = it.userName,
squadName = it.squadName,
liked = it.liked
)
}.toMutableList()
}
}
}

View File

@ -1,6 +1,7 @@
package com.isolaatti.posting.posts.presentation package com.isolaatti.posting.posts.presentation
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
import com.isolaatti.posting.common.domain.Ownable
abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBase: PostListingViewModelBase) : OnUserInteractedWithPostCallback { abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBase: PostListingViewModelBase) : OnUserInteractedWithPostCallback {
@ -16,7 +17,7 @@ abstract class PostListingRecyclerViewAdapterWiring(private val postsViewModelBa
abstract override fun onOpenPost(postId: Long) abstract override fun onOpenPost(postId: Long)
abstract override fun onOptions(postId: Long) abstract override fun onOptions(post: Ownable)
abstract override fun onProfileClick(userId: Int) abstract override fun onProfileClick(userId: Int)
} }

View File

@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.isolaatti.posting.likes.domain.repository.LikesRepository import com.isolaatti.posting.likes.domain.repository.LikesRepository
import com.isolaatti.posting.posts.data.remote.FeedDto import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.domain.entity.Post
import com.isolaatti.posting.posts.domain.use_case.DeletePost import com.isolaatti.posting.posts.domain.use_case.DeletePost
import com.isolaatti.profile.domain.use_case.GetProfilePosts import com.isolaatti.profile.domain.use_case.GetProfilePosts
import com.isolaatti.utils.Resource import com.isolaatti.utils.Resource
@ -24,8 +25,9 @@ abstract class PostListingViewModelBase : ViewModel() {
@Inject @Inject
lateinit var deletePostUseCase: DeletePost lateinit var deletePostUseCase: DeletePost
val posts: MutableLiveData<Pair<FeedDto?, UpdateEvent>?> = MutableLiveData() val posts: MutableLiveData<Pair<MutableList<Post>?, UpdateEvent>?> = MutableLiveData()
val postsList get() = posts.value?.first
val loadingPosts = MutableLiveData(false) val loadingPosts = MutableLiveData(false)
@ -34,7 +36,7 @@ abstract class PostListingViewModelBase : ViewModel() {
val errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData() val errorLoading: MutableLiveData<Resource.Error.ErrorType?> = MutableLiveData()
var isLoadingFromScrolling = false var isLoadingFromScrolling = false
fun getLastId(): Long = try { posts.value?.first?.data?.last()?.post?.id ?: 0 } catch (e: NoSuchElementException) { 0 } fun getLastId(): Long = try { posts.value?.first?.last()?.id ?: 0 } catch (e: NoSuchElementException) { 0 }
abstract fun getFeed(refresh: Boolean) abstract fun getFeed(refresh: Boolean)
@ -42,12 +44,12 @@ abstract class PostListingViewModelBase : ViewModel() {
fun likePost(postId: Long) { fun likePost(postId: Long) {
viewModelScope.launch { viewModelScope.launch {
likesRepository.likePost(postId).onEach {likeDto -> likesRepository.likePost(postId).onEach {likeDto ->
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId } val likedPost = posts.value?.first?.find { post -> post.id == likeDto.postId }
val index = posts.value?.first?.data?.indexOf(likedPost) val index = posts.value?.first?.indexOf(likedPost)
if(index != null){ if(index != null){
val temp = posts.value?.first val temp = posts.value?.first?.toMutableList()
Log.d("***", temp.toString()) Log.d("***", temp.toString())
temp?.data?.set(index, likedPost!!.apply { temp?.set(index, likedPost!!.apply {
liked = true liked = true
numberOfLikes = likeDto.likesCount numberOfLikes = likeDto.likesCount
@ -61,10 +63,11 @@ abstract class PostListingViewModelBase : ViewModel() {
fun unLikePost(postId: Long) { fun unLikePost(postId: Long) {
viewModelScope.launch { viewModelScope.launch {
likesRepository.unLikePost(postId).onEach {likeDto -> likesRepository.unLikePost(postId).onEach {likeDto ->
val likedPost = posts.value?.first?.data?.find { post -> post.post.id == likeDto.postId } val likedPost = posts.value?.first?.find { post -> post.id == likeDto.postId }
val index = posts.value?.first?.data?.indexOf(likedPost) val index = posts.value?.first?.indexOf(likedPost)
if(index != null){ if(index != null){
posts.value?.first?.data?.set(index, likedPost!!.apply { val temp = posts.value?.first?.toMutableList()
temp?.set(index, likedPost!!.apply {
liked = false liked = false
numberOfLikes = likeDto.likesCount numberOfLikes = likeDto.likesCount
}) })
@ -79,11 +82,11 @@ abstract class PostListingViewModelBase : ViewModel() {
deletePostUseCase(postId).onEach { res -> deletePostUseCase(postId).onEach { res ->
when(res) { when(res) {
is Resource.Success -> { is Resource.Success -> {
val postDeleted = posts.value?.first?.data?.find { postDto -> postDto.post.id == postId } val postDeleted = posts.value?.first?.find { post -> post.id == postId }
?: return@onEach ?: return@onEach
val index = posts.value?.first?.data?.indexOf(postDeleted) val index = posts.value?.first?.indexOf(postDeleted)
posts.value?.first?.data?.removeAt(index!!) posts.value?.first?.removeAt(index!!)
posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_REMOVED, index))) posts.postValue(posts.value?.copy(second = UpdateEvent(UpdateEvent.UpdateType.POST_REMOVED, index)))
} }
is Resource.Loading -> {} is Resource.Loading -> {}

View File

@ -12,15 +12,16 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback import com.isolaatti.posting.common.domain.OnUserInteractedWithPostCallback
import com.isolaatti.posting.posts.domain.entity.Post
import com.isolaatti.utils.UrlGen.userProfileImage import com.isolaatti.utils.UrlGen.userProfileImage
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callback: OnUserInteractedWithPostCallback, private var feedDto: FeedDto?) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callback: OnUserInteractedWithPostCallback) : RecyclerView.Adapter<PostsRecyclerViewAdapter.FeedViewHolder>(){
private var postList: List<Post>? = null
inner class FeedViewHolder(itemView: View) : ViewHolder(itemView) { inner class FeedViewHolder(itemView: View) : ViewHolder(itemView) {
fun bindView(postDto: FeedDto.PostDto, payloads: List<Any>) { fun bindView(postDto: Post, payloads: List<Any>) {
Log.d("payloads", payloads.count().toString()) Log.d("payloads", payloads.count().toString())
val likeButton: MaterialButton = itemView.findViewById(R.id.like_button) val likeButton: MaterialButton = itemView.findViewById(R.id.like_button)
@ -59,17 +60,17 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
val username: TextView = itemView.findViewById(R.id.text_view_username) val username: TextView = itemView.findViewById(R.id.text_view_username)
username.text = postDto.userName username.text = postDto.userName
username.setOnClickListener { username.setOnClickListener {
callback.onProfileClick(postDto.post.userId) callback.onProfileClick(postDto.userId)
} }
val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture) val profileImageView: ImageView = itemView.findViewById(R.id.avatar_picture)
Picasso.get().load(userProfileImage(postDto.post.userId)).into(profileImageView) Picasso.get().load(userProfileImage(postDto.userId)).into(profileImageView)
val dateTextView: TextView = itemView.findViewById(R.id.text_view_date) val dateTextView: TextView = itemView.findViewById(R.id.text_view_date)
dateTextView.text = postDto.post.date dateTextView.text = postDto.date
val content: TextView = itemView.findViewById(R.id.post_content) val content: TextView = itemView.findViewById(R.id.post_content)
markwon.setMarkdown(content, postDto.post.textContent) markwon.setMarkdown(content, postDto.textContent)
likeButton.isEnabled = true likeButton.isEnabled = true
@ -87,23 +88,23 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
val moreButton: MaterialButton = itemView.findViewById(R.id.more_button) val moreButton: MaterialButton = itemView.findViewById(R.id.more_button)
moreButton.setOnClickListener { moreButton.setOnClickListener {
callback.onOptions(postDto.post.id) callback.onOptions(postDto)
} }
likeButton.setOnClickListener { likeButton.setOnClickListener {
likeButton.isEnabled = false likeButton.isEnabled = false
if(postDto.liked){ if(postDto.liked){
callback.onUnLiked(postDto.post.id) callback.onUnLiked(postDto.id)
} else { } else {
callback.onLiked(postDto.post.id) callback.onLiked(postDto.id)
} }
} }
commentsButton.setOnClickListener { commentsButton.setOnClickListener {
callback.onComment(postDto.post.id) callback.onComment(postDto.id)
} }
itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener { itemView.findViewById<MaterialCardView>(R.id.card).setOnClickListener {
callback.onOpenPost(postDto.post.id) callback.onOpenPost(postDto.id)
} }
} }
@ -122,25 +123,25 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
} }
var previousSize = 0 var previousSize = 0
override fun getItemCount(): Int = feedDto?.data?.size ?: 0 override fun getItemCount(): Int = postList?.size ?: 0
override fun setHasStableIds(hasStableIds: Boolean) { override fun setHasStableIds(hasStableIds: Boolean) {
super.setHasStableIds(true) super.setHasStableIds(true)
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun updateList(updatedFeed: FeedDto, updateEvent: UpdateEvent? = null) { fun updateList(updatedFeed: List<Post>, updateEvent: UpdateEvent? = null) {
if(updateEvent == null) { if(updateEvent == null) {
feedDto = updatedFeed postList = updatedFeed
notifyDataSetChanged() notifyDataSetChanged()
return return
} }
val postUpdated = updateEvent.affectedPosition?.let { feedDto?.data?.get(it) } val postUpdated = updateEvent.affectedPosition?.let { postList?.get(it) }
val position = updateEvent.affectedPosition val position = updateEvent.affectedPosition
previousSize = itemCount previousSize = itemCount
feedDto = updatedFeed postList = updatedFeed
when(updateEvent.updateType) { when(updateEvent.updateType) {
UpdateEvent.UpdateType.POST_LIKED -> { UpdateEvent.UpdateType.POST_LIKED -> {
@ -179,8 +180,8 @@ class PostsRecyclerViewAdapter (private val markwon: Markwon, private val callba
requestedNewContent = false requestedNewContent = false
} }
override fun onBindViewHolder(holder: FeedViewHolder, position: Int, payloads: List<Any>) { override fun onBindViewHolder(holder: FeedViewHolder, position: Int, payloads: List<Any>) {
holder.bindView(feedDto?.data?.get(position) ?: return, payloads) holder.bindView(postList?.get(position) ?: return, payloads)
val totalItems = feedDto?.data?.size val totalItems = postList?.size
if(totalItems != null && totalItems > 0 && !requestedNewContent) { if(totalItems != null && totalItems > 0 && !requestedNewContent) {
if(position == totalItems - 1) { if(position == totalItems - 1) {
requestedNewContent = true requestedNewContent = true

View File

@ -1,13 +1,13 @@
package com.isolaatti.profile.domain.use_case package com.isolaatti.profile.domain.use_case
import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.data.remote.FeedFilterDto import com.isolaatti.posting.posts.data.remote.FeedFilterDto
import com.isolaatti.posting.posts.domain.PostsRepository import com.isolaatti.posting.posts.domain.PostsRepository
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 javax.inject.Inject import javax.inject.Inject
class GetProfilePosts @Inject constructor(private val postsRepository: PostsRepository) { class GetProfilePosts @Inject constructor(private val postsRepository: PostsRepository) {
operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<FeedDto>> = operator fun invoke(userId: Int, lastId: Long, olderFirst: Boolean, filter: FeedFilterDto?): Flow<Resource<MutableList<Post>>> =
postsRepository.getProfilePosts(userId, lastId, olderFirst, filter) postsRepository.getProfilePosts(userId, lastId, olderFirst, filter)
} }

View File

@ -65,8 +65,8 @@ class ProfileViewModel @Inject constructor(private val getProfileUseCase: GetPro
when (feedDtoResource) { when (feedDtoResource) {
is Resource.Success -> { is Resource.Success -> {
loadingPosts.postValue(false) loadingPosts.postValue(false)
posts.postValue(Pair(posts.value?.first?.concatFeed(feedDtoResource.data) ?: feedDtoResource.data, UpdateEvent(if(refresh) UpdateEvent.UpdateType.REFRESH else UpdateEvent.UpdateType.PAGE_ADDED, null))) posts.postValue(Pair(posts.value?.first?.apply { addAll(feedDtoResource.data ?: listOf()) } ?: feedDtoResource.data, UpdateEvent(if(refresh) UpdateEvent.UpdateType.REFRESH else UpdateEvent.UpdateType.PAGE_ADDED, null)))
noMoreContent.postValue(feedDtoResource.data?.moreContent == false) noMoreContent.postValue(feedDtoResource.data?.size == 0)
} }
is Resource.Loading -> { is Resource.Loading -> {

View File

@ -18,10 +18,12 @@ import com.isolaatti.followers.domain.FollowingState
import com.isolaatti.home.FeedFragment import com.isolaatti.home.FeedFragment
import com.isolaatti.posting.PostViewerActivity import com.isolaatti.posting.PostViewerActivity
import com.isolaatti.posting.comments.presentation.BottomSheetPostComments import com.isolaatti.posting.comments.presentation.BottomSheetPostComments
import com.isolaatti.posting.common.domain.Ownable
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options import com.isolaatti.posting.common.options_bottom_sheet.domain.Options
import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel import com.isolaatti.posting.common.options_bottom_sheet.presentation.BottomSheetPostOptionsViewModel
import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment import com.isolaatti.posting.common.options_bottom_sheet.ui.BottomSheetPostOptionsFragment
import com.isolaatti.posting.posts.data.remote.FeedDto import com.isolaatti.posting.posts.data.remote.FeedDto
import com.isolaatti.posting.posts.domain.entity.Post
import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring import com.isolaatti.posting.posts.presentation.PostListingRecyclerViewAdapterWiring
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
import com.isolaatti.posting.posts.presentation.UpdateEvent import com.isolaatti.posting.posts.presentation.UpdateEvent
@ -67,7 +69,7 @@ class ProfileMainFragment : Fragment() {
) )
} }
private val postsObserver: Observer<Pair<FeedDto?, UpdateEvent>?> = Observer { private val postsObserver: Observer<Pair<List<Post>?, UpdateEvent>?> = Observer {
if(it?.first != null) { if(it?.first != null) {
postsAdapter.updateList(it.first!!, it.second) postsAdapter.updateList(it.first!!, it.second)
postsAdapter.newContentRequestFinished() postsAdapter.newContentRequestFinished()
@ -188,7 +190,7 @@ class ProfileMainFragment : Fragment() {
.usePlugin(LinkifyPlugin.create()) .usePlugin(LinkifyPlugin.create())
.build() .build()
postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring, null ) postsAdapter = PostsRecyclerViewAdapter(markwon,postListingRecyclerViewAdapterWiring )
} }
@ -210,8 +212,8 @@ class ProfileMainFragment : Fragment() {
PostViewerActivity.startActivity(requireContext(), postId) PostViewerActivity.startActivity(requireContext(), postId)
} }
override fun onOptions(postId: Long) { override fun onOptions(post: Ownable) {
optionsViewModel.setOptions(Options.myPostOptions, FeedFragment.CALLER_ID, postId) optionsViewModel.setOptions(Options.POST_OPTIONS, FeedFragment.CALLER_ID, post)
val modalBottomSheet = BottomSheetPostOptionsFragment() val modalBottomSheet = BottomSheetPostOptionsFragment()
modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG) modalBottomSheet.show(requireActivity().supportFragmentManager, BottomSheetPostOptionsFragment.TAG)
} }

View File

@ -2,6 +2,8 @@ package com.isolaatti.settings
import com.isolaatti.database.AppDatabase import com.isolaatti.database.AppDatabase
import com.isolaatti.settings.data.KeyValueDao import com.isolaatti.settings.data.KeyValueDao
import com.isolaatti.settings.data.SettingsRepositoryImpl
import com.isolaatti.settings.domain.SettingsRepository
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -14,4 +16,9 @@ class Module {
fun provideKeyValueDao(database: AppDatabase): KeyValueDao { fun provideKeyValueDao(database: AppDatabase): KeyValueDao {
return database.keyValueDao() return database.keyValueDao()
} }
@Provides
fun provideSettingsRepository(keyValueDao: KeyValueDao): SettingsRepository {
return SettingsRepositoryImpl(keyValueDao)
}
} }

View File

@ -8,8 +8,8 @@ import androidx.room.Query
@Dao @Dao
interface KeyValueDao { interface KeyValueDao {
@Query("SELECT value FROM key_values WHERE id = :key") @Query("SELECT value FROM key_values WHERE id = :key")
fun getValue(key: String): String suspend fun getValue(key: String): String
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun setValue(entity: KeyValueEntity) suspend fun setValue(entity: KeyValueEntity)
} }

View File

@ -0,0 +1,13 @@
package com.isolaatti.settings.data
import com.isolaatti.settings.domain.SettingsRepository
class SettingsRepositoryImpl(private val keyValueDao: KeyValueDao) : SettingsRepository {
override suspend fun setKeyValue(key: String, value: String) {
keyValueDao.setValue(KeyValueEntity(key, value))
}
override suspend fun getKeyValue(key: String): String? {
return keyValueDao.getValue(key)
}
}

View File

@ -0,0 +1,6 @@
package com.isolaatti.settings.domain
interface SettingsRepository {
suspend fun setKeyValue(key: String, value: String)
suspend fun getKeyValue(key: String): String?
}

View File

@ -0,0 +1,19 @@
package com.isolaatti.settings.domain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
class UserIdSetting @Inject constructor(private val settingsRepository: SettingsRepository) {
suspend fun getUserId(): Int? {
return settingsRepository.getKeyValue(KEY_USER_ID)?.toIntOrNull()
}
suspend fun setUserId(userId: Int) {
settingsRepository.setKeyValue(KEY_USER_ID, userId.toString())
}
companion object {
const val KEY_USER_ID = "userId"
}
}