This commit is contained in:
Erik Cavazos 2023-08-27 23:03:40 -06:00
parent 633b366baf
commit ca17fe1e22
14 changed files with 171 additions and 43 deletions

View File

@ -308,7 +308,7 @@
<PersistentState> <PersistentState>
<option name="values"> <option name="values">
<map> <map>
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material/icons/materialicons/info/baseline_info_24.xml" /> <entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/download/baseline_download_24.xml" />
</map> </map>
</option> </option>
</PersistentState> </PersistentState>
@ -318,7 +318,7 @@
</option> </option>
<option name="values"> <option name="values">
<map> <map>
<entry key="outputName" value="baseline_info_24" /> <entry key="outputName" value="baseline_download_24" />
<entry key="sourceFile" value="C:\Users\erike\Downloads\isolaatti.svg" /> <entry key="sourceFile" value="C:\Users\erike\Downloads\isolaatti.svg" />
</map> </map>
</option> </option>

View File

@ -7,11 +7,11 @@
<deviceKey> <deviceKey>
<Key> <Key>
<type value="VIRTUAL_DEVICE_PATH" /> <type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\erike\.android\avd\Pixel_3a_API_34_extension_level_7_x86_64.avd" /> <value value="C:\Users\erike\.android\avd\Pixel_5_API_33.avd" />
</Key> </Key>
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-08-19T23:37:01.462209700Z" /> <timeTargetWasSelectedWithDropDown value="2023-08-21T02:45:21.143691700Z" />
</component> </component>
</project> </project>

View File

@ -30,6 +30,7 @@ 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
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.domain.entity.Post
import com.isolaatti.posting.posts.presentation.CreatePostContract import com.isolaatti.posting.posts.presentation.CreatePostContract
import com.isolaatti.posting.posts.presentation.EditPostContract import com.isolaatti.posting.posts.presentation.EditPostContract
import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter import com.isolaatti.posting.posts.presentation.PostsRecyclerViewAdapter
@ -80,26 +81,26 @@ class FeedFragment : Fragment(), OnUserInteractedWithPostCallback {
// region observers // region observers
val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked -> private val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked ->
if(optionClicked?.callerId == CALLER_ID) { if(optionClicked?.callerId == CALLER_ID) {
// post id should come as payload // post id should come as payload
val postId = optionClicked.payload as? Long ?: return@Observer val post = optionClicked.payload as? Post ?: return@Observer
when(optionClicked.optionId) { when(optionClicked.optionId) {
Options.Option.OPTION_DELETE -> { Options.Option.OPTION_DELETE -> {
Dialogs.buildDeletePostDialog(requireContext()) { delete -> Dialogs.buildDeletePostDialog(requireContext()) { delete ->
optionsViewModel.handle() optionsViewModel.handle()
if(delete) { if(delete) {
viewModel.deletePost(postId) viewModel.deletePost(post.id)
} }
}.show() }.show()
} }
Options.Option.OPTION_EDIT -> { Options.Option.OPTION_EDIT -> {
optionsViewModel.handle() optionsViewModel.handle()
editDiscussion.launch(postId) editDiscussion.launch(post.id)
} }
Options.Option.OPTION_REPORT -> { Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
} }
} }
} }

View File

@ -33,11 +33,12 @@ class FeedViewModel @Inject constructor(
postsRepository.getFeed(getLastId()).onEach { listResource -> postsRepository.getFeed(getLastId()).onEach { listResource ->
when (listResource) { when (listResource) {
is Resource.Success -> { is Resource.Success -> {
val eventType = if((postsList?.size ?: 0) > 0) UpdateEvent.UpdateType.PAGE_ADDED else UpdateEvent.UpdateType.REFRESH
loadingPosts.postValue(false) loadingPosts.postValue(false)
posts.postValue(Pair(postsList?.apply { posts.postValue(Pair(postsList?.apply {
addAll(listResource.data ?: listOf()) addAll(listResource.data ?: listOf())
} ?: listResource.data, } ?: listResource.data,
UpdateEvent(UpdateEvent.UpdateType.PAGE_ADDED, null))) UpdateEvent(eventType, null)))
noMoreContent.postValue(listResource.data?.size == 0) noMoreContent.postValue(listResource.data?.size == 0)
} }
@ -50,6 +51,7 @@ class FeedViewModel @Inject constructor(
is Resource.Error -> { is Resource.Error -> {
//errorLoading.postValue(feedDtoResource.errorType) //errorLoading.postValue(feedDtoResource.errorType)
} }
} }
isLoadingFromScrolling = false isLoadingFromScrolling = false
}.flowOn(Dispatchers.IO).launchIn(this) }.flowOn(Dispatchers.IO).launchIn(this)

View File

@ -11,13 +11,17 @@ 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.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.isolaatti.common.Dialogs
import com.isolaatti.databinding.BottomSheetPostCommentsBinding import com.isolaatti.databinding.BottomSheetPostCommentsBinding
import com.isolaatti.home.FeedFragment
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.posting.common.domain.Ownable 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
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.domain.entity.Post
import com.isolaatti.profile.ui.ProfileActivity import com.isolaatti.profile.ui.ProfileActivity
import com.isolaatti.utils.PicassoImagesPluginDef import com.isolaatti.utils.PicassoImagesPluginDef
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -36,9 +40,27 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels() val optionsViewModel: BottomSheetPostOptionsViewModel by activityViewModels()
val optionsObserver: Observer<OptionClicked?> = Observer { val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked ->
if(it?.callerId == CALLER_ID) { if(optionClicked?.callerId == BottomSheetPostComments.CALLER_ID) {
optionsViewModel.optionClicked(-1) val comment = optionClicked.payload as? Comment ?: return@Observer
when(optionClicked.optionId) {
Options.Option.OPTION_DELETE -> {
Dialogs.buildDeletePostDialog(requireContext()) { delete ->
optionsViewModel.handle()
if(delete) {
// remove comment
}
}.show()
}
Options.Option.OPTION_EDIT -> {
optionsViewModel.handle()
//editDiscussion.launch(post.id)
}
Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
}
}
} }
} }
@ -95,7 +117,6 @@ class BottomSheetPostComments() : BottomSheetDialogFragment(), OnUserInteractedC
} }
optionsViewModel.optionClicked(-1)
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver) optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
} }

View File

@ -17,31 +17,61 @@ data class Options(
const val OPTION_DELETE = 1 const val OPTION_DELETE = 1
const val OPTION_EDIT = 2 const val OPTION_EDIT = 2
const val OPTION_REPORT = 3 const val OPTION_REPORT = 3
const val OPTION_SAVE = 4
const val OPTION_SNAPSHOT = 5
} }
} }
companion object { companion object {
const val POST_OPTIONS = 1 const val POST_OPTIONS = 1
const val COMMENT_OPTIONS = 2
val noOptions = Options(0, listOf()) val noOptions = Options(0, 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.edit, R.drawable.baseline_edit_24, Option.OPTION_EDIT),
Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT)
))
val postOptions = Options(R.string.post_options_title, listOf( fun getPostsOptions(userOwned: Boolean, savable: Boolean, snapshotAble: Boolean): Options {
Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT) val list = mutableListOf(
)) Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT)
)
val myCommentOptions = Options(R.string.post_options_title, listOf( if(userOwned) {
Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE), list.addAll(listOf(
Option(R.string.edit, R.drawable.baseline_edit_24, Option.OPTION_EDIT), Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE),
Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT) Option(R.string.edit, R.drawable.baseline_edit_24, Option.OPTION_EDIT))
)) )
}
val commentOptions = Options(R.string.post_options_title, listOf( if(savable) {
Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT) list.add(Option(R.string.save, R.drawable.baseline_save_24, Option.OPTION_SAVE))
)) }
if(snapshotAble) {
list.add(Option(R.string.save_snapshot, R.drawable.baseline_download_24, Option.OPTION_SNAPSHOT))
}
return Options(R.string.post_options_title, list)
}
fun getCommentOptions(userOwned: Boolean, savable: Boolean, snapshotAble: Boolean): Options {
val list = mutableListOf(
Option(R.string.report, R.drawable.baseline_report_24, Option.OPTION_REPORT)
)
if(userOwned) {
list.addAll(listOf(
Option(R.string.delete, R.drawable.baseline_delete_24, Option.OPTION_DELETE),
Option(R.string.edit, R.drawable.baseline_edit_24, Option.OPTION_EDIT))
)
}
if(savable) {
list.add(Option(R.string.save, R.drawable.baseline_save_24, Option.OPTION_SAVE))
}
if(snapshotAble) {
list.add(Option(R.string.save_snapshot, R.drawable.baseline_download_24, Option.OPTION_SNAPSHOT))
}
return Options(R.string.comment_options, list)
}
} }
} }

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
import com.isolaatti.posting.common.domain.Ownable 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.COMMENT_OPTIONS
import com.isolaatti.posting.common.options_bottom_sheet.domain.Options.Companion.POST_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.posting.posts.data.remote.FeedDto
import com.isolaatti.settings.domain.UserIdSetting import com.isolaatti.settings.domain.UserIdSetting
@ -38,27 +39,33 @@ class BottomSheetPostOptionsViewModel @Inject constructor(private val userIdSett
when(options) { when(options) {
POST_OPTIONS -> { POST_OPTIONS -> {
userIdSetting.getUserId()?.let { userId -> userIdSetting.getUserId()?.let { userId ->
if(userId == payload?.userId) { _options.postValue(Options.getPostsOptions(
_options.postValue(Options.myPostOptions) userOwned = userId == payload?.userId,
} else { savable = false,
_options.postValue(Options.postOptions) snapshotAble = false)
} )
_callerId = callerId
_payload = payload
}
}
COMMENT_OPTIONS -> {
userIdSetting.getUserId()?.let { userId ->
_options.postValue(Options.getCommentOptions(
userOwned = userId == payload?.userId,
savable = false,
snapshotAble = false)
)
_callerId = callerId _callerId = callerId
_payload = payload _payload = payload
} }
} }
} }
} }
} }
} }
fun optionClicked(optionId: Int) { fun optionClicked(optionId: Int) {
_optionClicked.postValue(OptionClicked(optionId, _callerId, _payload)) _optionClicked.postValue(OptionClicked(optionId, _callerId, _payload))
} }
} }

View File

@ -55,6 +55,7 @@ class BottomSheetPostOptionsFragment : BottomSheetDialogFragment(), OptionsRecyc
private fun renderOptions(options: Options) { private fun renderOptions(options: Options) {
viewBinding.title.text = requireContext().getText(options.title)
viewBinding.recyclerOptions.adapter = OptionsRecyclerAdapter(options.items, this) viewBinding.recyclerOptions.adapter = OptionsRecyclerAdapter(options.items, this)
viewBinding.recyclerOptions.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) viewBinding.recyclerOptions.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
} }

View File

@ -5,6 +5,7 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
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
@ -13,17 +14,21 @@ import androidx.navigation.fragment.findNavController
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
import com.isolaatti.common.Dialogs
import com.isolaatti.databinding.FragmentDiscussionsBinding import com.isolaatti.databinding.FragmentDiscussionsBinding
import com.isolaatti.followers.domain.FollowingState 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.domain.Ownable
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
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.domain.entity.Post
import com.isolaatti.posting.posts.presentation.CreatePostContract
import com.isolaatti.posting.posts.presentation.EditPostContract
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
@ -53,6 +58,18 @@ class ProfileMainFragment : Fragment() {
private var scrollRange = -1 private var scrollRange = -1
private var isShow = false private var isShow = false
private val createDiscussion = registerForActivityResult(CreatePostContract()) {
if(it != null) {
Toast.makeText(requireContext(), R.string.posted_successfully, Toast.LENGTH_SHORT).show()
}
}
private val editDiscussion = registerForActivityResult(EditPostContract()) {
if(it != null) {
viewModel.onPostUpdate(it)
}
}
private val profileObserver = Observer<UserProfileDto> { profile -> private val profileObserver = Observer<UserProfileDto> { profile ->
Picasso.get() Picasso.get()
.load(UrlGen.userProfileImage(profile.id)) .load(UrlGen.userProfileImage(profile.id))
@ -102,6 +119,31 @@ class ProfileMainFragment : Fragment() {
} }
} }
private val optionsObserver: Observer<OptionClicked?> = Observer { optionClicked ->
if(optionClicked?.callerId == FeedFragment.CALLER_ID) {
// post id should come as payload
val post = optionClicked.payload as? Post ?: return@Observer
when(optionClicked.optionId) {
Options.Option.OPTION_DELETE -> {
Dialogs.buildDeletePostDialog(requireContext()) { delete ->
optionsViewModel.handle()
if(delete) {
viewModel.deletePost(post.id)
}
}.show()
}
Options.Option.OPTION_EDIT -> {
optionsViewModel.handle()
editDiscussion.launch(post.id)
}
Options.Option.OPTION_REPORT -> {
optionsViewModel.handle()
}
}
}
}
private lateinit var postListingRecyclerViewAdapterWiring: PostListingRecyclerViewAdapterWiring private lateinit var postListingRecyclerViewAdapterWiring: PostListingRecyclerViewAdapterWiring
@ -159,6 +201,7 @@ class ProfileMainFragment : Fragment() {
viewModel.profile.observe(viewLifecycleOwner, profileObserver) viewModel.profile.observe(viewLifecycleOwner, profileObserver)
viewModel.posts.observe(viewLifecycleOwner, postsObserver) viewModel.posts.observe(viewLifecycleOwner, postsObserver)
viewModel.followingState.observe(viewLifecycleOwner, followingStateObserver) viewModel.followingState.observe(viewLifecycleOwner, followingStateObserver)
optionsViewModel.optionClicked.observe(viewLifecycleOwner, optionsObserver)
viewModel.loadingPosts.observe(viewLifecycleOwner) { viewModel.loadingPosts.observe(viewLifecycleOwner) {
viewBinding.loadingIndicator.visibility = if(it) View.VISIBLE else View.GONE viewBinding.loadingIndicator.visibility = if(it) View.VISIBLE else View.GONE
if(!it) { if(!it) {

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
</vector>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent" <LinearLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
@ -9,6 +10,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textSize="20sp"
android:textStyle="bold"
tools:text="Title" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_options" android:id="@+id/recycler_options"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -32,6 +32,7 @@
<string name="home">Home</string> <string name="home">Home</string>
<string name="notifications">Notifications</string> <string name="notifications">Notifications</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="comment_options">Comment options</string>
<!-- Audio recorder --> <!-- Audio recorder -->
<string name="audio_recorder">Record audio</string> <string name="audio_recorder">Record audio</string>
@ -68,4 +69,6 @@
<string name="post_will_dropped">This discussion and all related content will be dropped. Continue?</string> <string name="post_will_dropped">This discussion and all related content will be dropped. Continue?</string>
<string name="yes_continue">Yes, delete</string> <string name="yes_continue">Yes, delete</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="save">Save</string>
<string name="save_snapshot">Save snapshot</string>
</resources> </resources>

View File

@ -12,8 +12,8 @@ buildscript {
} }
} }
plugins { plugins {
id 'com.android.application' version '8.1.0' apply false id 'com.android.application' version '8.1.1' apply false
id 'com.android.library' version '8.1.0' apply false id 'com.android.library' version '8.1.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
id 'com.google.dagger.hilt.android' version '2.47' apply false id 'com.google.dagger.hilt.android' version '2.47' apply false
} }