From fc28e9a4a83b5786c8c024793ae7bf937661161d Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Sun, 25 Jun 2023 10:50:05 +0530 Subject: [PATCH 01/35] wip added ui for poll func --- .idea/kotlinc.xml | 2 +- .../screens/common/GenericFeedAdapter.kt | 16 ++ .../screens/home/entities/FeedEntity.kt | 1 + .../screens/poll/CreatePollFragment.kt | 26 +++ .../screens/poll/InteractionPollFragment.kt | 26 +++ app/src/main/res/drawable/ic_poll.xml | 5 + .../main/res/layout/fragment_create_poll.xml | 149 ++++++++++++++++++ .../res/layout/fragment_interaction_poll.xml | 9 ++ .../main/res/layout/layout_home_buttons.xml | 17 ++ .../main/res/layout/poll_create_layout.xml | 8 + 10 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt create mode 100644 app/src/main/res/drawable/ic_poll.xml create mode 100644 app/src/main/res/layout/fragment_create_poll.xml create mode 100644 app/src/main/res/layout/fragment_interaction_poll.xml create mode 100644 app/src/main/res/layout/layout_home_buttons.xml create mode 100644 app/src/main/res/layout/poll_create_layout.xml diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index e1eea1d..2b8a50f 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 339896b..99e313a 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -25,6 +25,9 @@ class GenericFeedAdapter( fun onSignOutClicked() fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() + + fun onCreatePollButtonClicked() + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -80,6 +83,19 @@ class GenericFeedAdapter( } }) } + + FeedEntity.TYPE_CREATE_POLL -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.layout_home_buttons, parent, false) + return DoubtPreviewViewHolder( + view = view, + showVotingLayout = false, + interactionListener = object : DoubtPreviewViewHolder.InteractionListener { + override fun onDoubtClicked(doubtData: DoubtData, position: Int) { + interactionListener.onDoubtClicked(doubtData, position) + } + }) + } } throw Exception("type is not defined") diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index 1c455b8..bd096ac 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -13,6 +13,7 @@ data class FeedEntity( const val TYPE_SEARCH = 2 const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 + const val TYPE_CREATE_POLL = 5 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt new file mode 100644 index 0000000..5987ab3 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt @@ -0,0 +1,26 @@ +package com.doubtless.doubtless.screens.poll + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.doubtless.doubtless.R + + +class CreatePollFragment : Fragment() { + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_create_poll, container, false) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt new file mode 100644 index 0000000..e353568 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt @@ -0,0 +1,26 @@ +package com.doubtless.doubtless.screens.poll + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.doubtless.doubtless.R + + +class InteractionPollFragment : Fragment() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_interaction_poll, container, false) + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_poll.xml b/app/src/main/res/drawable/ic_poll.xml new file mode 100644 index 0000000..2411491 --- /dev/null +++ b/app/src/main/res/drawable/ic_poll.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_create_poll.xml b/app/src/main/res/layout/fragment_create_poll.xml new file mode 100644 index 0000000..b421a0b --- /dev/null +++ b/app/src/main/res/layout/fragment_create_poll.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_interaction_poll.xml b/app/src/main/res/layout/fragment_interaction_poll.xml new file mode 100644 index 0000000..6b0557d --- /dev/null +++ b/app/src/main/res/layout/fragment_interaction_poll.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_home_buttons.xml b/app/src/main/res/layout/layout_home_buttons.xml new file mode 100644 index 0000000..a83c8b1 --- /dev/null +++ b/app/src/main/res/layout/layout_home_buttons.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/poll_create_layout.xml b/app/src/main/res/layout/poll_create_layout.xml new file mode 100644 index 0000000..617f11f --- /dev/null +++ b/app/src/main/res/layout/poll_create_layout.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file From 163bb27b1da8d84107f124d3ad9f1ab404607b7c Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Tue, 27 Jun 2023 19:30:24 +0530 Subject: [PATCH 02/35] update: ui for poll create and view --- .../screens/common/GenericFeedAdapter.kt | 4 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 2 +- .../screens/home/entities/FeedEntity.kt | 2 +- .../screens/poll/InteractionPollFragment.kt | 26 ---- .../main/res/layout/fragment_create_poll.xml | 53 +++++-- .../res/layout/fragment_interaction_poll.xml | 9 -- app/src/main/res/layout/interaction_poll.xml | 133 ++++++++++++++++++ 7 files changed, 179 insertions(+), 50 deletions(-) delete mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt delete mode 100644 app/src/main/res/layout/fragment_interaction_poll.xml create mode 100644 app/src/main/res/layout/interaction_poll.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 99e313a..9a1d606 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -26,8 +26,6 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() - fun onCreatePollButtonClicked() - } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -84,7 +82,7 @@ class GenericFeedAdapter( }) } - FeedEntity.TYPE_CREATE_POLL -> { + FeedEntity.TYPE_BUTTONS -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.layout_home_buttons, parent, false) return DoubtPreviewViewHolder( diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 439e3e7..b35390b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -41,7 +41,7 @@ class ViewDoubtsFragment : Fragment() { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) - //enterTransition = inflater.inflateTransition(R.transition.slide) + // enterTransition = inflater.inflateTransition(R.transition.slide) // exitTransition = inflater.inflateTransition(R.transition.fade) userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index bd096ac..e521baf 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -13,7 +13,7 @@ data class FeedEntity( const val TYPE_SEARCH = 2 const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 - const val TYPE_CREATE_POLL = 5 + const val TYPE_BUTTONS = 5 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt deleted file mode 100644 index e353568..0000000 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.doubtless.doubtless.screens.poll - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.doubtless.doubtless.R - - -class InteractionPollFragment : Fragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?, - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_interaction_poll, container, false) - } - -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_create_poll.xml b/app/src/main/res/layout/fragment_create_poll.xml index b421a0b..1ac6e0c 100644 --- a/app/src/main/res/layout/fragment_create_poll.xml +++ b/app/src/main/res/layout/fragment_create_poll.xml @@ -70,10 +70,12 @@ app:layout_constraintTop_toBottomOf="@id/question_header" tools:ignore="Autofill,HardcodedText,LabelFor,SpeakableTextPresentCheck,VisualLintTextFieldSize" /> + - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_interaction_poll.xml b/app/src/main/res/layout/fragment_interaction_poll.xml deleted file mode 100644 index 6b0557d..0000000 --- a/app/src/main/res/layout/fragment_interaction_poll.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/interaction_poll.xml b/app/src/main/res/layout/interaction_poll.xml new file mode 100644 index 0000000..4a74ed3 --- /dev/null +++ b/app/src/main/res/layout/interaction_poll.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 620bd6426b127b3d97d1532f529f20999c110ceb Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Tue, 27 Jun 2023 21:10:29 +0530 Subject: [PATCH 03/35] added create poll button in ViewDoubtsFragment --- .../doubtless/navigation/FragNavigator.kt | 9 +++++++ .../common/ExtraOptionsButtonHolder.kt | 27 +++++++++++++++++++ .../screens/common/GenericFeedAdapter.kt | 17 ++++++------ .../screens/doubt/view/ViewDoubtsFragment.kt | 4 +++ .../screens/doubt/view/ViewDoubtsViewModel.kt | 4 ++- .../screens/home/entities/FeedEntity.kt | 4 +++ 6 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/common/ExtraOptionsButtonHolder.kt diff --git a/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt b/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt index 6655236..ddabab7 100644 --- a/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt +++ b/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt @@ -5,6 +5,7 @@ import androidx.fragment.app.FragmentManager import com.doubtless.doubtless.R import com.doubtless.doubtless.screens.answers.AnswersFragment import com.doubtless.doubtless.screens.doubt.DoubtData +import com.doubtless.doubtless.screens.poll.CreatePollFragment import com.doubtless.doubtless.screens.search.SearchFragment class FragNavigator constructor( @@ -44,4 +45,12 @@ class FragNavigator constructor( .commitAllowingStateLoss() } + fun moveToCreatePollFragment(){ + supportFragmentManager.beginTransaction() + .replace(containerId, CreatePollFragment()) + .addToBackStack(null) + .setReorderingAllowed(true) + .commitAllowingStateLoss() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/ExtraOptionsButtonHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/ExtraOptionsButtonHolder.kt new file mode 100644 index 0000000..312fca3 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/ExtraOptionsButtonHolder.kt @@ -0,0 +1,27 @@ +package com.doubtless.doubtless.screens.common + +import android.view.View +import android.widget.ImageView +import androidx.recyclerview.widget.RecyclerView +import com.doubtless.doubtless.R +import com.doubtless.doubtless.navigation.FragNavigator + +class ExtraOptionsButtonHolder(val view: View, val interactionListener: InteractionListener) + : RecyclerView.ViewHolder(view) { + + + interface InteractionListener{ + fun onCreatePollClicked() + } + + private val createPoll: ImageView + + + init { + createPoll = view.findViewById(R.id.ic_poll) + + createPoll.setOnClickListener { + interactionListener.onCreatePollClicked() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 9a1d606..f49360d 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -25,6 +25,7 @@ class GenericFeedAdapter( fun onSignOutClicked() fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() + fun onCreatePollClicked() } @@ -85,14 +86,14 @@ class GenericFeedAdapter( FeedEntity.TYPE_BUTTONS -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.layout_home_buttons, parent, false) - return DoubtPreviewViewHolder( - view = view, - showVotingLayout = false, - interactionListener = object : DoubtPreviewViewHolder.InteractionListener { - override fun onDoubtClicked(doubtData: DoubtData, position: Int) { - interactionListener.onDoubtClicked(doubtData, position) - } - }) + return ExtraOptionsButtonHolder(view= view, + object : ExtraOptionsButtonHolder.InteractionListener{ + override fun onCreatePollClicked() { + interactionListener.onCreatePollClicked() + } + + } + ) } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index b35390b..ff7020b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -121,6 +121,10 @@ class ViewDoubtsFragment : Fragment() { override fun onDeleteAccountClicked() { } + + override fun onCreatePollClicked() { + navigator.moveToCreatePollFragment() + } }) // how is rv restoring its scroll pos when switching tabs? diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 67c90aa..9303bc2 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -69,9 +69,11 @@ class ViewDoubtsViewModel constructor( } } - // for page 1 call add search entity + // for page 1 call add search and options button entity if (_homeEntities.isEmpty()) entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + if (_homeEntities.isEmpty()) + entitiesFromServer.add(1, FeedEntity.getOptionButtons()) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(entitiesFromServer) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index e521baf..b4fd0dc 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -18,5 +18,9 @@ data class FeedEntity( fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) } + + fun getOptionButtons(): FeedEntity{ + return FeedEntity(TYPE_BUTTONS, null, null) + } } } \ No newline at end of file From cd6d5ddc128dc6da999459af979f69b37acd299e Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Wed, 28 Jun 2023 20:59:36 +0530 Subject: [PATCH 04/35] view poll vieholder added , changes in ui --- .../screens/dashboard/DashboardFragment.kt | 4 + .../screens/doubt/view/ViewDoubtsFragment.kt | 2 + .../screens/doubt/view/ViewDoubtsViewModel.kt | 2 + .../view/viewholder/DoubtPreviewViewHolder.kt | 8 +- .../screens/home/entities/FeedEntity.kt | 4 + .../screens/poll/CreatePollFragment.kt | 19 +++- .../screens/poll/ViewPollViewHolder.kt | 41 +++++++++ .../screens/search/SearchFragment.kt | 1 + .../main/res/drawable/poll_item_border.xml | 5 ++ app/src/main/res/layout/doubt_layout.xml | 8 +- .../main/res/layout/fragment_create_poll.xml | 27 ++++-- .../{interaction_poll.xml => view_poll.xml} | 87 +++++++++++-------- 12 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt create mode 100644 app/src/main/res/drawable/poll_item_border.xml rename app/src/main/res/layout/{interaction_poll.xml => view_poll.xml} (61%) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index 496644e..5e2d38e 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -102,6 +102,10 @@ class DashboardFragment : Fragment() { override fun onDeleteAccountClicked() { showBottomSheet() } + + override fun onCreatePollClicked() { + + } }) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index ff7020b..6de1ac4 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -125,6 +125,8 @@ class ViewDoubtsFragment : Fragment() { override fun onCreatePollClicked() { navigator.moveToCreatePollFragment() } + + }) // how is rv restoring its scroll pos when switching tabs? diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 9303bc2..6620956 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -74,6 +74,8 @@ class ViewDoubtsViewModel constructor( entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) + if(_homeEntities.isEmpty()) + entitiesFromServer.add(6, FeedEntity.getPollView() ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(entitiesFromServer) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index 2d37b9b..a73acb6 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -1,13 +1,9 @@ package com.doubtless.doubtless.screens.doubt.view.viewholder -import android.content.res.ColorStateList import android.text.util.Linkify -import android.util.Log import android.view.View import android.widget.ImageView import android.widget.TextView -import android.widget.Toast -import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide @@ -53,8 +49,8 @@ class DoubtPreviewViewHolder( init { userName = view.findViewById(R.id.tv_username) time = view.findViewById(R.id.author_doubt_timestamp) - heading = view.findViewById(R.id.user_doubt_heading) - description = view.findViewById(R.id.user_doubt_description) + heading = view.findViewById(R.id.tv_poll_heading) + description = view.findViewById(R.id.tv_poll_description) ivDp = view.findViewById(R.id.iv_dp) tvNetVotes = view.findViewById(R.id.tv_votes) tvAnswers = view.findViewById(R.id.tv_answers) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index b4fd0dc..27ab211 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -14,6 +14,7 @@ data class FeedEntity( const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 const val TYPE_BUTTONS = 5 + const val TYPE_POLL_VOTE = 6 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) @@ -22,5 +23,8 @@ data class FeedEntity( fun getOptionButtons(): FeedEntity{ return FeedEntity(TYPE_BUTTONS, null, null) } + fun getPollView(): FeedEntity{ + return FeedEntity(TYPE_POLL_VOTE, null, null) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt index 5987ab3..b4e1182 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt @@ -6,10 +6,14 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.doubtless.doubtless.R +import com.doubtless.doubtless.databinding.FragmentCreatePollBinding class CreatePollFragment : Fragment() { + private var _binding : FragmentCreatePollBinding? = null + private val binding get() = _binding!! + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -20,7 +24,20 @@ class CreatePollFragment : Fragment() { savedInstanceState: Bundle?, ): View? { // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_create_poll, container, false) + _binding = FragmentCreatePollBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.topbarPoll.setOnClickListener { + requireActivity().onBackPressed() + } + } + + override fun onDestroy() { + super.onDestroy() + _binding=null } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt new file mode 100644 index 0000000..373d625 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt @@ -0,0 +1,41 @@ +package com.doubtless.doubtless.screens.poll + +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.ProgressBar +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.doubtless.doubtless.R + +class ViewPollViewHolder(val view: View, val interactionListener: InteractionListener):RecyclerView.ViewHolder(view) { + + interface InteractionListener{ + fun onPollOptionClicked() + } + + private val userName: TextView + private val heading: TextView + private val description: TextView + private val college: TextView + private val ivDp: ImageView + private val tvOption: TextView + private val progressBar: ProgressBar + private val llOptions: LinearLayout + private val time: TextView + private val tvYear: TextView + + init { + userName = view.findViewById(R.id.tv_username_poll) + heading = view.findViewById(R.id.tv_poll_heading) + description = view.findViewById(R.id.tv_poll_description) + college = view.findViewById(R.id.tv_poll_college) + ivDp = view.findViewById(R.id.tv_user_dp_poll) + progressBar = view.findViewById(R.id.progress_poll) + tvOption = view.findViewById(R.id. tv_option) + llOptions = view.findViewById(R.id.ll_options) + time = view.findViewById(R.id.author_doubt_timestamp2) + tvYear = view.findViewById(R.id.user_year2) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 92c71fb..3e3dfe7 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -70,6 +70,7 @@ class SearchFragment : Fragment() { override fun onSubmitFeedbackClicked() {} override fun onDeleteAccountClicked() {} + override fun onCreatePollClicked() {} }) } diff --git a/app/src/main/res/drawable/poll_item_border.xml b/app/src/main/res/drawable/poll_item_border.xml new file mode 100644 index 0000000..5f47c34 --- /dev/null +++ b/app/src/main/res/drawable/poll_item_border.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/doubt_layout.xml b/app/src/main/res/layout/doubt_layout.xml index 4a0324e..e1455dc 100644 --- a/app/src/main/res/layout/doubt_layout.xml +++ b/app/src/main/res/layout/doubt_layout.xml @@ -100,7 +100,7 @@ app:tint="@color/purple" /> + app:layout_constraintTop_toBottomOf="@id/tv_poll_heading" /> + app:layout_constraintTop_toBottomOf="@id/tv_poll_description" /> + app:layout_constraintTop_toBottomOf="@+id/option_header" + android:orientation="horizontal"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/interaction_poll.xml b/app/src/main/res/layout/view_poll.xml similarity index 61% rename from app/src/main/res/layout/interaction_poll.xml rename to app/src/main/res/layout/view_poll.xml index 4a74ed3..8867b18 100644 --- a/app/src/main/res/layout/interaction_poll.xml +++ b/app/src/main/res/layout/view_poll.xml @@ -1,23 +1,23 @@ + android:padding="8dp"> + app:layout_constraintStart_toStartOf="@id/tv_username_poll" + app:layout_constraintTop_toBottomOf="@id/tv_username_poll" /> + app:layout_constraintStart_toEndOf="@id/tv_username_poll" + app:layout_constraintTop_toTopOf="@id/tv_username_poll" /> + app:layout_constraintTop_toTopOf="@id/tv_username_poll" /> + app:layout_constraintTop_toBottomOf="@id/tv_user_dp_poll" /> + app:layout_constraintTop_toBottomOf="@id/tv_poll_heading" /> - - - - + app:layout_constraintTop_toBottomOf="@+id/tv_poll_description"> + + + + + + + + + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/ll_options" /> \ No newline at end of file From 16cea41604190a97b282a4211e596612b7f13684 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Thu, 29 Jun 2023 10:44:08 +0530 Subject: [PATCH 05/35] added poll view in main feed with multiple options, poll func WIP --- .../screens/common/GenericFeedAdapter.kt | 20 +++++++++++- .../screens/dashboard/DashboardFragment.kt | 4 +-- .../screens/doubt/view/ViewDoubtsFragment.kt | 4 +++ .../screens/doubt/view/ViewDoubtsViewModel.kt | 4 +-- .../screens/home/entities/FeedEntity.kt | 9 +++--- .../screens/poll/ViewPollViewHolder.kt | 30 +++++++++++++++++- .../screens/search/SearchFragment.kt | 1 + .../main/res/layout/poll_options_layout.xml | 31 +++++++++++++++++++ app/src/main/res/layout/view_poll.xml | 2 -- 9 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 app/src/main/res/layout/poll_options_layout.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index f49360d..415b7aa 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -12,6 +12,7 @@ import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder +import com.doubtless.doubtless.screens.poll.ViewPollViewHolder class GenericFeedAdapter( private val genericFeedEntities: MutableList, @@ -26,7 +27,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - + fun onPollOptionClicked(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -95,6 +96,19 @@ class GenericFeedAdapter( } ) } + + FeedEntity.TYPE_POLL -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.view_poll, parent, false) + return ViewPollViewHolder( + view = view, + interactionListener = object : ViewPollViewHolder.InteractionListener { + override fun onPollOptionClicked(position: Int) { + interactionListener.onPollOptionClicked(position) + } + } + ) + } } throw Exception("type is not defined") @@ -111,6 +125,10 @@ class GenericFeedAdapter( if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_SEARCH_RESULT) holder.setData(genericFeedEntities[position].search_doubt!!.toDoubtData()) + if (holder is ViewPollViewHolder && getItemViewType(position) == FeedEntity.TYPE_POLL) + holder.setData(genericFeedEntities[position]) + + if (position == itemCount - 1) { onLastItemReached.invoke() } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index 5e2d38e..2d4b0b1 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -103,9 +103,9 @@ class DashboardFragment : Fragment() { showBottomSheet() } - override fun onCreatePollClicked() { + override fun onCreatePollClicked() {} - } + override fun onPollOptionClicked(position: Int) {} }) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 6de1ac4..7e92412 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -126,6 +126,10 @@ class ViewDoubtsFragment : Fragment() { navigator.moveToCreatePollFragment() } + override fun onPollOptionClicked(position: Int) { + + } + }) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 6620956..71dfca3 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -68,14 +68,14 @@ class ViewDoubtsViewModel constructor( _homeEntitiesIds[doubtData.id!!] = 1 } } - + val pollOptions = listOf("Option 1", "Option 2", "Option 3") // for page 1 call add search and options button entity if (_homeEntities.isEmpty()) entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollView() ) + entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(entitiesFromServer) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index 27ab211..a9bf04c 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -6,7 +6,8 @@ import com.doubtless.doubtless.screens.search.SearchResult data class FeedEntity( val type: Int, val doubt: DoubtData? = null, - val search_doubt: SearchResult? = null + val search_doubt: SearchResult? = null, + val pollOptions: List? = null ) { companion object { const val TYPE_DOUBT = 1 @@ -14,7 +15,7 @@ data class FeedEntity( const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 const val TYPE_BUTTONS = 5 - const val TYPE_POLL_VOTE = 6 + const val TYPE_POLL = 6 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) @@ -23,8 +24,8 @@ data class FeedEntity( fun getOptionButtons(): FeedEntity{ return FeedEntity(TYPE_BUTTONS, null, null) } - fun getPollView(): FeedEntity{ - return FeedEntity(TYPE_POLL_VOTE, null, null) + fun getPollEntity(pollOptions: List): FeedEntity { + return FeedEntity(TYPE_POLL, null, null, pollOptions) } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt index 373d625..4ac224a 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt @@ -1,5 +1,6 @@ package com.doubtless.doubtless.screens.poll +import android.view.LayoutInflater import android.view.View import android.widget.ImageView import android.widget.LinearLayout @@ -7,11 +8,12 @@ import android.widget.ProgressBar import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.doubtless.doubtless.R +import com.doubtless.doubtless.screens.home.entities.FeedEntity class ViewPollViewHolder(val view: View, val interactionListener: InteractionListener):RecyclerView.ViewHolder(view) { interface InteractionListener{ - fun onPollOptionClicked() + fun onPollOptionClicked(position : Int) } private val userName: TextView @@ -36,6 +38,32 @@ class ViewPollViewHolder(val view: View, val interactionListener: InteractionLis llOptions = view.findViewById(R.id.ll_options) time = view.findViewById(R.id.author_doubt_timestamp2) tvYear = view.findViewById(R.id.user_year2) + + } + + fun setData(feedEntity: FeedEntity) { + llOptions.removeAllViews() + + val pollOptions = feedEntity.pollOptions + if (pollOptions != null) { + for (i in pollOptions.indices) { + val option = pollOptions[i] + val optionView = createOptionView(option, i) + llOptions.addView(optionView) + } + } + } + private fun createOptionView(option: String, position: Int): View { + val optionView = LayoutInflater.from(view.context) + .inflate(R.layout.poll_options_layout, llOptions, false) + val tvOption = optionView.findViewById(R.id.tv_option) + tvOption.text = option + + optionView.setOnClickListener { + interactionListener.onPollOptionClicked(position) + } + + return optionView } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 3e3dfe7..be172cf 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -71,6 +71,7 @@ class SearchFragment : Fragment() { override fun onDeleteAccountClicked() {} override fun onCreatePollClicked() {} + override fun onPollOptionClicked(position: Int) {} }) } diff --git a/app/src/main/res/layout/poll_options_layout.xml b/app/src/main/res/layout/poll_options_layout.xml new file mode 100644 index 0000000..5caafa5 --- /dev/null +++ b/app/src/main/res/layout/poll_options_layout.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_poll.xml b/app/src/main/res/layout/view_poll.xml index 8867b18..3498cf0 100644 --- a/app/src/main/res/layout/view_poll.xml +++ b/app/src/main/res/layout/view_poll.xml @@ -99,13 +99,11 @@ android:layout_marginStart="16dp" android:layout_marginTop="6dp" android:layout_marginEnd="16dp" - android:background="@drawable/poll_item_border" android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_poll_description"> - From 742efc253acf162bbd7b826d2bf8e211732ffa4a Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Thu, 29 Jun 2023 12:05:27 +0530 Subject: [PATCH 06/35] poll vote WIP --- .../screens/common/GenericFeedAdapter.kt | 6 ++--- .../screens/dashboard/DashboardFragment.kt | 2 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 2 +- .../screens/poll/ViewPollViewHolder.kt | 26 +++++++++++++++++-- .../screens/search/SearchFragment.kt | 2 +- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 415b7aa..be60b18 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -27,7 +27,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - fun onPollOptionClicked(position: Int) + fun onPollOptionClicked(position: Int, option: String) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -103,8 +103,8 @@ class GenericFeedAdapter( return ViewPollViewHolder( view = view, interactionListener = object : ViewPollViewHolder.InteractionListener { - override fun onPollOptionClicked(position: Int) { - interactionListener.onPollOptionClicked(position) + override fun onPollOptionClicked(position: Int, option: String) { + interactionListener.onPollOptionClicked(position, option) } } ) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index 2d4b0b1..7c1bbf8 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -105,7 +105,7 @@ class DashboardFragment : Fragment() { override fun onCreatePollClicked() {} - override fun onPollOptionClicked(position: Int) {} + override fun onPollOptionClicked(position: Int, option: String) {} }) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 7e92412..a289596 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -126,7 +126,7 @@ class ViewDoubtsFragment : Fragment() { navigator.moveToCreatePollFragment() } - override fun onPollOptionClicked(position: Int) { + override fun onPollOptionClicked(position: Int, option: String) { } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt index 4ac224a..9b48236 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt @@ -13,7 +13,7 @@ import com.doubtless.doubtless.screens.home.entities.FeedEntity class ViewPollViewHolder(val view: View, val interactionListener: InteractionListener):RecyclerView.ViewHolder(view) { interface InteractionListener{ - fun onPollOptionClicked(position : Int) + fun onPollOptionClicked(position : Int, option: String) } private val userName: TextView @@ -53,6 +53,7 @@ class ViewPollViewHolder(val view: View, val interactionListener: InteractionLis } } } + private fun createOptionView(option: String, position: Int): View { val optionView = LayoutInflater.from(view.context) .inflate(R.layout.poll_options_layout, llOptions, false) @@ -60,10 +61,31 @@ class ViewPollViewHolder(val view: View, val interactionListener: InteractionLis tvOption.text = option optionView.setOnClickListener { - interactionListener.onPollOptionClicked(position) + interactionListener.onPollOptionClicked(position, option) + updateProgress() } return optionView } + private fun updateProgress() { + // Calculate the total number of votes for all options + var totalVotes = 0 + for (i in 0 until llOptions.childCount) { + val optionView = llOptions.getChildAt(i) as LinearLayout + val voteCount = optionView.findViewById(R.id.vote_count).text.toString().toInt() + totalVotes += voteCount + } + + // Update the progress percentage for each option + for (i in 0 until llOptions.childCount) { + val optionView = llOptions.getChildAt(i) as LinearLayout + val progressPoll = optionView.findViewById(R.id.progress_poll) + val voteCount = optionView.findViewById(R.id.vote_count).text.toString().toInt() + + val progress = if (totalVotes > 0) (voteCount.toFloat() / totalVotes * 100).toInt() else 0 + progressPoll.progress = progress + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index be172cf..3793157 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -71,7 +71,7 @@ class SearchFragment : Fragment() { override fun onDeleteAccountClicked() {} override fun onCreatePollClicked() {} - override fun onPollOptionClicked(position: Int) {} + override fun onPollOptionClicked(position: Int, option :String) {} }) } From d89d9e454f6bc481de95437052df19ca02ec6999 Mon Sep 17 00:00:00 2001 From: Prattham Arora <55047418+PratthamArora@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:29:29 +0530 Subject: [PATCH 07/35] fix fatal NPE for ViewDoubtViewModel (#73) * fix fatal NPE * logout user if user is null * code clean up * add non fatal log for null user --- .../auth/exception/UserNotFoundException.kt | 3 + .../screens/auth/login/LoginUtils.kt | 8 ++ .../screens/auth/login/LoginUtilsImpl.kt | 39 +++++++ .../screens/doubt/view/ViewDoubtsFragment.kt | 48 +++++++-- .../screens/doubt/view/ViewDoubtsViewModel.kt | 101 ++++++++++-------- .../com/doubtless/doubtless/utils/Resource.kt | 12 +++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 162 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/auth/exception/UserNotFoundException.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtils.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtilsImpl.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/utils/Resource.kt diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/exception/UserNotFoundException.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/exception/UserNotFoundException.kt new file mode 100644 index 0000000..961485b --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/exception/UserNotFoundException.kt @@ -0,0 +1,3 @@ +package com.doubtless.doubtless.screens.auth.exception + +class UserNotFoundException : Exception() \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtils.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtils.kt new file mode 100644 index 0000000..1468d0c --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtils.kt @@ -0,0 +1,8 @@ +package com.doubtless.doubtless.screens.auth.login + +import android.app.Activity +import com.doubtless.doubtless.analytics.AnalyticsTracker + +interface LoginUtils { + fun logOutUser(analyticsTracker: AnalyticsTracker, activity: Activity) +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtilsImpl.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtilsImpl.kt new file mode 100644 index 0000000..b580342 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/login/LoginUtilsImpl.kt @@ -0,0 +1,39 @@ +package com.doubtless.doubtless.screens.auth.login + +import android.app.Activity +import android.widget.Toast +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.analytics.AnalyticsTracker +import com.doubtless.doubtless.screens.auth.usecases.UserManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + + +object LoginUtilsImpl : LoginUtils { + + override fun logOutUser(analyticsTracker: AnalyticsTracker, activity: Activity) { + analyticsTracker.trackLogout() + CoroutineScope(Dispatchers.Main).launch { + + val result = withContext(Dispatchers.IO) { + DoubtlessApp.getInstance().getAppCompRoot().getUserManager().onUserLogoutSync() + } + + if (result is UserManager.Result.LoggedOut) { + + DoubtlessApp.getInstance().getAppCompRoot().router.moveToLoginActivity( + activity + ) + activity.finish() + + } else if (result is UserManager.Result.Error) { + + Toast.makeText( + activity, result.message, Toast.LENGTH_LONG + ).show() // encapsulate error ui handling + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index a289596..d749f41 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -5,20 +5,24 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import com.doubtless.doubtless.DoubtlessApp -import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker import com.doubtless.doubtless.databinding.FragmentViewDoubtsBinding import com.doubtless.doubtless.navigation.FragNavigator -import com.doubtless.doubtless.screens.common.GenericFeedAdapter +import com.doubtless.doubtless.screens.auth.exception.UserNotFoundException +import com.doubtless.doubtless.screens.auth.login.LoginUtilsImpl import com.doubtless.doubtless.screens.auth.usecases.UserManager +import com.doubtless.doubtless.screens.common.GenericFeedAdapter import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.home.entities.FeedConfig import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.utils.Resource +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.gson.Gson @@ -137,15 +141,45 @@ class ViewDoubtsFragment : Fragment() { binding.doubtsRecyclerView.adapter = adapter binding.doubtsRecyclerView.layoutManager = LinearLayoutManager(context) - viewModel.fetchedHomeEntities.observe(viewLifecycleOwner) { - binding.llProgressBar.visibility= View.GONE //hide progress bar - if (it == null) return@observe - adapter.appendDoubts(it) - viewModel.notifyFetchedDoubtsConsumed() + viewModel.fetchedHomeEntities.observe(viewLifecycleOwner) { result -> + binding.llProgressBar.visibility = View.GONE //hide progress bar binding.layoutSwipe.isRefreshing = false + result?.let { + when (result) { + is Resource.Success -> { + result.data?.let { + adapter.appendDoubts(it) + } + } + is Resource.Error -> { + when (result.error) { + is UserNotFoundException -> { + if (!isAdded) return@observe + FirebaseCrashlytics.getInstance() + .recordException(Exception("current user is null")) + LoginUtilsImpl.logOutUser(analyticsTracker, requireActivity()) + result.message?.let { it1 -> showToast(it1) } + } + else -> { + result.message?.let { + showToast(it) + } + } + } + } + is Resource.Loading -> { + } + } + } + } } + private fun showToast(msg: String) { + Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT) + .show() + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 71dfca3..00ebb07 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -1,11 +1,16 @@ package com.doubtless.doubtless.screens.doubt.view import androidx.lifecycle.* +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker +import com.doubtless.doubtless.screens.auth.exception.UserNotFoundException import com.doubtless.doubtless.screens.auth.usecases.UserManager import com.doubtless.doubtless.screens.home.entities.FeedEntity import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase -import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase.* +import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase.FetchHomeFeedRequest +import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase.Result +import com.doubtless.doubtless.utils.Resource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -22,12 +27,12 @@ class ViewDoubtsViewModel constructor( private var isLoading = false - private val _fetchedHomeEntities = MutableLiveData?>() - val fetchedHomeEntities: LiveData?> = + private val _fetchedHomeEntities = MutableLiveData?>>() + val fetchedHomeEntities: LiveData?>> = _fetchedHomeEntities // TODO : use Result here! fun notifyFetchedDoubtsConsumed() { - _fetchedHomeEntities.value = null + _fetchedHomeEntities.value = Resource.Success(data = null) } fun fetchDoubts(forPageOne: Boolean = false) = viewModelScope.launch(Dispatchers.IO) { @@ -36,54 +41,64 @@ class ViewDoubtsViewModel constructor( isLoading = true - val result = fetchHomeFeedUseCase.fetchFeedSync( - request = FetchHomeFeedRequest( - user = userManager.getCachedUserData()!!, - fetchFromPage1 = forPageOne + val currentUser = userManager.getCachedUserData() ?: userManager.getLoggedInUser() + currentUser?.let { user -> + val result = fetchHomeFeedUseCase.fetchFeedSync( + request = FetchHomeFeedRequest( + user = user, + fetchFromPage1 = forPageOne + ) ) - ) - if (result is Result.ListEnded || result is Result.Error) { - // ERROR CASE - _fetchedHomeEntities.postValue(null) - isLoading = false - return@launch - } + if (result is Result.ListEnded || result is Result.Error) { + // ERROR CASE + _fetchedHomeEntities.postValue(Resource.Error()) + isLoading = false + return@launch + } - result as FetchHomeFeedUseCase.Result.Success + result as FetchHomeFeedUseCase.Result.Success - if (!forPageOne) { - analyticsTracker.trackFeedNextPage(homeEntities.size) - } else { - analyticsTracker.trackFeedRefresh() - } + if (!forPageOne) { + analyticsTracker.trackFeedNextPage(homeEntities.size) + } else { + analyticsTracker.trackFeedRefresh() + } - val entitiesFromServer = mutableListOf() + val entitiesFromServer = mutableListOf() - result.data.forEach { doubtData -> + result.data.forEach { doubtData -> - // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. - if (_homeEntitiesIds.contains(doubtData.id) == false) { - entitiesFromServer.add(doubtData.toHomeEntity()) - _homeEntitiesIds[doubtData.id!!] = 1 + // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. + if (_homeEntitiesIds.contains(doubtData.id) == false) { + entitiesFromServer.add(doubtData.toHomeEntity()) + _homeEntitiesIds[doubtData.id!!] = 1 + } } + + // for page 1 call add search entity + if (_homeEntities.isEmpty()) + entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + + _homeEntities.addAll(entitiesFromServer) + _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) + fetchHomeFeedUseCase.notifyDistinctDocsFetched( + docsFetched = homeEntities.size + - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 + ) + isLoading = false + } ?: kotlin.run { + // current user is null + // ERROR CASE + _fetchedHomeEntities.postValue( + Resource.Error( + message = DoubtlessApp.getInstance().getString(R.string.sign_in_again), + data = null, + error = UserNotFoundException() + ) + ) + isLoading = false } - val pollOptions = listOf("Option 1", "Option 2", "Option 3") - // for page 1 call add search and options button entity - if (_homeEntities.isEmpty()) - entitiesFromServer.add(0, FeedEntity.getSearchEntity()) - if (_homeEntities.isEmpty()) - entitiesFromServer.add(1, FeedEntity.getOptionButtons()) - if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) - - _homeEntities.addAll(entitiesFromServer) - _fetchedHomeEntities.postValue(entitiesFromServer) - fetchHomeFeedUseCase.notifyDistinctDocsFetched( - docsFetched = homeEntities.size - - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 - ) - isLoading = false } fun refreshList() { diff --git a/app/src/main/java/com/doubtless/doubtless/utils/Resource.kt b/app/src/main/java/com/doubtless/doubtless/utils/Resource.kt new file mode 100644 index 0000000..1907ac5 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/utils/Resource.kt @@ -0,0 +1,12 @@ +package com.doubtless.doubtless.utils + +sealed class Resource( + val data: T? = null, + val message: String? = null, + val error: Exception? = null +) { + class Success(data: T) : Resource(data) + class Loading : Resource() + class Error(message: String? = null, data: T? = null, error: Exception? = null) : + Resource(data, message, error) +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d165fb3..181b759 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,4 +19,5 @@ Higher Studies Placements Please select at max 3 tags + Please sign in again! \ No newline at end of file From a3f8688f2d44771efd58d356424a1c09fec23cc7 Mon Sep 17 00:00:00 2001 From: Prattham Arora <55047418+PratthamArora@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:31:29 +0530 Subject: [PATCH 08/35] Added icon animations to upvote and downvote icons for Doubts and Answers (#68) * test * added animation to upvote and downvote icons for doubts * added animation to upvote and downvote icons for answers * fix animation for first load and code clean up * fix merge conflict --------- Co-authored-by: pratthamarora --- .../answers/viewholder/AnswerViewHolder.kt | 46 ++++++---- .../view/viewholder/DoubtPreviewViewHolder.kt | 50 ++++++----- .../doubtless/doubtless/utils/Extensions.kt | 12 +++ .../main/res/animator/scale_votes_icon.xml | 86 +++++++++++++++++++ app/src/main/res/drawable/upvote_icon.xml | 5 ++ app/src/main/res/layout/answer_layout.xml | 37 ++++---- app/src/main/res/layout/doubt_layout.xml | 36 ++++---- 7 files changed, 200 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt create mode 100644 app/src/main/res/animator/scale_votes_icon.xml create mode 100644 app/src/main/res/drawable/upvote_icon.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt index d6c4ff5..3b1ce9d 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt @@ -1,6 +1,7 @@ package com.doubtless.doubtless.screens.answers.viewholder import android.view.View +import android.widget.CheckBox import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible @@ -11,11 +12,11 @@ import com.doubtless.doubtless.R import com.doubtless.doubtless.screens.answers.AnswerData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.utils.Utils +import com.doubtless.doubtless.utils.addStateListAnimation import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.util.Date -import kotlin.math.ceil +import java.util.* import kotlin.math.floor @@ -32,8 +33,8 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact private val ivDp: ImageView private val tvYear: TextView private val tvVotes: TextView - private val ivUpVote: ImageView - private val ivDownVote: ImageView + private val upVote: CheckBox + private val downVote: CheckBox init { authorName = itemView.findViewById(R.id.tv_author_name) @@ -42,8 +43,8 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact ivDp = itemView.findViewById(R.id.iv_dp_author) tvYear = itemView.findViewById(R.id.user_year) tvVotes = itemView.findViewById(R.id.tv_votes) - ivUpVote = itemView.findViewById(R.id.iv_votes) - ivDownVote = itemView.findViewById(R.id.iv_downvote) + upVote = itemView.findViewById(R.id.cb_upvote) + downVote = itemView.findViewById(R.id.cb_downvote) } fun setData(answerData: AnswerData) { @@ -66,8 +67,10 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact val votingUseCase = DoubtlessApp.getInstance().getAppCompRoot().getAnswerVotingDoubtCase(answerData.copy()) setVotesUi(answerData, votingUseCase) - ivUpVote.setOnClickListener { + upVote.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { + if (it.stateListAnimator == null) + it.addStateListAnimation(R.animator.scale_votes_icon) val result = votingUseCase.upvoteDoubt() @@ -81,8 +84,10 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact } } - ivDownVote.setOnClickListener { + downVote.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { + if (it.stateListAnimator == null) + it.addStateListAnimation(R.animator.scale_votes_icon) val result = votingUseCase.downVoteDoubt() @@ -102,19 +107,22 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact } private fun setVotesUi(answerData: AnswerData, votingUseCase: VotingUseCase) { - ivDownVote.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_off_alt_24)) - ivUpVote.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_off_alt_24)) tvVotes.text = floor(answerData.netVotes).toInt().toString() - CoroutineScope(Dispatchers.Main).launch { - val currentState = votingUseCase.getUserCurrentState() - - if (currentState == VotingUseCase.UPVOTED) - ivUpVote.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_filled)) - - if (currentState == VotingUseCase.DOWNVOTED) - ivDownVote.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_filled)) + when (votingUseCase.getUserCurrentState()) { + VotingUseCase.UPVOTED -> { + downVote.isClickable = false + upVote.isChecked = true + } + VotingUseCase.DOWNVOTED -> { + upVote.isClickable = false + downVote.isChecked = true + } + else -> { + downVote.isClickable = true + upVote.isClickable = true + } + } } } - } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index a73acb6..682ffa5 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -2,6 +2,7 @@ package com.doubtless.doubtless.screens.doubt.view.viewholder import android.text.util.Linkify import android.view.View +import android.widget.CheckBox import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible @@ -13,6 +14,7 @@ import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.utils.Utils import com.doubtless.doubtless.utils.Utils.flatten +import com.doubtless.doubtless.utils.addStateListAnimation import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -36,8 +38,8 @@ class DoubtPreviewViewHolder( private val description: TextView private val ivDp: ImageView private val tvNetVotes: TextView - private val ivUpvotes: ImageView - private val ivDownvotes: ImageView + private val upvotes: CheckBox + private val downvotes: CheckBox private val tvAnswers: TextView private val tvTags: TextView private val tvCollege: TextView @@ -56,11 +58,11 @@ class DoubtPreviewViewHolder( tvAnswers = view.findViewById(R.id.tv_answers) tvTags = view.findViewById(R.id.tv_tags) tvCollege = view.findViewById(R.id.user_college) - ivUpvotes = view.findViewById(R.id.iv_upvotes) - ivDownvotes = view.findViewById(R.id.iv_downvote) + upvotes = view.findViewById(R.id.cb_upvotes) + downvotes = view.findViewById(R.id.cb_downvote) - ivUpvotes.isVisible = showVotingLayout - ivDownvotes.isVisible = showVotingLayout + upvotes.isVisible = showVotingLayout + downvotes.isVisible = showVotingLayout } fun setData(doubtData: DoubtData) { @@ -110,8 +112,11 @@ class DoubtPreviewViewHolder( setVotesUi(doubtData, votingUseCase) - ivUpvotes.setOnClickListener { + + upvotes.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { + if (it.stateListAnimator == null) + it.addStateListAnimation(R.animator.scale_votes_icon) val result = votingUseCase.upvoteDoubt() @@ -126,8 +131,10 @@ class DoubtPreviewViewHolder( } } - ivDownvotes.setOnClickListener { + downvotes.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { + if (it.stateListAnimator == null) + it.addStateListAnimation(R.animator.scale_votes_icon) val result = votingUseCase.downVoteDoubt() @@ -144,23 +151,22 @@ class DoubtPreviewViewHolder( } private fun setVotesUi(doubtData: DoubtData, votingUseCase: VotingUseCase) { - ivDownvotes.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_off_alt_24)) - - ivUpvotes.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_off_alt_24)) - // ivUpvotes.setColorFilter(ContextCompat.getColor(itemView.context, R.color.grey), android.graphics.PorterDuff.Mode.SRC_IN); - tvNetVotes.text = floor(doubtData.netVotes).toInt().toString() - CoroutineScope(Dispatchers.Main).launch { - val currentState = votingUseCase.getUserCurrentState() - - if (currentState == VotingUseCase.UPVOTED) { - ivUpvotes.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_filled)) - // ivUpvotes.setColorFilter(ContextCompat.getColor(itemView.context, R.color.purple), android.graphics.PorterDuff.Mode.SRC_IN); + when (votingUseCase.getUserCurrentState()) { + VotingUseCase.UPVOTED -> { + downvotes.isClickable = false + upvotes.isChecked = true + } + VotingUseCase.DOWNVOTED -> { + upvotes.isClickable = false + downvotes.isChecked = true + } + else -> { + downvotes.isClickable = true + upvotes.isClickable = true + } } - - if (currentState == VotingUseCase.DOWNVOTED) - ivDownvotes.setImageDrawable(itemView.context.getDrawable(R.drawable.ic_baseline_thumb_up_filled)) } } } diff --git a/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt b/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt new file mode 100644 index 0000000..724d406 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt @@ -0,0 +1,12 @@ +package com.doubtless.doubtless.utils + +import android.animation.AnimatorInflater +import android.view.View +import androidx.annotation.AnimatorRes + +fun View.addStateListAnimation(@AnimatorRes animation: Int) { + this.stateListAnimator = AnimatorInflater.loadStateListAnimator( + this.context, + animation + ) +} \ No newline at end of file diff --git a/app/src/main/res/animator/scale_votes_icon.xml b/app/src/main/res/animator/scale_votes_icon.xml new file mode 100644 index 0000000..1e2e200 --- /dev/null +++ b/app/src/main/res/animator/scale_votes_icon.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/upvote_icon.xml b/app/src/main/res/drawable/upvote_icon.xml new file mode 100644 index 0000000..8f17949 --- /dev/null +++ b/app/src/main/res/drawable/upvote_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/answer_layout.xml b/app/src/main/res/layout/answer_layout.xml index e7aaa65..fa0a675 100644 --- a/app/src/main/res/layout/answer_layout.xml +++ b/app/src/main/res/layout/answer_layout.xml @@ -100,15 +100,17 @@ app:layout_constraintStart_toStartOf="@+id/tv_author_name" app:layout_constraintTop_toBottomOf="@+id/iv_dp_author" /> - + app:layout_constraintTop_toBottomOf="@id/author_answer_description_2" /> + app:layout_constraintBottom_toBottomOf="@id/cb_upvote" + app:layout_constraintStart_toEndOf="@id/cb_upvote" + app:layout_constraintTop_toTopOf="@id/cb_upvote" /> - diff --git a/app/src/main/res/layout/doubt_layout.xml b/app/src/main/res/layout/doubt_layout.xml index e1455dc..e925134 100644 --- a/app/src/main/res/layout/doubt_layout.xml +++ b/app/src/main/res/layout/doubt_layout.xml @@ -136,7 +136,8 @@ android:textColor="@color/grey" android:textSize="12dp" app:layout_constraintStart_toStartOf="@id/iv_votes" - app:layout_constraintTop_toBottomOf="@id/iv_votes" /> + app:layout_constraintTop_toBottomOf="@id/iv_votes" + tools:text="Related to : attendance, marks, teachers" /> - + /> - + android:backgroundTint="@color/grey" + android:button="@null" + android:checked="false" + app:layout_constraintStart_toEndOf="@id/cb_upvotes" + app:layout_constraintTop_toTopOf="@id/cb_upvotes" /> From 7bc68f25392ff0b573eb7d05694a26e409a37baa Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Tue, 27 Jun 2023 21:10:29 +0530 Subject: [PATCH 09/35] added create poll button in ViewDoubtsFragment --- .../screens/common/GenericFeedAdapter.kt | 20 +------------------ .../screens/doubt/view/ViewDoubtsViewModel.kt | 8 +++++--- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index be60b18..f49360d 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -12,7 +12,6 @@ import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder -import com.doubtless.doubtless.screens.poll.ViewPollViewHolder class GenericFeedAdapter( private val genericFeedEntities: MutableList, @@ -27,7 +26,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - fun onPollOptionClicked(position: Int, option: String) + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -96,19 +95,6 @@ class GenericFeedAdapter( } ) } - - FeedEntity.TYPE_POLL -> { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.view_poll, parent, false) - return ViewPollViewHolder( - view = view, - interactionListener = object : ViewPollViewHolder.InteractionListener { - override fun onPollOptionClicked(position: Int, option: String) { - interactionListener.onPollOptionClicked(position, option) - } - } - ) - } } throw Exception("type is not defined") @@ -125,10 +111,6 @@ class GenericFeedAdapter( if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_SEARCH_RESULT) holder.setData(genericFeedEntities[position].search_doubt!!.toDoubtData()) - if (holder is ViewPollViewHolder && getItemViewType(position) == FeedEntity.TYPE_POLL) - holder.setData(genericFeedEntities[position]) - - if (position == itemCount - 1) { onLastItemReached.invoke() } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 00ebb07..1da873b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -76,9 +76,11 @@ class ViewDoubtsViewModel constructor( } } - // for page 1 call add search entity - if (_homeEntities.isEmpty()) - entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + // for page 1 call add search and options button entity + if (_homeEntities.isEmpty()) + entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + if (_homeEntities.isEmpty()) + entitiesFromServer.add(1, FeedEntity.getOptionButtons()) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) From 2363a424374343639a6478cc25573b7a56d65bc6 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Wed, 28 Jun 2023 20:59:36 +0530 Subject: [PATCH 10/35] view poll vieholder added , changes in ui --- .../doubtless/screens/doubt/view/ViewDoubtsViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 1da873b..01a5451 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -81,6 +81,8 @@ class ViewDoubtsViewModel constructor( entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) + if(_homeEntities.isEmpty()) + entitiesFromServer.add(6, FeedEntity.getPollView() ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) From 93c8459f21ebb5e4003fced9af15389263a9eb1d Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Thu, 29 Jun 2023 10:44:08 +0530 Subject: [PATCH 11/35] added poll view in main feed with multiple options, poll func WIP --- .../screens/common/GenericFeedAdapter.kt | 20 ++++++++++++++++++- .../screens/doubt/view/ViewDoubtsViewModel.kt | 9 ++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index f49360d..415b7aa 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -12,6 +12,7 @@ import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder +import com.doubtless.doubtless.screens.poll.ViewPollViewHolder class GenericFeedAdapter( private val genericFeedEntities: MutableList, @@ -26,7 +27,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - + fun onPollOptionClicked(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -95,6 +96,19 @@ class GenericFeedAdapter( } ) } + + FeedEntity.TYPE_POLL -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.view_poll, parent, false) + return ViewPollViewHolder( + view = view, + interactionListener = object : ViewPollViewHolder.InteractionListener { + override fun onPollOptionClicked(position: Int) { + interactionListener.onPollOptionClicked(position) + } + } + ) + } } throw Exception("type is not defined") @@ -111,6 +125,10 @@ class GenericFeedAdapter( if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_SEARCH_RESULT) holder.setData(genericFeedEntities[position].search_doubt!!.toDoubtData()) + if (holder is ViewPollViewHolder && getItemViewType(position) == FeedEntity.TYPE_POLL) + holder.setData(genericFeedEntities[position]) + + if (position == itemCount - 1) { onLastItemReached.invoke() } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 01a5451..58779ad 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -76,13 +76,20 @@ class ViewDoubtsViewModel constructor( } } + // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. + if (_homeEntitiesIds.contains(doubtData.id) == false) { + entitiesFromServer.add(doubtData.toHomeEntity()) + _homeEntitiesIds[doubtData.id!!] = 1 + } + } + val pollOptions = listOf("Option 1", "Option 2", "Option 3") // for page 1 call add search and options button entity if (_homeEntities.isEmpty()) entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollView() ) + entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) From fb31868e66cb7496c2309803d184b55408ea2850 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Fri, 30 Jun 2023 13:27:06 +0530 Subject: [PATCH 12/35] changes --- .../doubtless/screens/common/GenericFeedAdapter.kt | 7 +++---- .../doubtless/doubtless/screens/search/SearchFragment.kt | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 415b7aa..072069f 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -8,7 +8,6 @@ import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R import com.doubtless.doubtless.screens.dashboard.viewholder.UserProfileViewHolder import com.doubtless.doubtless.screens.doubt.DoubtData -import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder @@ -27,7 +26,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - fun onPollOptionClicked(position: Int) + fun onPollOptionClicked(position: Int, option: String) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -103,8 +102,8 @@ class GenericFeedAdapter( return ViewPollViewHolder( view = view, interactionListener = object : ViewPollViewHolder.InteractionListener { - override fun onPollOptionClicked(position: Int) { - interactionListener.onPollOptionClicked(position) + override fun onPollOptionClicked(position: Int, option: String) { + interactionListener.onPollOptionClicked(position, option) } } ) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 3793157..139a5bd 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -71,7 +71,7 @@ class SearchFragment : Fragment() { override fun onDeleteAccountClicked() {} override fun onCreatePollClicked() {} - override fun onPollOptionClicked(position: Int, option :String) {} + override fun onPollOptionClicked(position: Int, option: String) {} }) } From 3935a352e459633357199e00eb26d91c663803f8 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Fri, 30 Jun 2023 22:01:38 +0530 Subject: [PATCH 13/35] changes --- .../screens/doubt/view/ViewDoubtsViewModel.kt | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 58779ad..1b8c867 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -76,20 +76,14 @@ class ViewDoubtsViewModel constructor( } } - // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. - if (_homeEntitiesIds.contains(doubtData.id) == false) { - entitiesFromServer.add(doubtData.toHomeEntity()) - _homeEntitiesIds[doubtData.id!!] = 1 - } - } - val pollOptions = listOf("Option 1", "Option 2", "Option 3") - // for page 1 call add search and options button entity - if (_homeEntities.isEmpty()) - entitiesFromServer.add(0, FeedEntity.getSearchEntity()) - if (_homeEntities.isEmpty()) - entitiesFromServer.add(1, FeedEntity.getOptionButtons()) - if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) + val pollOptions = listOf("Option 1", "Option 2", "Option 3") + // for page 1 call add search and options button entity + if (_homeEntities.isEmpty()) + entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + if (_homeEntities.isEmpty()) + entitiesFromServer.add(1, FeedEntity.getOptionButtons()) + if (_homeEntities.isEmpty()) + entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions)) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) @@ -110,6 +104,8 @@ class ViewDoubtsViewModel constructor( ) isLoading = false } + + } fun refreshList() { From 9802125890fc4321a4020536e38fb8cab63a1b2b Mon Sep 17 00:00:00 2001 From: Prattham Arora <55047418+PratthamArora@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:29:29 +0530 Subject: [PATCH 14/35] fix fatal NPE for ViewDoubtViewModel (#73) * fix fatal NPE * logout user if user is null * code clean up * add non fatal log for null user --- .../doubtless/screens/doubt/view/ViewDoubtsViewModel.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 1b8c867..00ebb07 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -76,14 +76,9 @@ class ViewDoubtsViewModel constructor( } } - val pollOptions = listOf("Option 1", "Option 2", "Option 3") - // for page 1 call add search and options button entity + // for page 1 call add search entity if (_homeEntities.isEmpty()) entitiesFromServer.add(0, FeedEntity.getSearchEntity()) - if (_homeEntities.isEmpty()) - entitiesFromServer.add(1, FeedEntity.getOptionButtons()) - if (_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions)) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) @@ -104,8 +99,6 @@ class ViewDoubtsViewModel constructor( ) isLoading = false } - - } fun refreshList() { From 2d8d6b087754bc4eb616299b3c15694d24451cdb Mon Sep 17 00:00:00 2001 From: Prattham Arora <55047418+PratthamArora@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:07:43 +0530 Subject: [PATCH 15/35] automatically close keyboard after search query is submitted (#70) * test * added animation to upvote and downvote icons for doubts * added animation to upvote and downvote icons for answers * fix animation for first load and code clean up * automatically close keyboard after search query is submitted * fix merge conflict --------- Co-authored-by: pratthamarora --- .../doubtless/doubtless/screens/search/SearchFragment.kt | 3 +++ .../main/java/com/doubtless/doubtless/utils/Extensions.kt | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 139a5bd..8ac0b9c 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -18,6 +18,7 @@ import com.doubtless.doubtless.screens.common.GenericFeedAdapter import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.main.MainActivity import com.doubtless.doubtless.screens.search.usecases.FetchSearchResultsUseCase +import com.doubtless.doubtless.utils.hideSoftKeyboard import kotlinx.coroutines.* class SearchFragment : Fragment() { @@ -115,6 +116,8 @@ class SearchFragment : Fragment() { it.toGenericEntity() }) binding.progressSearch.visibility = View.GONE + requireView().hideSoftKeyboard() + binding.etSearch.clearFocus() } } diff --git a/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt b/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt index 724d406..1c9064a 100644 --- a/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt +++ b/app/src/main/java/com/doubtless/doubtless/utils/Extensions.kt @@ -1,7 +1,9 @@ package com.doubtless.doubtless.utils import android.animation.AnimatorInflater +import android.content.Context import android.view.View +import android.view.inputmethod.InputMethodManager import androidx.annotation.AnimatorRes fun View.addStateListAnimation(@AnimatorRes animation: Int) { @@ -9,4 +11,9 @@ fun View.addStateListAnimation(@AnimatorRes animation: Int) { this.context, animation ) +} + +fun View.hideSoftKeyboard() { + val imm = this.context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm?.hideSoftInputFromWindow(this.windowToken, 0) } \ No newline at end of file From 28c9cfb37943151765746b22f7ff1dd80a2e9cfc Mon Sep 17 00:00:00 2001 From: sidsharma2002 Date: Sun, 25 Jun 2023 10:52:52 +0530 Subject: [PATCH 16/35] bottom nav icons changes --- .../screens/main/bottomNav/RetroBottomNav.kt | 3 +- .../bottomNav/SoberBottomNavElementLayout.kt | 19 +++ .../res/drawable/create_checkable_icon.xml | 5 + .../res/drawable/dashboard_checkable_icon.xml | 5 + .../main/res/drawable/home_checkable_icon.xml | 5 + .../main/res/drawable/ic_outline_add_24.xml | 5 + .../res/drawable/ic_outline_dashboard_24.xml | 5 + .../main/res/drawable/ic_outline_home_24.xml | 5 + app/src/main/res/layout/doubt_layout.xml | 2 +- app/src/main/res/layout/fragment_main.xml | 151 +++++------------- app/src/main/res/values/colors.xml | 1 + 11 files changed, 91 insertions(+), 115 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt create mode 100644 app/src/main/res/drawable/create_checkable_icon.xml create mode 100644 app/src/main/res/drawable/dashboard_checkable_icon.xml create mode 100644 app/src/main/res/drawable/home_checkable_icon.xml create mode 100644 app/src/main/res/drawable/ic_outline_add_24.xml create mode 100644 app/src/main/res/drawable/ic_outline_dashboard_24.xml create mode 100644 app/src/main/res/drawable/ic_outline_home_24.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt index 81404df..d968f7b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt @@ -8,13 +8,14 @@ import android.os.VibratorManager import android.util.AttributeSet import android.view.View import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children import com.doubtless.doubtless.theming.buttons.SecondaryButton class RetroBottomNav( context: Context, attributeSet: AttributeSet -) : LinearLayout(context, attributeSet) { +) : ConstraintLayout(context, attributeSet) { private var currentSelectedIndex: Int? = null private val elements: ArrayList = arrayListOf() diff --git a/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt b/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt new file mode 100644 index 0000000..8aff1ac --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt @@ -0,0 +1,19 @@ +package com.doubtless.doubtless.theming.bottomNav + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatCheckBox +import com.doubtless.doubtless.screens.main.bottomNav.BottomIntractableElement + +class SoberBottomNavElementLayout(context: Context, attributeSet: AttributeSet?) + : AppCompatCheckBox(context, attributeSet), BottomIntractableElement { + + override fun onSelected() { + isChecked = true + } + + override fun onUnselected() { + isChecked = false + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/create_checkable_icon.xml b/app/src/main/res/drawable/create_checkable_icon.xml new file mode 100644 index 0000000..5d42350 --- /dev/null +++ b/app/src/main/res/drawable/create_checkable_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/dashboard_checkable_icon.xml b/app/src/main/res/drawable/dashboard_checkable_icon.xml new file mode 100644 index 0000000..5742478 --- /dev/null +++ b/app/src/main/res/drawable/dashboard_checkable_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_checkable_icon.xml b/app/src/main/res/drawable/home_checkable_icon.xml new file mode 100644 index 0000000..bebb2a2 --- /dev/null +++ b/app/src/main/res/drawable/home_checkable_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_outline_add_24.xml b/app/src/main/res/drawable/ic_outline_add_24.xml new file mode 100644 index 0000000..b063a36 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_add_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_outline_dashboard_24.xml b/app/src/main/res/drawable/ic_outline_dashboard_24.xml new file mode 100644 index 0000000..5fbe0ef --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_dashboard_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_outline_home_24.xml b/app/src/main/res/drawable/ic_outline_home_24.xml new file mode 100644 index 0000000..678f2e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_home_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/doubt_layout.xml b/app/src/main/res/layout/doubt_layout.xml index e925134..6261b58 100644 --- a/app/src/main/res/layout/doubt_layout.xml +++ b/app/src/main/res/layout/doubt_layout.xml @@ -16,7 +16,7 @@ + android:background="@color/separation_grey" /> diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 75c382b..29f6329 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -14,127 +14,52 @@ app:layout_constraintBottom_toTopOf="@id/retro_bottom_nav" app:layout_constraintTop_toTopOf="parent" /> + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d23b332..60f4a5e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,4 +9,5 @@ #e090c9 #fab679 #A93AFF + #99B8B8B8 \ No newline at end of file From aeca466ffbf396ad5d5e189464ee2faffec5d444 Mon Sep 17 00:00:00 2001 From: sidsharma2002 Date: Sun, 25 Jun 2023 12:20:54 +0530 Subject: [PATCH 17/35] removed voting usecase from onBind --- .../doubtless/screens/answers/AnswerData.kt | 7 ++- .../screens/answers/AnswerDoubtEntity.kt | 5 +- .../screens/answers/AnswerDoubtsAdapter.kt | 3 +- .../screens/answers/AnswersFragment.kt | 54 ++++++++++++------ .../screens/answers/AnswersViewModel.kt | 57 +++++++++++++++---- .../answers/viewholder/AnswerViewHolder.kt | 15 +++-- 6 files changed, 101 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt index 5042fdc..9dd19e1 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt @@ -92,7 +92,12 @@ data class AnswerData( } fun toAnswerDoubtEntity(answerData: AnswerData): AnswerDoubtEntity { - return AnswerDoubtEntity(AnswerDoubtEntity.TYPE_ANSWER, null, answerData) + return AnswerDoubtEntity( + type = AnswerDoubtEntity.TYPE_ANSWER, + doubt = null, + answer = answerData, + answerVotingUseCase = DoubtlessApp.getInstance().getAppCompRoot().getAnswerVotingDoubtCase(answerData) + ) } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtEntity.kt index 15a4c3b..5457bba 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtEntity.kt @@ -1,11 +1,14 @@ package com.doubtless.doubtless.screens.answers import com.doubtless.doubtless.screens.doubt.DoubtData +import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase data class AnswerDoubtEntity( val type: Int, val doubt: DoubtData? = null, - val answer: AnswerData? = null + val answer: AnswerData? = null, + val answerVotingUseCase: VotingUseCase? = null, + val doubtVotingUseCase: VotingUseCase? = null ) { companion object { const val TYPE_DOUBT = 1 diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtsAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtsAdapter.kt index 89e364b..f16bfaa 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtsAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerDoubtsAdapter.kt @@ -75,7 +75,7 @@ class AnswerDoubtsAdapter( holder.setData(doubtAnswerEntities[position].doubt!!) if (holder is AnswerViewHolder) - holder.setData(doubtAnswerEntities[position].answer!!) + holder.setData(doubtAnswerEntities[position].answer!!, doubtAnswerEntities[position].answerVotingUseCase!!) if (holder is EnterAnswerViewHolder) holder.setData(user) @@ -98,6 +98,7 @@ class AnswerDoubtsAdapter( index = 2, element = answer ) // first 2 are doubt and enter answer view + notifyItemInserted(1) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt index 3636b44..9fd7db0 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt @@ -122,27 +122,47 @@ class AnswersFragment : Fragment() { } viewModel.answerDoubtEntities.observe(viewLifecycleOwner) { - if (it == null) return@observe - adapter.appendAnswer(it) - viewModel.notifyAnswersConsumed() + when (it) { + is AnswersViewModel.Result.Loading -> { + binding.progressPostAnswer.visibility = View.VISIBLE + } + + is AnswersViewModel.Result.Success -> { + adapter.appendAnswer(it.data) + viewModel.notifyAnswersConsumed() + binding.progressPostAnswer.visibility = View.GONE + } + + is AnswersViewModel.Result.Error -> { + Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show() + binding.progressPostAnswer.visibility = View.GONE + } + } } viewModel.publishAnswerStatus.observe(viewLifecycleOwner) { - if (it is PublishAnswerUseCase.Result.Success) { - binding.progressPostAnswer.visibility = View.GONE - Toast.makeText(requireContext(), "Successfully posted!", Toast.LENGTH_SHORT).show() - adapter.appendAnswerAtFirst(answerData = it.answerData) - // this will increase the count across screens as the same reference was passed to the arguments. - // Its generally not a good thing to do.t(it.answerData) - doubtData.no_answers += 1 - } + when (it) { + is PublishAnswerUseCase.Result.Success -> { - if (it is PublishAnswerUseCase.Result.Error){ - binding.progressPostAnswer.visibility = View.GONE - Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show() - } - if (it is PublishAnswerUseCase.Result.Loading){ - binding.progressPostAnswer.visibility = View.VISIBLE + binding.progressPostAnswer.visibility = View.GONE + + Toast.makeText(requireContext(), "Successfully posted!", Toast.LENGTH_SHORT).show() + + adapter.appendAnswerAtFirst(answerData = it.answerData) + + // this will increase the count across screens as the same reference was passed to the arguments. + // Its generally not a good thing to do. + doubtData.no_answers += 1 + } + + is PublishAnswerUseCase.Result.Error -> { + binding.progressPostAnswer.visibility = View.GONE + Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show() + } + + is PublishAnswerUseCase.Result.Loading -> { + binding.progressPostAnswer.visibility = View.VISIBLE + } } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersViewModel.kt index 041f96c..1bb1464 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersViewModel.kt @@ -1,6 +1,7 @@ package com.doubtless.doubtless.screens.answers import androidx.lifecycle.* +import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.screens.answers.usecases.FetchAnswerUseCase import com.doubtless.doubtless.screens.answers.usecases.PublishAnswerUseCase import com.doubtless.doubtless.screens.auth.usecases.UserManager @@ -15,28 +16,55 @@ class AnswersViewModel( private val doubtData: DoubtData ) : ViewModel() { - private val _answerDoubtEntities = MutableLiveData( - listOf( - AnswerDoubtEntity(AnswerDoubtEntity.TYPE_DOUBT, doubtData, null), - AnswerDoubtEntity(AnswerDoubtEntity.TYPE_ANSWER_ENTER, null, null) + sealed class Result { + class Success(val data: List) : Result() + object Loading : Result() + class Error(val message: String) : Result() + } + + private val _answerDoubtEntities = MutableLiveData( + Result.Success( + listOf( + AnswerDoubtEntity( + type = AnswerDoubtEntity.TYPE_DOUBT, + doubt = doubtData, + answer = null, + answerVotingUseCase = null, + doubtVotingUseCase = DoubtlessApp.getInstance().getAppCompRoot() + .getDoubtVotingDoubtCase(doubtData) + ), + AnswerDoubtEntity(AnswerDoubtEntity.TYPE_ANSWER_ENTER, null, null) + ) ) ) - val answerDoubtEntities: LiveData?> = _answerDoubtEntities + val answerDoubtEntities: LiveData = _answerDoubtEntities private val _publishAnswerStatus = MutableLiveData() val publishAnswerStatus: LiveData = _publishAnswerStatus fun fetchAnswers() = viewModelScope.launch(Dispatchers.IO) { + + _answerDoubtEntities.postValue(Result.Loading) + val result = fetchAnswerUseCase.fetchAnswers() - if (result is FetchAnswerUseCase.Result.Success) { - _answerDoubtEntities.postValue(result.data.map { - AnswerData.toAnswerDoubtEntity(it) - }) - } else { - /* no-op */ + when (result) { + is FetchAnswerUseCase.Result.Success -> { + _answerDoubtEntities.postValue( + Result.Success(result.data.map { + AnswerData.toAnswerDoubtEntity(it) + }) + ) + } + else -> { + _answerDoubtEntities.postValue( + Result.Error( + (result as FetchAnswerUseCase.Result.Error).message + ) + ) + } } } @@ -61,7 +89,12 @@ class AnswersViewModel( ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return AnswersViewModel(fetchAnswerUseCase, publishAnswerUseCase, userManager, doubtData) as T + return AnswersViewModel( + fetchAnswerUseCase, + publishAnswerUseCase, + userManager, + doubtData + ) as T } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt index 3b1ce9d..fbfb409 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt @@ -7,7 +7,6 @@ import android.widget.TextView import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide -import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R import com.doubtless.doubtless.screens.answers.AnswerData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase @@ -17,6 +16,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.util.* +import java.util.Date import kotlin.math.floor @@ -47,7 +47,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact downVote = itemView.findViewById(R.id.cb_downvote) } - fun setData(answerData: AnswerData) { + fun setData(answerData: AnswerData, answerVotingUseCase: VotingUseCase) { itemView.setOnClickListener { interactionListener.onAnswerClicked(answerData, adapterPosition) @@ -64,15 +64,14 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact description.text = answerData.description tvYear.text = "| ${answerData.authorYear} Year |" - val votingUseCase = DoubtlessApp.getInstance().getAppCompRoot().getAnswerVotingDoubtCase(answerData.copy()) - setVotesUi(answerData, votingUseCase) + setVotesUi(answerData, answerVotingUseCase) upVote.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { if (it.stateListAnimator == null) it.addStateListAnimation(R.animator.scale_votes_icon) - val result = votingUseCase.upvoteDoubt() + val result = answerVotingUseCase.upvoteDoubt() if (result is VotingUseCase.Result.UpVoted) { answerData.netVotes += 1 @@ -80,7 +79,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact answerData.netVotes -= 1 } - setVotesUi(answerData, votingUseCase) + setVotesUi(answerData, answerVotingUseCase) } } @@ -89,7 +88,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact if (it.stateListAnimator == null) it.addStateListAnimation(R.animator.scale_votes_icon) - val result = votingUseCase.downVoteDoubt() + val result = answerVotingUseCase.downVoteDoubt() if (result is VotingUseCase.Result.DownVoted) { answerData.netVotes -= 1 @@ -97,7 +96,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact answerData.netVotes += 1 } - setVotesUi(answerData, votingUseCase) + setVotesUi(answerData, answerVotingUseCase) } } From 26a054ffa095726900180c014f431d6421e984ab Mon Sep 17 00:00:00 2001 From: sidsharma2002 Date: Sun, 25 Jun 2023 14:29:40 +0530 Subject: [PATCH 18/35] on pressing back on bottom nav redirects to home frag --- .../doubtless/di/AppCompositionRoot.kt | 13 +++++ .../dashboard/DashboardContainerFragment.kt | 11 +++- .../doubt/create/CreateDoubtFragment.kt | 52 +++++++++++++++---- .../doubtless/screens/main/MainFragment.kt | 6 ++- .../{RetroBottomNav.kt => BottomNavLayout.kt} | 8 +-- app/src/main/res/layout/fragment_main.xml | 4 +- 6 files changed, 72 insertions(+), 22 deletions(-) rename app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/{RetroBottomNav.kt => BottomNavLayout.kt} (94%) diff --git a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt index f8f7d63..8a0f5ac 100644 --- a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt +++ b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt @@ -200,6 +200,19 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return null } + fun getCreateFragmentNavigator(mainActivity: MainActivity): FragNavigator? { + val createFrag = + (mainActivity.supportFragmentManager.findFragmentByTag("MainFragment") as MainFragment?) + ?.childFragmentManager?.findFragmentByTag("mainfrag_1") + + if (createFrag != null) { + return DoubtlessApp.getInstance().getAppCompRoot() + .getFragNavigator(createFrag.childFragmentManager, R.id.bottomNav_child_container) + } + + return null + } + fun getDashboardFragNavigator(mainActivity: MainActivity): FragNavigator? { val dashboardFrag = diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardContainerFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardContainerFragment.kt index d84f9ff..eaf698b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardContainerFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardContainerFragment.kt @@ -8,6 +8,7 @@ import com.doubtless.doubtless.R import com.doubtless.doubtless.navigation.FragNavigator import com.doubtless.doubtless.navigation.OnBackPressListener import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.main.MainFragment class DashboardContainerFragment : Fragment(R.layout.fragment_home) { @@ -28,7 +29,15 @@ class DashboardContainerFragment : Fragment(R.layout.fragment_home) { private val onBackPressListener = object : OnBackPressListener { override fun onBackPress(): Boolean { - return navigator.onBackPress() + + val backPressConsumed = navigator.onBackPress() + + return if (backPressConsumed) + true + else { + (parentFragment as? MainFragment)?.selectHomeBottomNavElement() + true + } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt index cd6c85c..8f2ba7d 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt @@ -16,11 +16,15 @@ import androidx.lifecycle.ViewModelProvider import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.analytics.AnalyticsTracker import com.doubtless.doubtless.databinding.FragmentCreateDoubtBinding +import com.doubtless.doubtless.navigation.FragNavigator +import com.doubtless.doubtless.navigation.OnBackPressListener import com.doubtless.doubtless.screens.auth.User import com.doubtless.doubtless.screens.auth.usecases.UserManager import com.doubtless.doubtless.screens.doubt.PublishDoubtRequest import com.doubtless.doubtless.screens.doubt.usecases.DoubtDataSharedPrefUseCase import com.doubtless.doubtless.screens.doubt.usecases.PostDoubtUseCase +import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.main.MainFragment import com.doubtless.doubtless.screens.onboarding.OnBoardingAttributes import com.doubtless.doubtless.screens.onboarding.usecases.FetchOnBoardingDataUseCase import com.google.android.material.chip.Chip @@ -34,6 +38,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlin.properties.Delegates +/** + * NOTE : right now this fragment only opens up from bottom nav so the navigation functionality is coded assuming that. + * change things in future if this assumption changes. + */ class CreateDoubtFragment : Fragment() { private var _binding: FragmentCreateDoubtBinding? = null @@ -44,6 +52,7 @@ class CreateDoubtFragment : Fragment() { private lateinit var analyticsTracker: AnalyticsTracker private lateinit var postDoubtUseCase: PostDoubtUseCase private lateinit var remoteConfig: FirebaseRemoteConfig + private var navigator: FragNavigator? = null private var maxHeadingCharLimit by Delegates.notNull() private var maxDescriptionCharLimit by Delegates.notNull() @@ -56,17 +65,36 @@ class CreateDoubtFragment : Fragment() { private var onBoardingAttributes: OnBoardingAttributes? = null + private val onBackPressListener = object : OnBackPressListener { + override fun onBackPress(): Boolean { + + val backPressConsumed = navigator?.onBackPress() ?: false + + return if (backPressConsumed) + true + else { + (parentFragment as? MainFragment)?.selectHomeBottomNavElement() + true + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + db = Firebase.firestore - userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() - analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() - doubtDataSharedPrefUseCase = - DoubtlessApp.getInstance().getAppCompRoot().getDoubtDataSharedPrefUseCase() - onBoardingDataUseCase = DoubtlessApp.getInstance().getAppCompRoot() - .getFetchOnBoardingDataUseCase(userManager.getCachedUserData()!!) - remoteConfig = DoubtlessApp.getInstance().getAppCompRoot().getRemoteConfig() - postDoubtUseCase = DoubtlessApp.getInstance().getAppCompRoot().getPostDoubtUseCase() + + val appComp = DoubtlessApp.getInstance().getAppCompRoot() + + userManager = appComp.getUserManager() + analyticsTracker = appComp.getAnalyticsTracker() + doubtDataSharedPrefUseCase = appComp.getDoubtDataSharedPrefUseCase() + onBoardingDataUseCase = + appComp.getFetchOnBoardingDataUseCase(userManager.getCachedUserData()!!) + remoteConfig = appComp.getRemoteConfig() + postDoubtUseCase = appComp.getPostDoubtUseCase() + navigator = appComp.getCreateFragmentNavigator(requireActivity() as MainActivity) + viewModel = ViewModelProvider( this, CreateDoubtViewModel.Companion.Factory(postDoubtUseCase) @@ -175,14 +203,19 @@ class CreateDoubtFragment : Fragment() { } + override fun onResume() { + super.onResume() + (requireActivity() as MainActivity).registerBackPress(onBackPressListener) + } + override fun onPause() { super.onPause() doubtDataSharedPrefUseCase.saveDoubtData( binding.doubtHeading.text.toString(), binding.doubtDescription.text.toString() ) + (requireActivity() as MainActivity).unregisterBackPress(onBackPressListener) } - private fun checkText() { val errorMessage = isEverythingValid() @@ -199,7 +232,6 @@ class CreateDoubtFragment : Fragment() { binding.postButton.alpha = 0.8f showConfirmationDialog(getSelectedTags(), keywordsEntered) - } private fun isEverythingValid(): String? { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt index 336c7b5..c022c73 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt @@ -76,8 +76,10 @@ class MainFragment : Fragment() { return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + fun selectHomeBottomNavElement() { + if (!binding.btmNavHome.isChecked) { + binding.btmNavHome.callOnClick() + } } override fun onDestroyView() { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt similarity index 94% rename from app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt rename to app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt index d968f7b..027e934 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/RetroBottomNav.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt @@ -1,18 +1,12 @@ package com.doubtless.doubtless.screens.main.bottomNav import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.os.Vibrator -import android.os.VibratorManager import android.util.AttributeSet import android.view.View -import android.widget.LinearLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children -import com.doubtless.doubtless.theming.buttons.SecondaryButton -class RetroBottomNav( +class BottomNavLayout( context: Context, attributeSet: AttributeSet ) : ConstraintLayout(context, attributeSet) { diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 29f6329..6ccaf19 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -21,7 +21,7 @@ android:background="@color/separation_grey" app:layout_constraintBottom_toTopOf="@id/retro_bottom_nav" /> - - + \ No newline at end of file From 37c66c845568ab0e716ee750a677799e9ff9b0bc Mon Sep 17 00:00:00 2001 From: sidsharma2002 Date: Mon, 26 Jun 2023 06:36:24 +0530 Subject: [PATCH 19/35] in app notif implemented --- app/build.gradle | 14 +- .../doubtless/analytics/AnalyticsTracker.kt | 2 + .../constants/FirestoreCollection.kt | 2 +- .../doubtless/di/AppCompositionRoot.kt | 44 +++++- .../doubtless/localDatabase/AppDatabase.kt | 33 ++++ .../doubtless/localDatabase/Converter.kt | 22 +++ .../screens/answers/AnswersFragment.kt | 20 ++- .../screens/dashboard/DashboardFragment.kt | 10 +- .../FetchDoubtDataFromDoubtIdUseCase.kt | 27 ++++ .../InAppNotifContainerFragment.kt | 59 +++++++ .../InAppNotificationAdapter.kt | 50 ++++++ .../InAppNotificationFragment.kt | 148 ++++++++++++++++++ .../InAppNotificationViewModel.kt | 89 +++++++++++ .../PostAnswerNotificationViewHolder.kt | 53 +++++++ .../dao/InAppNotificationDao.kt | 17 ++ .../model/InAppNotificationEntity.kt | 64 ++++++++ .../usecases/FetchInAppNotificationUseCase.kt | 52 ++++++ .../FetchUnreadNotificationUseCase.kt | 48 ++++++ .../MarkInAppNotificationsReadUseCase.kt | 52 ++++++ .../doubtless/screens/main/MainActivity.kt | 4 + .../doubtless/screens/main/MainFragment.kt | 32 +++- .../screens/main/bottomNav/BottomNavData.kt | 1 + .../screens/main/bottomNav/BottomNavLayout.kt | 9 +- .../bottomNav/SoberBottomNavElementLayout.kt | 5 + .../buttons/RetroBottomNavElementLayout.kt | 4 + .../theming/buttons/SecondaryButton.kt | 4 + .../drawable/ic_baseline_notifications_24.xml | 5 + .../drawable/ic_outline_notifications_24.xml | 5 + .../main/res/drawable/ic_reply_arrow_24.xml | 5 + .../res/drawable/notif_checkable_icon.xml | 5 + .../main/res/layout/fragment_inapp_notif.xml | 48 ++++++ app/src/main/res/layout/fragment_main.xml | 16 +- .../res/layout/item_post_answer_notif.xml | 137 ++++++++++++++++ app/src/main/res/values/colors.xml | 1 + 34 files changed, 1071 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/localDatabase/AppDatabase.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/localDatabase/Converter.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchDoubtDataFromDoubtIdUseCase.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotifContainerFragment.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationAdapter.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationFragment.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationViewModel.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/PostAnswerNotificationViewHolder.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/dao/InAppNotificationDao.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/model/InAppNotificationEntity.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchInAppNotificationUseCase.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchUnreadNotificationUseCase.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/MarkInAppNotificationsReadUseCase.kt create mode 100644 app/src/main/res/drawable/ic_baseline_notifications_24.xml create mode 100644 app/src/main/res/drawable/ic_outline_notifications_24.xml create mode 100644 app/src/main/res/drawable/ic_reply_arrow_24.xml create mode 100644 app/src/main/res/drawable/notif_checkable_icon.xml create mode 100644 app/src/main/res/layout/fragment_inapp_notif.xml create mode 100644 app/src/main/res/layout/item_post_answer_notif.xml diff --git a/app/build.gradle b/app/build.gradle index 6d5302a..904fcb9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ plugins { id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' id 'kotlin-parcelize' + id 'kotlin-kapt' } android { @@ -15,7 +16,7 @@ android { minSdk 28 targetSdk 33 versionCode 6 - versionName "0.1.3" + versionName "0.1.3-inapp" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug @@ -72,6 +73,17 @@ dependencies { implementation 'com.amplitude:analytics-android:1.+' // works with .+ and not 1.0 :/ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + def room_version = "2.5.2" + + implementation("androidx.room:room-runtime:$room_version") + annotationProcessor("androidx.room:room-compiler:$room_version") + + // To use Kotlin annotation processing tool (kapt) + kapt("androidx.room:room-compiler:$room_version") + + // optional - Kotlin Extensions and Coroutines support for Room + implementation("androidx.room:room-ktx:$room_version") + // server implementation 'com.squareup.retrofit2:retrofit:2.9.0' diff --git a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt index 619be60..491a798 100644 --- a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt +++ b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt @@ -86,7 +86,9 @@ class AnalyticsTracker constructor( map["app_version_code"] = BuildConfig.VERSION_CODE.toString() val user = userManager.getCachedUserData() ?: return map + map["user_id"] = user.id.toString() + map["user_email"] = user.email.toString() if (user.local_user_attr?.tags != null) map["user_tags"] = user.local_user_attr.tags.toString() diff --git a/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt b/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt index c4ca8ee..0e6f560 100644 --- a/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt +++ b/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt @@ -20,7 +20,7 @@ class FirestoreCollection { val UPVOTE_DATA_USERS = "upvoted_users" val DOWNVOTE_DATA_USERS = "downvoted_users" - + val NOTIFICATION = "notifications" } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt index 8a0f5ac..eed5b06 100644 --- a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt +++ b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt @@ -9,6 +9,7 @@ import com.amplitude.android.Configuration import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker +import com.doubtless.doubtless.localDatabase.AppDatabase import com.doubtless.doubtless.navigation.FragNavigator import com.doubtless.doubtless.navigation.Router import com.doubtless.doubtless.network.DoubtlessServer @@ -24,12 +25,17 @@ import com.doubtless.doubtless.screens.dashboard.usecases.FetchUserDataUseCase import com.doubtless.doubtless.screens.dashboard.usecases.FetchUserFeedByDateUseCase import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.usecases.DoubtDataSharedPrefUseCase +import com.doubtless.doubtless.screens.doubt.usecases.FetchDoubtDataFromDoubtIdUseCase import com.doubtless.doubtless.screens.doubt.usecases.PostDoubtUseCase import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.home.entities.FeedConfig import com.doubtless.doubtless.screens.home.usecases.FetchFeedByDateUseCase import com.doubtless.doubtless.screens.home.usecases.FetchFeedByPopularityUseCase import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase +import com.doubtless.doubtless.screens.inAppNotification.dao.InAppNotificationDao +import com.doubtless.doubtless.screens.inAppNotification.usecases.FetchInAppNotificationUseCase +import com.doubtless.doubtless.screens.inAppNotification.usecases.FetchUnreadNotificationUseCase +import com.doubtless.doubtless.screens.inAppNotification.usecases.MarkInAppNotificationsReadUseCase import com.doubtless.doubtless.screens.main.MainActivity import com.doubtless.doubtless.screens.main.MainFragment import com.doubtless.doubtless.screens.onboarding.usecases.AddOnBoardingDataUseCase @@ -132,6 +138,24 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return PostDoubtUseCase(getServer()) } + // --------- InApp Notification ---------- + + fun getFetchNotificationUseCase(): FetchInAppNotificationUseCase { + return FetchInAppNotificationUseCase(getInAppNotificationDao(), getFetchUnreadNotificationUseCase()) + } + + fun getMarkInAppNotificationsReadUseCase(): MarkInAppNotificationsReadUseCase { + return MarkInAppNotificationsReadUseCase(getInAppNotificationDao(), FirebaseFirestore.getInstance()) + } + + private fun getFetchUnreadNotificationUseCase(): FetchUnreadNotificationUseCase { + return FetchUnreadNotificationUseCase(FirebaseFirestore.getInstance(), getUserManager()) + } + + private fun getInAppNotificationDao(): InAppNotificationDao { + return AppDatabase.getDbInstance().inAppNotificationDao() + } + // ------- Common -------- fun getAnswerVotingDoubtCase(answerData: AnswerData): VotingUseCase { @@ -142,6 +166,10 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return VotingUseCase(FirebaseFirestore.getInstance(), getUserManager().getCachedUserData()!!, false, null, doubtData) } + fun getFetchDoubtDataFromDoubtIdUseCase(): FetchDoubtDataFromDoubtIdUseCase { + return FetchDoubtDataFromDoubtIdUseCase(FirebaseFirestore.getInstance()) + } + // ------- User --------- private lateinit var userManager: UserManager @@ -213,11 +241,25 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return null } + fun getInAppFragNavigator(mainActivity: MainActivity): FragNavigator? { + + val inAppFrag = + (mainActivity.supportFragmentManager.findFragmentByTag("MainFragment") as MainFragment?) + ?.childFragmentManager?.findFragmentByTag("mainfrag_2") + + if (inAppFrag != null) { + return DoubtlessApp.getInstance().getAppCompRoot() + .getFragNavigator(inAppFrag.childFragmentManager, R.id.bottomNav_child_container) + } + + return null + } + fun getDashboardFragNavigator(mainActivity: MainActivity): FragNavigator? { val dashboardFrag = (mainActivity.supportFragmentManager.findFragmentByTag("MainFragment") as MainFragment?) - ?.childFragmentManager?.findFragmentByTag("mainfrag_2") + ?.childFragmentManager?.findFragmentByTag("mainfrag_3") if (dashboardFrag != null) { return DoubtlessApp.getInstance().getAppCompRoot() diff --git a/app/src/main/java/com/doubtless/doubtless/localDatabase/AppDatabase.kt b/app/src/main/java/com/doubtless/doubtless/localDatabase/AppDatabase.kt new file mode 100644 index 0000000..950304a --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/localDatabase/AppDatabase.kt @@ -0,0 +1,33 @@ +package com.doubtless.doubtless.localDatabase + +import androidx.room.* +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.screens.inAppNotification.dao.InAppNotificationDao +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity + +class AppDatabase { + @Database(entities = [InAppNotificationEntity::class], version = 1) + @TypeConverters(Converters::class) + abstract class AppDB : RoomDatabase() { + abstract fun inAppNotificationDao(): InAppNotificationDao + } + + companion object { + + private var database: AppDB? = null + + @Synchronized + fun getDbInstance(): AppDB { + + if (database == null) { + database = Room.databaseBuilder( + context = DoubtlessApp.getInstance(), + klass = AppDB::class.java, + name = "doubtless-db" + ).build() + } + + return database!! + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/localDatabase/Converter.kt b/app/src/main/java/com/doubtless/doubtless/localDatabase/Converter.kt new file mode 100644 index 0000000..fbdd0f5 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/localDatabase/Converter.kt @@ -0,0 +1,22 @@ +package com.doubtless.doubtless.localDatabase + +import androidx.room.TypeConverter +import com.google.firebase.Timestamp +import com.google.gson.Gson +import java.util.* + +class Converters { + + private val gson = Gson() + + @TypeConverter + fun longToTimestamp(value: Long): Timestamp { + return Timestamp(Date(value)) + } + + @TypeConverter + fun timestampToLong(timestamp: Timestamp): Long { + return timestamp.toDate().time + } + +} diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt index 9fd7db0..c2016df 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt @@ -20,6 +20,7 @@ import com.doubtless.doubtless.screens.auth.usecases.UserManager import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.view.ViewDoubtsViewModel import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.main.MainFragment class AnswersFragment : Fragment() { @@ -46,8 +47,23 @@ class AnswersFragment : Fragment() { userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() - val _navigator = DoubtlessApp.getInstance().getAppCompRoot() - .getHomeFragNavigator(requireActivity() as MainActivity) + + var _navigator: FragNavigator? = null + + // uuhh!! + val currentSelectedFrag = + (requireActivity() as MainActivity).getMainFragment()?.getCurrentSelectedElement() + + // encapsulate this logic + if (currentSelectedFrag is MainFragment.CurrentSelectedBottomNavFrag.HomeFrag) { + _navigator = DoubtlessApp.getInstance().getAppCompRoot() + .getHomeFragNavigator(requireActivity() as MainActivity) + } + + if (currentSelectedFrag is MainFragment.CurrentSelectedBottomNavFrag.DashboardFrag) { + _navigator = DoubtlessApp.getInstance().getAppCompRoot() + .getDashboardFragNavigator(requireActivity() as MainActivity) + } if (_navigator != null) navigator = _navigator diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index 7c1bbf8..c16ad5e 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -50,6 +50,8 @@ class DashboardFragment : Fragment() { private lateinit var tracker: AnalyticsTracker + private var onCreateEventUnConsumed = true + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) tracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() @@ -57,10 +59,9 @@ class DashboardFragment : Fragment() { userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() navigator = DoubtlessApp.getInstance().getAppCompRoot() .getDashboardFragNavigator(requireActivity() as MainActivity)!! - viewModel = getViewModel() - viewModel.fetchDoubts(forPageOne = true) + onCreateEventUnConsumed = true } override fun onCreateView( @@ -75,6 +76,11 @@ class DashboardFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + if (onCreateEventUnConsumed) { + viewModel.fetchDoubts(forPageOne = true) + onCreateEventUnConsumed = false + } + val feedList = mutableListOf() feedList.add(FeedEntity(FeedEntity.TYPE_USER_PROFILE, null)) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchDoubtDataFromDoubtIdUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchDoubtDataFromDoubtIdUseCase.kt new file mode 100644 index 0000000..a010ec1 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchDoubtDataFromDoubtIdUseCase.kt @@ -0,0 +1,27 @@ +package com.doubtless.doubtless.screens.doubt.usecases + +import com.doubtless.doubtless.constants.FirestoreCollection +import com.doubtless.doubtless.screens.doubt.DoubtData +import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext + +class FetchDoubtDataFromDoubtIdUseCase constructor( + private val firestore: FirebaseFirestore +) { + + suspend fun getDoubtData(doubtId: String): DoubtData? = withContext(Dispatchers.IO) { + try { + val document = firestore.collection(FirestoreCollection.AllDoubts) + .whereEqualTo("doubt_id", doubtId) + .get().await() + + return@withContext DoubtData.parse(document.documents[0]) + + } catch (e: Exception) { + return@withContext null + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotifContainerFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotifContainerFragment.kt new file mode 100644 index 0000000..de036fa --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotifContainerFragment.kt @@ -0,0 +1,59 @@ +package com.doubtless.doubtless.screens.inAppNotification + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.commit +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.R +import com.doubtless.doubtless.navigation.FragNavigator +import com.doubtless.doubtless.navigation.OnBackPressListener +import com.doubtless.doubtless.screens.dashboard.DashboardFragment +import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.main.MainFragment + +class InAppNotificationContainerFragment: Fragment(R.layout.fragment_home) { + + private lateinit var navigator: FragNavigator + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState == null) { + childFragmentManager.commit { + replace(R.id.bottomNav_child_container, InAppNotificationFragment()) + } + } + + navigator = DoubtlessApp.getInstance().getAppCompRoot() + .getInAppFragNavigator(requireActivity() as MainActivity)!! + } + + private val onBackPressListener = object : OnBackPressListener { + override fun onBackPress(): Boolean { + + val backPressConsumed = navigator.onBackPress() + + return if (backPressConsumed) + true + else { + (parentFragment as? MainFragment)?.selectHomeBottomNavElement() + true + } + } + } + + override fun onResume() { + super.onResume() + (requireActivity() as MainActivity).registerBackPress(onBackPressListener) + } + + override fun onPause() { + super.onPause() + (requireActivity() as MainActivity).unregisterBackPress(onBackPressListener) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationAdapter.kt new file mode 100644 index 0000000..029308e --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationAdapter.kt @@ -0,0 +1,50 @@ +package com.doubtless.doubtless.screens.inAppNotification + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.doubtless.doubtless.R +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity + +class InAppNotificationAdapter constructor( + private val notifications: List, + private val interactionListener: InteractionListener +) : RecyclerView.Adapter() { + + interface InteractionListener { + fun onPostAnswerNotifClicked(doubtId: String) + } + + // rn there is only post answer type. + private val TYPE_POST_ANSWER = 1 + + private val data: MutableList = notifications.toMutableList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + // fyi: passing null in the parent, renders spacings incorrectly. + val itemView = + LayoutInflater.from(parent.context).inflate(R.layout.item_post_answer_notif, parent, false) + return PostAnswerNotificationViewHolder(itemView, interactionListener) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is PostAnswerNotificationViewHolder) { + holder.bind(data[position]) + } + } + + override fun getItemViewType(position: Int): Int { + return TYPE_POST_ANSWER + } + + override fun getItemCount(): Int { + return data.size + } + + fun setNewNotifications(newNotifications: MutableList) { + data.clear() + data.addAll(newNotifications) + notifyDataSetChanged() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationFragment.kt new file mode 100644 index 0000000..09ad73e --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationFragment.kt @@ -0,0 +1,148 @@ +package com.doubtless.doubtless.screens.inAppNotification + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.R +import com.doubtless.doubtless.databinding.FragmentInappNotifBinding +import com.doubtless.doubtless.navigation.FragNavigator +import com.doubtless.doubtless.navigation.OnBackPressListener +import com.doubtless.doubtless.screens.doubt.usecases.FetchDoubtDataFromDoubtIdUseCase +import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.main.MainFragment +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class InAppNotificationFragment : Fragment() { + + private var _binding: FragmentInappNotifBinding? = null + private val binding get() = _binding!! + + private lateinit var adapter: InAppNotificationAdapter + private lateinit var navigator: FragNavigator + private lateinit var fetchDoubtDataFromDoubtIdUseCase: FetchDoubtDataFromDoubtIdUseCase + + private val viewModel: InAppNotificationViewModel by viewModels( + factoryProducer = { + InAppNotificationViewModel.Companion.Factory( + DoubtlessApp.getInstance().getAppCompRoot().getFetchNotificationUseCase(), + DoubtlessApp.getInstance().getAppCompRoot().getMarkInAppNotificationsReadUseCase() + ) + } + ) + + private var onCreateEventUnConsumed = true + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + onCreateEventUnConsumed = true + navigator = DoubtlessApp.getInstance().getAppCompRoot() + .getInAppFragNavigator(requireActivity() as MainActivity)!! + fetchDoubtDataFromDoubtIdUseCase = + DoubtlessApp.getInstance().getAppCompRoot().getFetchDoubtDataFromDoubtIdUseCase() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentInappNotifBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (onCreateEventUnConsumed) { + viewModel.fetchNotification() + onCreateEventUnConsumed = false + } + + if (::adapter.isInitialized) { + binding.rvNotif.adapter = adapter + } + + viewModel.notificationStatus.observe(viewLifecycleOwner) { + when (it) { + is InAppNotificationViewModel.Result.Success -> { + + binding.progressBar.isVisible = false + + if (::adapter.isInitialized == false) { + + adapter = InAppNotificationAdapter( + it.notifications.toMutableList(), + object : InAppNotificationAdapter.InteractionListener { + override fun onPostAnswerNotifClicked(doubtId: String) { + + CoroutineScope(Dispatchers.IO).launch { + + val doubtData = + fetchDoubtDataFromDoubtIdUseCase.getDoubtData(doubtId) + ?: return@launch + + if (!isAdded) return@launch + + navigator.moveToDoubtDetailFragment(doubtData) + } + } + }) + + binding.rvNotif.adapter = adapter + + if (it.additionalError != null) + Toast.makeText(requireContext(), it.additionalError, Toast.LENGTH_SHORT) + .show() + } else { + adapter.setNewNotifications(it.notifications.toMutableList()) + } + } + + is InAppNotificationViewModel.Result.Error -> { + binding.progressBar.isVisible = false + Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show() + } + + is InAppNotificationViewModel.Result.NoData -> { + + if (it.additionalError != null) + Toast.makeText(requireContext(), it.additionalError, Toast.LENGTH_SHORT) + .show() + + binding.progressBar.isVisible = false + } + + is InAppNotificationViewModel.Result.Loading -> { + binding.progressBar.isVisible = true + } + } + } + } + + private val onBackPressListener = object : OnBackPressListener { + override fun onBackPress(): Boolean { + (parentFragment as? MainFragment)?.selectHomeBottomNavElement() + return true + } + } + + override fun onResume() { + super.onResume() + (requireActivity() as MainActivity).registerBackPress(onBackPressListener) + } + + override fun onPause() { + super.onPause() + viewModel.markNotificationsAsRead() + (requireActivity() as MainActivity).unregisterBackPress(onBackPressListener) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationViewModel.kt new file mode 100644 index 0000000..d76add1 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/InAppNotificationViewModel.kt @@ -0,0 +1,89 @@ +package com.doubtless.doubtless.screens.inAppNotification + +import androidx.lifecycle.* +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity +import com.doubtless.doubtless.screens.inAppNotification.usecases.FetchInAppNotificationUseCase +import com.doubtless.doubtless.screens.inAppNotification.usecases.FetchUnreadNotificationUseCase +import com.doubtless.doubtless.screens.inAppNotification.usecases.MarkInAppNotificationsReadUseCase +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class InAppNotificationViewModel constructor( + private val fetchInAppNotificationUseCase: FetchInAppNotificationUseCase, + private val markInAppNotificationsReadUseCase: MarkInAppNotificationsReadUseCase +) : ViewModel() { + + sealed class Result { + class Success( + val notifications: List, + val additionalError: String? = null + ) : Result() + + class NoData(val additionalError: String? = null) : Result() + class Error(val message: String) : Result() + object Loading : Result() + } + + private val _notificationStatus = MutableLiveData() + val notificationStatus: LiveData = _notificationStatus + + fun fetchNotification() = CoroutineScope(Dispatchers.IO).launch { + + _notificationStatus.postValue(Result.Loading) + + val result = fetchInAppNotificationUseCase.getNotifications() + + when (result) { + is FetchInAppNotificationUseCase.Result.Success -> { + if (result.notifications.isNotEmpty()) + _notificationStatus.postValue( + Result.Success( + notifications = result.notifications.distinctBy { + it.notificationId + }.sortedByDescending { + it.createdOn?.toDate()?.time + }, + additionalError = result.error + ) + ) + else + _notificationStatus.postValue(Result.NoData(result.error)) + } + + is FetchInAppNotificationUseCase.Result.Error -> { + _notificationStatus.postValue(Result.Error(result.message)) + } + } + } + + fun markNotificationsAsRead() = CoroutineScope(Dispatchers.IO).launch { + if (_notificationStatus.value is Result.Success && _notificationStatus.value != null) { + + val notifs = + (_notificationStatus.value!! as Result.Success).notifications.toMutableList() + + markInAppNotificationsReadUseCase + .markRead(notifs) + + _notificationStatus.postValue(Result.Success(notifs.map { + it.copy(isRead = true) + })) + } + } + + companion object { + class Factory constructor( + private val fetchInAppNotificationUseCase: FetchInAppNotificationUseCase, + private val markInAppNotificationsReadUseCase: MarkInAppNotificationsReadUseCase + ) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return InAppNotificationViewModel( + fetchInAppNotificationUseCase, + markInAppNotificationsReadUseCase + ) as T + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/PostAnswerNotificationViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/PostAnswerNotificationViewHolder.kt new file mode 100644 index 0000000..ea38f49 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/PostAnswerNotificationViewHolder.kt @@ -0,0 +1,53 @@ +package com.doubtless.doubtless.screens.inAppNotification + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.bumptech.glide.Glide +import com.doubtless.doubtless.R +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity +import com.doubtless.doubtless.utils.Utils + +class PostAnswerNotificationViewHolder( + itemView: View, + private val interactionListener: InAppNotificationAdapter.InteractionListener +) : ViewHolder(itemView) { + + private val tvTopDescription: TextView = itemView.findViewById(R.id.tv_top_description) + private val tvDoubtDescription: TextView = itemView.findViewById(R.id.tv_doubt_description) + private val tvUsername: TextView = itemView.findViewById(R.id.tv_username) + private val tvUserYear: TextView = itemView.findViewById(R.id.user_year) + private val tvUserCollege: TextView = itemView.findViewById(R.id.user_college) + private val tvTimestamp: TextView = itemView.findViewById(R.id.author_answer_timestamp) + private val ivDp: ImageView = itemView.findViewById(R.id.iv_dp) + private val tvAnswerDescription: TextView = itemView.findViewById(R.id.user_answer_description) + + fun bind(data: InAppNotificationEntity) { + + itemView.setOnClickListener { + interactionListener.onPostAnswerNotifClicked(data.doubtId!!) + } + + if (!data.isRead) { + itemView.setBackgroundColor(itemView.resources.getColor(R.color.light_purple)) + } else { + itemView.setBackgroundColor(itemView.resources.getColor(R.color.white)) + } + + tvDoubtDescription.text = data.doubtHeading + Glide.with(itemView.context).load(data.authorPhotoUrl).circleCrop().into(ivDp) + tvUsername.text = data.answerAuthorName + tvUserYear.text = "" + tvUserCollege.text = "" + + try { + tvTimestamp.text = Utils.getTimeAgo(data.createdOn?.toDate()!!) + } catch (e: Exception) { + + } + + tvAnswerDescription.text = data.description?.trim() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/dao/InAppNotificationDao.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/dao/InAppNotificationDao.kt new file mode 100644 index 0000000..0c9e5c1 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/dao/InAppNotificationDao.kt @@ -0,0 +1,17 @@ +package com.doubtless.doubtless.screens.inAppNotification.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity + +@Dao +interface InAppNotificationDao { + + @Query("SELECT * FROM InAppNotifications WHERE isRead = true") + fun getAllReadNotifications(): List + + @Insert + fun insertNewNotifications(notificationEntities: List) + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/model/InAppNotificationEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/model/InAppNotificationEntity.kt new file mode 100644 index 0000000..dd4e95b --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/model/InAppNotificationEntity.kt @@ -0,0 +1,64 @@ +package com.doubtless.doubtless.screens.inAppNotification.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.google.errorprone.annotations.Keep +import com.google.firebase.Timestamp +import com.google.firebase.firestore.DocumentSnapshot +import com.google.firebase.firestore.Exclude +import com.google.firebase.firestore.IgnoreExtraProperties +import com.google.firebase.firestore.ServerTimestamp +import com.google.firebase.firestore.ktx.getField + +@Keep +@Entity(tableName = "InAppNotifications") +@IgnoreExtraProperties +data class InAppNotificationEntity( + @get:Exclude + @PrimaryKey + val notificationId: String, + val answerAuthorId: String? = null, + val answerAuthorName: String? = null, + val answerId: String? = null, + val authorPhotoUrl: String? = null, + @ServerTimestamp + val createdOn: Timestamp? = null, + val description: String? = null, + val doubtAuthorId: String? = null, + val doubtHeading: String? = null, + val doubtId: String? = null, + val type: String? = TYPE_POST_ANSWER, + val isRead: Boolean = false +) { + companion object { + + const val TYPE_POST_ANSWER = "postAnswer" + + fun fromDocumentSnapshot(documentSnapshot: DocumentSnapshot?): InAppNotificationEntity? { + return try { + val notif = InAppNotificationEntity( + notificationId = documentSnapshot!!.id, + answerAuthorId = documentSnapshot.getField("answer_author_id"), + answerAuthorName = documentSnapshot.getField("answer_author_name"), + answerId = documentSnapshot.getField("answer_id"), + authorPhotoUrl = documentSnapshot.getField("author_photo_url"), + createdOn = documentSnapshot.getField("created_on"), + description = documentSnapshot.getField("answer_description"), + doubtAuthorId = documentSnapshot.getField("doubt_author_id"), + doubtHeading = documentSnapshot.getField("doubt_heading"), + doubtId = documentSnapshot.getField("doubt_id"), + type = documentSnapshot.getField("type") + ) + + if (notif.type?.equals(TYPE_POST_ANSWER) == false) { + return null + } + + return notif + + } catch (e: Exception) { + null + } + } + } +} diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchInAppNotificationUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchInAppNotificationUseCase.kt new file mode 100644 index 0000000..1a1dfd0 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchInAppNotificationUseCase.kt @@ -0,0 +1,52 @@ +package com.doubtless.doubtless.screens.inAppNotification.usecases + +import com.doubtless.doubtless.screens.inAppNotification.dao.InAppNotificationDao +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class FetchInAppNotificationUseCase constructor( + private val inAppNotificationDao: InAppNotificationDao, + private val unreadNotificationUseCase: FetchUnreadNotificationUseCase +) { + sealed class Result { + class Success(val notifications: List, val error: String? = null) : Result() + class Error(val message: String): Result() + } + + suspend fun getNotifications(): Result = withContext(Dispatchers.IO) { + try { + val notifications = mutableListOf() + + var error: String? = null + + // first fetch all unread notifications + val result = unreadNotificationUseCase.fetchNotification() + + if (result is FetchUnreadNotificationUseCase.Result.Success) { + notifications.addAll(result.notifications) + } + + if (result is FetchUnreadNotificationUseCase.Result.Error) { + error = result.message + } + + // now fetch old read notifications from db + try { + notifications.addAll(inAppNotificationDao.getAllReadNotifications()) + } catch (e: Exception) { + error = e.message + } + + if (notifications.isEmpty() && error != null) { + return@withContext Result.Error(error) + } + + return@withContext Result.Success(notifications, error) + + } catch (e: Exception) { + return@withContext Result.Error(e.message ?: "some error occurred!") + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchUnreadNotificationUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchUnreadNotificationUseCase.kt new file mode 100644 index 0000000..7d6fd6b --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/FetchUnreadNotificationUseCase.kt @@ -0,0 +1,48 @@ +package com.doubtless.doubtless.screens.inAppNotification.usecases + +import com.doubtless.doubtless.constants.FirestoreCollection +import com.doubtless.doubtless.screens.auth.usecases.UserManager +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity +import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext + +class FetchUnreadNotificationUseCase constructor( + private val firestore: FirebaseFirestore, + private val userManager: UserManager +) { + + sealed class Result { + class Success(val notifications: List) : Result() + class Error(val message: String) : Result() + } + + suspend fun fetchNotification(): Result = withContext(Dispatchers.IO) { + return@withContext try { + + val userId = userManager.getCachedUserData()!!.id + + val result = firestore.collection(FirestoreCollection.NOTIFICATION) + .whereEqualTo("doubt_author_id", userId) + .whereEqualTo("is_read", false) + .get().await() + + val notifications = mutableListOf() + + result.documents.forEach { + try { + notifications.add(InAppNotificationEntity.fromDocumentSnapshot(it)!!) + } catch (e: Exception) { + e.printStackTrace() + } + } + + return@withContext Result.Success(notifications) + + } catch (e: Exception) { + Result.Error(e.message ?: "some error occurred!") + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/MarkInAppNotificationsReadUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/MarkInAppNotificationsReadUseCase.kt new file mode 100644 index 0000000..3ed8ba5 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/inAppNotification/usecases/MarkInAppNotificationsReadUseCase.kt @@ -0,0 +1,52 @@ +package com.doubtless.doubtless.screens.inAppNotification.usecases + +import com.doubtless.doubtless.constants.FirestoreCollection +import com.doubtless.doubtless.screens.inAppNotification.dao.InAppNotificationDao +import com.doubtless.doubtless.screens.inAppNotification.model.InAppNotificationEntity +import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class MarkInAppNotificationsReadUseCase constructor( + private val dao: InAppNotificationDao, + private val firestore: FirebaseFirestore +) { + + sealed class Result { + object Success : Result() + class Error(val message: String) : Result() + } + + suspend fun markRead(notifications: List): Result = + withContext(Dispatchers.IO) { + + try { + val markedReadNotifications = notifications.filter { + it.isRead == false + }.map { + it.copy(isRead = true) + } + + markReadOnFirestore(notifications) + + dao.insertNewNotifications(markedReadNotifications) + + return@withContext Result.Success + + } catch (e: Exception) { + return@withContext Result.Error(e.message ?: "some error occurred!") + } + + } + + private fun markReadOnFirestore(notifications: List) { + notifications.forEach { + + firestore.collection(FirestoreCollection.NOTIFICATION) + .document(it.notificationId) + .update("is_read", true) + + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/MainActivity.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/MainActivity.kt index 4827477..f1d70a8 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/MainActivity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/MainActivity.kt @@ -25,6 +25,10 @@ class MainActivity : AppCompatActivity(), BackPressDispatcher { } } + fun getMainFragment(): MainFragment? { + return supportFragmentManager.findFragmentByTag("MainFragment") as MainFragment? + } + // ----- backpress impl ------ private val backPressListeners: MutableList = mutableListOf() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt index c022c73..eab986f 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/MainFragment.kt @@ -13,6 +13,8 @@ import com.doubtless.doubtless.screens.dashboard.DashboardFragment import com.doubtless.doubtless.screens.doubt.create.CreateDoubtFragment import com.doubtless.doubtless.screens.doubt.view.ViewDoubtsFragment import com.doubtless.doubtless.screens.home.HomeFragment +import com.doubtless.doubtless.screens.inAppNotification.InAppNotificationContainerFragment +import com.doubtless.doubtless.screens.inAppNotification.InAppNotificationFragment class MainFragment : Fragment() { @@ -20,7 +22,12 @@ class MainFragment : Fragment() { private val binding get() = _binding!! private val bottomNavFragments = - listOf(HomeFragment(), CreateDoubtFragment(), DashboardContainerFragment()) + listOf( + HomeFragment(), + CreateDoubtFragment(), + InAppNotificationContainerFragment(), + DashboardContainerFragment() + ) private var areBottomNavFragmentsAdded = false @@ -82,6 +89,29 @@ class MainFragment : Fragment() { } } + sealed class CurrentSelectedBottomNavFrag { + object HomeFrag : CurrentSelectedBottomNavFrag() + object CreateFrag : CurrentSelectedBottomNavFrag() + object InAppNotificationFrag : CurrentSelectedBottomNavFrag() + object DashboardFrag : CurrentSelectedBottomNavFrag() + object UnknownFrag : CurrentSelectedBottomNavFrag() + } + + fun getCurrentSelectedElement(): CurrentSelectedBottomNavFrag { + + val index = binding.retroBottomNav.getCurrentSelectedIndex() + + return when (index) { + 0 -> CurrentSelectedBottomNavFrag.HomeFrag + 1 -> CurrentSelectedBottomNavFrag.CreateFrag + 2 -> CurrentSelectedBottomNavFrag.InAppNotificationFrag + 3 -> CurrentSelectedBottomNavFrag.DashboardFrag + else -> { + CurrentSelectedBottomNavFrag.UnknownFrag + } + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavData.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavData.kt index 9297c5d..218b845 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavData.kt @@ -14,4 +14,5 @@ interface OnSelectedItemChangedListener { interface BottomIntractableElement { fun onSelected() fun onUnselected() + fun onReselected() } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt index 027e934..a052338 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/main/bottomNav/BottomNavLayout.kt @@ -39,11 +39,7 @@ class BottomNavLayout( private fun onNewSelectedIndex(index: Int) { onSelectedItemChangedListener?.onNewSelectedIndex(index) - elements[index].onSelected() -// -// (elements[index] as View).layoutParams = -// LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f) } private fun notifyAllUnSelectedElements() { @@ -61,7 +57,10 @@ class BottomNavLayout( clickedBtn: BottomIntractableElement ) { // proceed on only new index selection. - if (currentSelectedIndex == idx) return + if (currentSelectedIndex == idx) { + clickedBtn.onReselected() + return + } currentSelectedIndex = idx onSelectedItemChangedListener?.onNewSelectedIndex(idx) diff --git a/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt b/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt index 8aff1ac..2ad3246 100644 --- a/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt +++ b/app/src/main/java/com/doubtless/doubtless/theming/bottomNav/SoberBottomNavElementLayout.kt @@ -12,6 +12,11 @@ class SoberBottomNavElementLayout(context: Context, attributeSet: AttributeSet?) isChecked = true } + override fun onReselected() { + // on reselection the checked icon will toggle, hence manually make it checked. + isChecked = true + } + override fun onUnselected() { isChecked = false } diff --git a/app/src/main/java/com/doubtless/doubtless/theming/buttons/RetroBottomNavElementLayout.kt b/app/src/main/java/com/doubtless/doubtless/theming/buttons/RetroBottomNavElementLayout.kt index 600763a..db36ecd 100644 --- a/app/src/main/java/com/doubtless/doubtless/theming/buttons/RetroBottomNavElementLayout.kt +++ b/app/src/main/java/com/doubtless/doubtless/theming/buttons/RetroBottomNavElementLayout.kt @@ -37,4 +37,8 @@ class RetroBottomNavElementLayout(context: Context, attributeSet: AttributeSet?) isCurrentlySelected = false } + override fun onReselected() { + + } + } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/theming/buttons/SecondaryButton.kt b/app/src/main/java/com/doubtless/doubtless/theming/buttons/SecondaryButton.kt index d017866..66db7d5 100644 --- a/app/src/main/java/com/doubtless/doubtless/theming/buttons/SecondaryButton.kt +++ b/app/src/main/java/com/doubtless/doubtless/theming/buttons/SecondaryButton.kt @@ -76,6 +76,10 @@ class SecondaryButton constructor( setCardBackgroundColor(resources.getColor(R.color.cream)) } + override fun onReselected() { + + } + override fun onUnselected() { setCardBackgroundColor(resources.getColor(R.color.cream)) isCurrentlySelected = false diff --git a/app/src/main/res/drawable/ic_baseline_notifications_24.xml b/app/src/main/res/drawable/ic_baseline_notifications_24.xml new file mode 100644 index 0000000..1d038a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_notifications_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_outline_notifications_24.xml b/app/src/main/res/drawable/ic_outline_notifications_24.xml new file mode 100644 index 0000000..3d20551 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_notifications_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_reply_arrow_24.xml b/app/src/main/res/drawable/ic_reply_arrow_24.xml new file mode 100644 index 0000000..f75e16a --- /dev/null +++ b/app/src/main/res/drawable/ic_reply_arrow_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/notif_checkable_icon.xml b/app/src/main/res/drawable/notif_checkable_icon.xml new file mode 100644 index 0000000..b199ae9 --- /dev/null +++ b/app/src/main/res/drawable/notif_checkable_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_inapp_notif.xml b/app/src/main/res/layout/fragment_inapp_notif.xml new file mode 100644 index 0000000..f6e1887 --- /dev/null +++ b/app/src/main/res/layout/fragment_inapp_notif.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 6ccaf19..2dccd33 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -38,7 +38,7 @@ android:button="@null" android:checked="false" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.2" + app:layout_constraintHorizontal_bias="0.1" app:layout_constraintStart_toStartOf="parent" /> + + + app:layout_constraintStart_toEndOf="@id/btm_nav_notif" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_post_answer_notif.xml b/app/src/main/res/layout/item_post_answer_notif.xml new file mode 100644 index 0000000..254a777 --- /dev/null +++ b/app/src/main/res/layout/item_post_answer_notif.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 60f4a5e..2c775ac 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,5 +9,6 @@ #e090c9 #fab679 #A93AFF + #FAF4FF #99B8B8B8 \ No newline at end of file From ae71a9e32f8694ce0166dbd4422b74d62cbee3f1 Mon Sep 17 00:00:00 2001 From: Aditya <77337791+Aditya13s@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:33:26 +0530 Subject: [PATCH 20/35] Home Screen Filter Tags (#77) * Implemented Filter Tags in the Main Home Screen * File Rename * Fixes * Addressed code review feedback * Review Changes * Removed onSearchClicked() function from fragments * Removed Unused code * fix fatal NPE for ViewDoubtViewModel (#73) * fix fatal NPE * logout user if user is null * code clean up * add non fatal log for null user * Added icon animations to upvote and downvote icons for Doubts and Answers (#68) * test * added animation to upvote and downvote icons for doubts * added animation to upvote and downvote icons for answers * fix animation for first load and code clean up * fix merge conflict --------- Co-authored-by: pratthamarora * automatically close keyboard after search query is submitted (#70) * test * added animation to upvote and downvote icons for doubts * added animation to upvote and downvote icons for answers * fix animation for first load and code clean up * automatically close keyboard after search query is submitted * fix merge conflict --------- Co-authored-by: pratthamarora * bottom nav icons changes * removed voting usecase from onBind * on pressing back on bottom nav redirects to home frag * in app notif implemented * Added Analytics in Filter Tags * Implemented Filter Tags in the Main Home Screen * File Rename * Fixes * Addressed code review feedback * Review Changes * Removed onSearchClicked() function from fragments * Removed Unused code * Added Analytics in Filter Tags * Fixed Merge Conflicts * fixed merge conflicts --------- Co-authored-by: Prattham Arora <55047418+PratthamArora@users.noreply.github.com> Co-authored-by: pratthamarora Co-authored-by: sidsharma2002 Co-authored-by: Siddharth sharma --- .../doubtless/analytics/AnalyticsTracker.kt | 12 +- .../constants/FirestoreCollection.kt | 7 +- .../doubtless/di/AppCompositionRoot.kt | 50 ++++++-- .../screens/common/GenericFeedAdapter.kt | 67 ++-------- .../screens/dashboard/DashboardFragment.kt | 2 - .../doubt/FilterTagsViewPagerAdapter.kt | 26 ++++ .../screens/doubt/HomeMainScreenFragment.kt | 112 +++++++++++++++++ .../doubt/usecases/FetchFilterTagsUseCase.kt | 45 +++++++ .../doubt/view/HomeMainScreenViewModel.kt | 50 ++++++++ .../screens/doubt/view/ViewDoubtsFragment.kt | 97 +++++++-------- .../screens/doubt/view/ViewDoubtsViewModel.kt | 117 ++++++++++-------- .../doubtless/screens/home/HomeFragment.kt | 5 +- .../screens/home/entities/FeedEntity.kt | 19 +-- .../home/usecases/FetchFeedByDateUseCase.kt | 22 +++- .../usecases/FetchFeedByPopularityUseCase.kt | 25 +++- .../home/usecases/FetchHomeFeedUseCase.kt | 10 +- .../screens/search/SearchFragment.kt | 54 ++++---- .../res/layout/fragment_home_main_screen.xml | 52 ++++++++ app/src/main/res/values/style.xml | 8 ++ 19 files changed, 544 insertions(+), 236 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/FilterTagsViewPagerAdapter.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchFilterTagsUseCase.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt create mode 100644 app/src/main/res/layout/fragment_home_main_screen.xml create mode 100644 app/src/main/res/values/style.xml diff --git a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt index 491a798..40e2330 100644 --- a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt +++ b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt @@ -10,6 +10,16 @@ class AnalyticsTracker constructor( private val userManager: UserManager ) { + fun trackTagsFragment(tag: String) { + val map = hashMapOf() + map.putAll(getCommonAttrs()) + + map["tag"] = tag + + amplitude.track("tags", map) + } + + fun trackLoginStarted() { val map = hashMapOf() map.putAll(getCommonAttrs()) @@ -58,7 +68,7 @@ class AnalyticsTracker constructor( fun trackDoubtUpVoted(doubtData: DoubtData) { val map = getCommonAttrs().toMutableMap().apply { - this["doubt_data"] = doubtData.toString() + this["doubt_data"] = doubtData.toString() } amplitude.track("doubt_upvote", map) diff --git a/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt b/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt index 0e6f560..97fe5d2 100644 --- a/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt +++ b/app/src/main/java/com/doubtless/doubtless/constants/FirestoreCollection.kt @@ -1,7 +1,5 @@ package com.doubtless.doubtless.constants -import com.doubtless.doubtless.BuildConfig - class FirestoreCollection { companion object { @@ -21,6 +19,11 @@ class FirestoreCollection { val DOWNVOTE_DATA_USERS = "downvoted_users" val NOTIFICATION = "notifications" + + const val TAG_MY_COLLEGE = "My College" + const val TAG_ALL = "All" + + } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt index eed5b06..5ac4ac5 100644 --- a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt +++ b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt @@ -26,6 +26,7 @@ import com.doubtless.doubtless.screens.dashboard.usecases.FetchUserFeedByDateUse import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.usecases.DoubtDataSharedPrefUseCase import com.doubtless.doubtless.screens.doubt.usecases.FetchDoubtDataFromDoubtIdUseCase +import com.doubtless.doubtless.screens.doubt.usecases.FetchFilterTagsUseCase import com.doubtless.doubtless.screens.doubt.usecases.PostDoubtUseCase import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.screens.home.entities.FeedConfig @@ -141,11 +142,17 @@ class AppCompositionRoot(appContext: DoubtlessApp) { // --------- InApp Notification ---------- fun getFetchNotificationUseCase(): FetchInAppNotificationUseCase { - return FetchInAppNotificationUseCase(getInAppNotificationDao(), getFetchUnreadNotificationUseCase()) + return FetchInAppNotificationUseCase( + getInAppNotificationDao(), + getFetchUnreadNotificationUseCase() + ) } fun getMarkInAppNotificationsReadUseCase(): MarkInAppNotificationsReadUseCase { - return MarkInAppNotificationsReadUseCase(getInAppNotificationDao(), FirebaseFirestore.getInstance()) + return MarkInAppNotificationsReadUseCase( + getInAppNotificationDao(), + FirebaseFirestore.getInstance() + ) } private fun getFetchUnreadNotificationUseCase(): FetchUnreadNotificationUseCase { @@ -156,14 +163,26 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return AppDatabase.getDbInstance().inAppNotificationDao() } - // ------- Common -------- + // ------- Doubt Preview -------- fun getAnswerVotingDoubtCase(answerData: AnswerData): VotingUseCase { - return VotingUseCase(FirebaseFirestore.getInstance(), getUserManager().getCachedUserData()!!, true, answerData, null) + return VotingUseCase( + FirebaseFirestore.getInstance(), + getUserManager().getCachedUserData()!!, + true, + answerData, + null + ) } fun getDoubtVotingDoubtCase(doubtData: DoubtData): VotingUseCase { - return VotingUseCase(FirebaseFirestore.getInstance(), getUserManager().getCachedUserData()!!, false, null, doubtData) + return VotingUseCase( + FirebaseFirestore.getInstance(), + getUserManager().getCachedUserData()!!, + false, + null, + doubtData + ) } fun getFetchDoubtDataFromDoubtIdUseCase(): FetchDoubtDataFromDoubtIdUseCase { @@ -263,13 +282,19 @@ class AppCompositionRoot(appContext: DoubtlessApp) { if (dashboardFrag != null) { return DoubtlessApp.getInstance().getAppCompRoot() - .getFragNavigator(dashboardFrag.childFragmentManager, R.id.bottomNav_child_container) + .getFragNavigator( + dashboardFrag.childFragmentManager, + R.id.bottomNav_child_container + ) } return null } - private fun getFragNavigator(supportFragmentManager: FragmentManager, @IdRes containerId: Int ): FragNavigator { + private fun getFragNavigator( + supportFragmentManager: FragmentManager, + @IdRes containerId: Int + ): FragNavigator { return FragNavigator( containerId, supportFragmentManager @@ -327,6 +352,7 @@ class AppCompositionRoot(appContext: DoubtlessApp) { return remoteConfig } + // --------- Dashboard ----------- fun getFetchUserDataUseCase(): FetchUserDataUseCase { return FetchUserDataUseCase( FetchUserFeedByDateUseCase(FirebaseFirestore.getInstance()), @@ -334,9 +360,17 @@ class AppCompositionRoot(appContext: DoubtlessApp) { ) } - fun getDeleteAccountUseCase() : DeleteAccountUseCase { + fun getDeleteAccountUseCase(): DeleteAccountUseCase { return DeleteAccountUseCase( FirebaseFirestore.getInstance() ) } + + // --------- Home Main Screen ----------- + + fun getFetchFilterTagsUseCase(): FetchFilterTagsUseCase { + return FetchFilterTagsUseCase( + FirebaseFirestore.getInstance() + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 072069f..c9350d2 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -10,8 +10,6 @@ import com.doubtless.doubtless.screens.dashboard.viewholder.UserProfileViewHolde import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity -import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder -import com.doubtless.doubtless.screens.poll.ViewPollViewHolder class GenericFeedAdapter( private val genericFeedEntities: MutableList, @@ -20,13 +18,10 @@ class GenericFeedAdapter( ) : RecyclerView.Adapter() { interface InteractionListener { - fun onSearchBarClicked() fun onDoubtClicked(doubtData: DoubtData, position: Int) fun onSignOutClicked() fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() - fun onCreatePollClicked() - fun onPollOptionClicked(position: Int, option: String) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -38,7 +33,9 @@ class GenericFeedAdapter( return UserProfileViewHolder( view, object : UserProfileViewHolder.InteractionListener { - override fun onDeleteAccountClicked() = interactionListener.onDeleteAccountClicked() + override fun onDeleteAccountClicked() = + interactionListener.onDeleteAccountClicked() + override fun onSignOutClicked() = interactionListener.onSignOutClicked() override fun onSubmitFeedbackClicked() = interactionListener.onSubmitFeedbackClicked() @@ -48,8 +45,7 @@ class GenericFeedAdapter( FeedEntity.TYPE_DOUBT -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.doubt_layout, parent, false) - return DoubtPreviewViewHolder( - view = view, + return DoubtPreviewViewHolder(view = view, showVotingLayout = false, interactionListener = object : DoubtPreviewViewHolder.InteractionListener { override fun onDoubtClicked(doubtData: DoubtData, position: Int) { @@ -58,23 +54,10 @@ class GenericFeedAdapter( }) } - FeedEntity.TYPE_SEARCH -> { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.layout_home_search, parent, false) - return HomeSearchViewHolder( - view, - object : HomeSearchViewHolder.InteractionListener { - override fun onLayoutClicked() { - interactionListener.onSearchBarClicked() - } - }) - } - FeedEntity.TYPE_SEARCH_RESULT -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.doubt_layout, parent, false) - return DoubtPreviewViewHolder( - view = view, + return DoubtPreviewViewHolder(view = view, showVotingLayout = false, interactionListener = object : DoubtPreviewViewHolder.InteractionListener { override fun onDoubtClicked(doubtData: DoubtData, position: Int) { @@ -82,32 +65,6 @@ class GenericFeedAdapter( } }) } - - FeedEntity.TYPE_BUTTONS -> { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.layout_home_buttons, parent, false) - return ExtraOptionsButtonHolder(view= view, - object : ExtraOptionsButtonHolder.InteractionListener{ - override fun onCreatePollClicked() { - interactionListener.onCreatePollClicked() - } - - } - ) - } - - FeedEntity.TYPE_POLL -> { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.view_poll, parent, false) - return ViewPollViewHolder( - view = view, - interactionListener = object : ViewPollViewHolder.InteractionListener { - override fun onPollOptionClicked(position: Int, option: String) { - interactionListener.onPollOptionClicked(position, option) - } - } - ) - } } throw Exception("type is not defined") @@ -118,15 +75,13 @@ class GenericFeedAdapter( userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() ) - if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_DOUBT) - holder.setData(genericFeedEntities[position].doubt!!) - - if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_SEARCH_RESULT) - holder.setData(genericFeedEntities[position].search_doubt!!.toDoubtData()) - - if (holder is ViewPollViewHolder && getItemViewType(position) == FeedEntity.TYPE_POLL) - holder.setData(genericFeedEntities[position]) + if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_DOUBT) holder.setData( + genericFeedEntities[position].doubt!! + ) + if (holder is DoubtPreviewViewHolder && getItemViewType(position) == FeedEntity.TYPE_SEARCH_RESULT) holder.setData( + genericFeedEntities[position].search_doubt!!.toDoubtData() + ) if (position == itemCount - 1) { onLastItemReached.invoke() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index c16ad5e..3ce3572 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -88,8 +88,6 @@ class DashboardFragment : Fragment() { adapter = GenericFeedAdapter(genericFeedEntities = feedList, onLastItemReached = { viewModel.fetchDoubts() }, interactionListener = object : GenericFeedAdapter.InteractionListener { - override fun onSearchBarClicked() { - } override fun onDoubtClicked(doubtData: DoubtData, position: Int) { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/FilterTagsViewPagerAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/FilterTagsViewPagerAdapter.kt new file mode 100644 index 0000000..c8e88ac --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/FilterTagsViewPagerAdapter.kt @@ -0,0 +1,26 @@ +package com.doubtless.doubtless.screens.doubt + +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.doubtless.doubtless.screens.doubt.view.ViewDoubtsFragment + +class FilterTagsViewPagerAdapter( + homeMainScreenFragment: HomeMainScreenFragment, private val tagsList: List +) : FragmentStateAdapter( + homeMainScreenFragment +) { + + override fun getItemCount(): Int { + return tagsList.size + } + + override fun createFragment(position: Int): Fragment { + val fragment = ViewDoubtsFragment() + val args = Bundle() + args.putString("tag", tagsList[position]) + fragment.arguments = args + return fragment + + } +} diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt new file mode 100644 index 0000000..d4f1415 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt @@ -0,0 +1,112 @@ +package com.doubtless.doubtless.screens.doubt + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.analytics.AnalyticsTracker +import com.doubtless.doubtless.databinding.FragmentHomeMainScreenBinding +import com.doubtless.doubtless.navigation.FragNavigator +import com.doubtless.doubtless.screens.auth.usecases.UserManager +import com.doubtless.doubtless.screens.doubt.view.HomeMainScreenViewModel +import com.doubtless.doubtless.screens.main.MainActivity +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import java.util.Locale + + +class HomeMainScreenFragment : Fragment() { + private var _binding: FragmentHomeMainScreenBinding? = null + private val binding get() = _binding!! + private lateinit var navigator: FragNavigator + private lateinit var viewModel: HomeMainScreenViewModel + private lateinit var userManager: UserManager + private lateinit var analyticsTracker: AnalyticsTracker + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentHomeMainScreenBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() + analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() + + val _navigator = DoubtlessApp.getInstance().getAppCompRoot() + .getHomeFragNavigator(requireActivity() as MainActivity) + if (_navigator != null) navigator = _navigator + + viewModel = getViewModel() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (viewModel.tags.value.isNullOrEmpty()) { + viewModel.fetchTags() + + } + + viewModel.tags.observe(viewLifecycleOwner) { + if (binding.viewPager.adapter == null) { + setupViewPager(it) + } + } + binding.tvSearch.setOnClickListener { + navigator.moveToSearchFragment() + } + + } + + private fun setupViewPager(tags: List) { + val pagerAdapter = FilterTagsViewPagerAdapter(this@HomeMainScreenFragment, tags) + binding.viewPager.adapter = pagerAdapter + val capitalizedTagList = tags.map { s -> + s.replaceFirstChar { + if (it.isLowerCase()) it.uppercase( + Locale.ROOT + ) else it.toString() + } + }.toMutableList() + + TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position -> + if (position == 0) { + tab.text = "${userManager.getCachedUserData()?.local_user_attr!!.college!!} only" + return@TabLayoutMediator + } + tab.text = capitalizedTagList[position] + }.attach() + + binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + analyticsTracker.trackTagsFragment(capitalizedTagList[tab!!.position]) + } + override fun onTabUnselected(tab: TabLayout.Tab?) {} + override fun onTabReselected(tab: TabLayout.Tab?) {} + + }) + + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun getViewModel(): HomeMainScreenViewModel { + return ViewModelProvider( + owner = this, factory = HomeMainScreenViewModel.Companion.Factory( + fetchFilterTagsUseCase = DoubtlessApp.getInstance().getAppCompRoot() + .getFetchFilterTagsUseCase() + ) + )[HomeMainScreenViewModel::class.java] + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchFilterTagsUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchFilterTagsUseCase.kt new file mode 100644 index 0000000..22de2c2 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchFilterTagsUseCase.kt @@ -0,0 +1,45 @@ +package com.doubtless.doubtless.screens.doubt.usecases + +import com.doubtless.doubtless.constants.FirestoreCollection +import com.google.firebase.firestore.FirebaseFirestore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext + +class FetchFilterTagsUseCase constructor( + private val firestore: FirebaseFirestore, +) { + private var cachedTags: List? = null + + sealed class Result { + class Success(val data: List) : Result() + class Error(val message: String) : Result() + } + + suspend fun fetchTagsFromFirebase(): Result = withContext(Dispatchers.IO) { + return@withContext try { + if (cachedTags?.isNotEmpty() == true) { + Result.Success(cachedTags!!) + } else { + val tags: MutableList = mutableListOf() + + val querySnapshot = firestore.collection(FirestoreCollection.MiscAppData) + .whereEqualTo("type", "attr_data").get().await() + + for (document in querySnapshot.documents) { + val tagsList = document.get("tags") as? List + tagsList?.let { + tags.addAll(it) + } + } + + cachedTags = tags + Result.Success(tags) + } + + } catch (e: Exception) { + Result.Error(e.message ?: "Failed to fetch tags") + } + } + +} diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt new file mode 100644 index 0000000..0430bdc --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt @@ -0,0 +1,50 @@ +package com.doubtless.doubtless.screens.doubt.view + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.doubtless.doubtless.constants.FirestoreCollection +import com.doubtless.doubtless.screens.doubt.usecases.FetchFilterTagsUseCase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class HomeMainScreenViewModel constructor( + private val fetchFilterTagsUseCase: FetchFilterTagsUseCase, +) : ViewModel() { + + private val _tags = MutableLiveData>() + val tags: LiveData> = _tags + + fun fetchTags() = viewModelScope.launch(Dispatchers.IO) { + + val result = fetchFilterTagsUseCase.fetchTagsFromFirebase() + + if (result is FetchFilterTagsUseCase.Result.Error) { + _tags.postValue(listOf(FirestoreCollection.TAG_MY_COLLEGE, FirestoreCollection.TAG_ALL)) + return@launch + } + + result as FetchFilterTagsUseCase.Result.Success + + val tags = result.data.toMutableList() + tags.addAll( + 0, + listOf(FirestoreCollection.TAG_MY_COLLEGE, FirestoreCollection.TAG_ALL) + ) + _tags.postValue(tags) + } + + + companion object { + class Factory constructor( + private val fetchFilterTagsUseCase: FetchFilterTagsUseCase + ) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return HomeMainScreenViewModel(fetchFilterTagsUseCase) as T + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index d749f41..92c96a0 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -23,7 +23,6 @@ import com.doubtless.doubtless.screens.home.entities.FeedConfig import com.doubtless.doubtless.screens.main.MainActivity import com.doubtless.doubtless.utils.Resource import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.gson.Gson @@ -39,34 +38,30 @@ class ViewDoubtsFragment : Fragment() { private lateinit var navigator: FragNavigator private lateinit var remoteConfig: FirebaseRemoteConfig private lateinit var feedConfig: FeedConfig - private lateinit var firebaseFirestore: FirebaseFirestore + private lateinit var tag: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) - // enterTransition = inflater.inflateTransition(R.transition.slide) + //enterTransition = inflater.inflateTransition(R.transition.slide) // exitTransition = inflater.inflateTransition(R.transition.fade) + tag = arguments?.getString("tag")!! + userManager = DoubtlessApp.getInstance().getAppCompRoot().getUserManager() analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() remoteConfig = DoubtlessApp.getInstance().getAppCompRoot().getRemoteConfig() - feedConfig = FeedConfig.parse(Gson(), remoteConfig) - ?: FeedConfig( - pageSize = 10, - recentPostsCount = 6, - feedDebounce = 3000, - searchDebounce = 600 - ) + feedConfig = FeedConfig.parse(Gson(), remoteConfig) ?: FeedConfig( + pageSize = 10, recentPostsCount = 6, feedDebounce = 3000, searchDebounce = 600 + ) val _navigator = DoubtlessApp.getInstance().getAppCompRoot() .getHomeFragNavigator(requireActivity() as MainActivity) - if (_navigator != null) - navigator = _navigator + if (_navigator != null) navigator = _navigator viewModel = getViewModel() - viewModel.fetchDoubts(forPageOne = true) } override fun onCreateView( @@ -79,10 +74,14 @@ class ViewDoubtsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + if (viewModel.fetchedHomeEntities.value?.data.isNullOrEmpty()) { + viewModel.fetchDoubts(forPageOne = true, feedTag = tag) + } + // for debouncing var lastRefreshed = System.currentTimeMillis() - binding.llProgressBar.visibility= View.VISIBLE //show progress bar + binding.llProgressBar.visibility = View.VISIBLE //show progress bar binding.layoutSwipe.setOnRefreshListener { @@ -93,49 +92,38 @@ class ViewDoubtsFragment : Fragment() { } lastRefreshed = System.currentTimeMillis() - binding.layoutSwipe.isRefreshing = true - viewModel.refreshList() + viewModel.refreshList(tag = tag) adapter.clearCurrentList() } - adapter = GenericFeedAdapter( - genericFeedEntities = viewModel.homeEntities.toMutableList(), - onLastItemReached = { - viewModel.fetchDoubts() - }, - interactionListener = object : GenericFeedAdapter.InteractionListener { - override fun onSearchBarClicked() { - navigator.moveToSearchFragment() - } - - override fun onDoubtClicked(doubtData: DoubtData, position: Int) { - // note that this we are not sending a copy of doubtData here, - // hence if netVotes are changed on the other screen then it will change here too. - // this solves our problem but can cause complications on long term. - navigator.moveToDoubtDetailFragment(doubtData) - } - - override fun onSignOutClicked() { - - } - - override fun onSubmitFeedbackClicked() { - } - - override fun onDeleteAccountClicked() { - } + if (!::adapter.isInitialized) { + adapter = + GenericFeedAdapter(genericFeedEntities = viewModel.homeEntities.toMutableList(), + onLastItemReached = { + viewModel.fetchDoubts(feedTag = tag) + }, + interactionListener = object : GenericFeedAdapter.InteractionListener { + + override fun onDoubtClicked(doubtData: DoubtData, position: Int) { + // note that this we are not sending a copy of doubtData here, + // hence if netVotes are changed on the other screen then it will change here too. + // this solves our problem but can cause complications on long term. + navigator.moveToDoubtDetailFragment(doubtData) + } - override fun onCreatePollClicked() { - navigator.moveToCreatePollFragment() - } + override fun onSignOutClicked() { - override fun onPollOptionClicked(position: Int, option: String) { + } - } + override fun onSubmitFeedbackClicked() { + } + override fun onDeleteAccountClicked() { + } + }) + } - }) // how is rv restoring its scroll pos when switching tabs? binding.doubtsRecyclerView.adapter = adapter @@ -146,12 +134,14 @@ class ViewDoubtsFragment : Fragment() { binding.layoutSwipe.isRefreshing = false result?.let { when (result) { - is Resource.Success -> { + is Resource.Success<*> -> { result.data?.let { adapter.appendDoubts(it) + } } - is Resource.Error -> { + + is Resource.Error<*> -> { when (result.error) { is UserNotFoundException -> { if (!isAdded) return@observe @@ -160,6 +150,7 @@ class ViewDoubtsFragment : Fragment() { LoginUtilsImpl.logOutUser(analyticsTracker, requireActivity()) result.message?.let { it1 -> showToast(it1) } } + else -> { result.message?.let { showToast(it) @@ -167,7 +158,8 @@ class ViewDoubtsFragment : Fragment() { } } } - is Resource.Loading -> { + + is Resource.Loading<*> -> { } } } @@ -187,8 +179,7 @@ class ViewDoubtsFragment : Fragment() { private fun getViewModel(): ViewDoubtsViewModel { return ViewModelProvider( - owner = this, - factory = ViewDoubtsViewModel.Companion.Factory( + owner = this, factory = ViewDoubtsViewModel.Companion.Factory( fetchHomeFeedUseCase = DoubtlessApp.getInstance().getAppCompRoot() .getFetchHomeFeedUseCase(feedConfig), analyticsTracker = analyticsTracker, diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 00ebb07..15181a9 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -1,6 +1,10 @@ package com.doubtless.doubtless.screens.doubt.view -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker @@ -13,6 +17,7 @@ import com.doubtless.doubtless.screens.home.usecases.FetchHomeFeedUseCase.Result import com.doubtless.doubtless.utils.Resource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlin.collections.set class ViewDoubtsViewModel constructor( private val fetchHomeFeedUseCase: FetchHomeFeedUseCase, @@ -35,76 +40,74 @@ class ViewDoubtsViewModel constructor( _fetchedHomeEntities.value = Resource.Success(data = null) } - fun fetchDoubts(forPageOne: Boolean = false) = viewModelScope.launch(Dispatchers.IO) { + fun fetchDoubts(forPageOne: Boolean = false, feedTag: String) = + viewModelScope.launch(Dispatchers.IO) { - if (isLoading) return@launch + if (isLoading) return@launch - isLoading = true + isLoading = true - val currentUser = userManager.getCachedUserData() ?: userManager.getLoggedInUser() - currentUser?.let { user -> - val result = fetchHomeFeedUseCase.fetchFeedSync( - request = FetchHomeFeedRequest( - user = user, - fetchFromPage1 = forPageOne + val currentUser = userManager.getCachedUserData() ?: userManager.getLoggedInUser() + currentUser?.let { user -> + val result = fetchHomeFeedUseCase.fetchFeedSync( + request = FetchHomeFeedRequest( + user = user, + fetchFromPage1 = forPageOne, + tag = feedTag + ) ) - ) - if (result is Result.ListEnded || result is Result.Error) { - // ERROR CASE - _fetchedHomeEntities.postValue(Resource.Error()) - isLoading = false - return@launch - } + if (result is Result.ListEnded || result is Result.Error) { + // ERROR CASE + _fetchedHomeEntities.postValue(Resource.Error()) + isLoading = false + return@launch + } - result as FetchHomeFeedUseCase.Result.Success + result as Result.Success - if (!forPageOne) { - analyticsTracker.trackFeedNextPage(homeEntities.size) - } else { - analyticsTracker.trackFeedRefresh() - } + if (!forPageOne) { + analyticsTracker.trackFeedNextPage(homeEntities.size) + } else { + analyticsTracker.trackFeedRefresh() + } - val entitiesFromServer = mutableListOf() + val entitiesFromServer = mutableListOf() - result.data.forEach { doubtData -> + result.data.forEach { doubtData -> - // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. - if (_homeEntitiesIds.contains(doubtData.id) == false) { - entitiesFromServer.add(doubtData.toHomeEntity()) - _homeEntitiesIds[doubtData.id!!] = 1 + // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. + if (_homeEntitiesIds.contains(doubtData.id) == false) { + entitiesFromServer.add(doubtData.toHomeEntity()) + _homeEntitiesIds[doubtData.id!!] = 1 + } } - } - // for page 1 call add search entity - if (_homeEntities.isEmpty()) - entitiesFromServer.add(0, FeedEntity.getSearchEntity()) - - _homeEntities.addAll(entitiesFromServer) - _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) - fetchHomeFeedUseCase.notifyDistinctDocsFetched( - docsFetched = homeEntities.size - - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 - ) - isLoading = false - } ?: kotlin.run { - // current user is null - // ERROR CASE - _fetchedHomeEntities.postValue( - Resource.Error( - message = DoubtlessApp.getInstance().getString(R.string.sign_in_again), - data = null, - error = UserNotFoundException() + _homeEntities.addAll(entitiesFromServer) + _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) + fetchHomeFeedUseCase.notifyDistinctDocsFetched( + docsFetched = homeEntities.size + - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 ) - ) - isLoading = false + isLoading = false + } ?: kotlin.run { + // current user is null + // ERROR CASE + _fetchedHomeEntities.postValue( + Resource.Error( + message = DoubtlessApp.getInstance().getString(R.string.sign_in_again), + data = null, + error = UserNotFoundException() + ) + ) + isLoading = false + } } - } - fun refreshList() { + fun refreshList(tag: String?) { _homeEntities.clear() _homeEntitiesIds.clear() - fetchDoubts(forPageOne = true) + fetchDoubts(forPageOne = true, feedTag = tag!!) } companion object { @@ -115,7 +118,11 @@ class ViewDoubtsViewModel constructor( ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return ViewDoubtsViewModel(fetchHomeFeedUseCase, analyticsTracker, userManager) as T + return ViewDoubtsViewModel( + fetchHomeFeedUseCase, + analyticsTracker, + userManager + ) as T } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/HomeFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/HomeFragment.kt index f1fc215..c2db25f 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/HomeFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/HomeFragment.kt @@ -12,7 +12,7 @@ import com.doubtless.doubtless.R import com.doubtless.doubtless.databinding.FragmentHomeBinding import com.doubtless.doubtless.navigation.FragNavigator import com.doubtless.doubtless.navigation.OnBackPressListener -import com.doubtless.doubtless.screens.doubt.view.ViewDoubtsFragment +import com.doubtless.doubtless.screens.doubt.HomeMainScreenFragment import com.doubtless.doubtless.screens.main.MainActivity class HomeFragment : Fragment() { @@ -21,6 +21,7 @@ class HomeFragment : Fragment() { private lateinit var navigator: FragNavigator private var _binding: FragmentHomeBinding? = null + // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! @@ -30,7 +31,7 @@ class HomeFragment : Fragment() { if (savedInstanceState == null) { childFragmentManager.commit { - replace(R.id.bottomNav_child_container, ViewDoubtsFragment()) + replace(R.id.bottomNav_child_container, HomeMainScreenFragment()) } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index a9bf04c..01a6633 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -6,26 +6,13 @@ import com.doubtless.doubtless.screens.search.SearchResult data class FeedEntity( val type: Int, val doubt: DoubtData? = null, - val search_doubt: SearchResult? = null, - val pollOptions: List? = null + val search_doubt: SearchResult? = null ) { companion object { const val TYPE_DOUBT = 1 - const val TYPE_SEARCH = 2 - const val TYPE_SEARCH_RESULT = 3 - const val TYPE_USER_PROFILE = 4 - const val TYPE_BUTTONS = 5 - const val TYPE_POLL = 6 + const val TYPE_SEARCH_RESULT = 2 + const val TYPE_USER_PROFILE = 3 - fun getSearchEntity(): FeedEntity { - return FeedEntity(TYPE_SEARCH, null, null) - } - fun getOptionButtons(): FeedEntity{ - return FeedEntity(TYPE_BUTTONS, null, null) - } - fun getPollEntity(pollOptions: List): FeedEntity { - return FeedEntity(TYPE_POLL, null, null, pollOptions) - } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt index 1f57813..4f9480b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt @@ -2,7 +2,6 @@ package com.doubtless.doubtless.screens.home.usecases import com.doubtless.doubtless.constants.FirestoreCollection import com.doubtless.doubtless.screens.doubt.DoubtData -import com.google.firebase.firestore.FieldPath import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import com.google.firebase.firestore.QuerySnapshot @@ -25,8 +24,25 @@ class FetchFeedByDateUseCase constructor( withContext(Dispatchers.IO) { try { - var query = firestore.collection(FirestoreCollection.AllDoubts) - .orderBy("created_on", Query.Direction.DESCENDING) + + var query = when (request.tag) { + FirestoreCollection.TAG_ALL -> { + firestore.collection(FirestoreCollection.AllDoubts) + .orderBy("created_on", Query.Direction.DESCENDING) + } + + FirestoreCollection.TAG_MY_COLLEGE -> { + firestore.collection(FirestoreCollection.AllDoubts) + .whereEqualTo("author_college", request.user.local_user_attr?.college) + .orderBy("created_on", Query.Direction.DESCENDING) + } + + else -> { + firestore.collection(FirestoreCollection.AllDoubts) + .whereArrayContains("tags", request.tag) + .orderBy("created_on", Query.Direction.DESCENDING) + } + } if (lastDoubtData != null && request.fetchFromPage1 == false) { query = query.startAfter(lastDoubtData!!.date) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt index cc2fbe6..a375e89 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt @@ -2,7 +2,6 @@ package com.doubtless.doubtless.screens.home.usecases import com.doubtless.doubtless.constants.FirestoreCollection import com.doubtless.doubtless.screens.doubt.DoubtData -import com.google.firebase.firestore.FieldPath import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import com.google.firebase.firestore.QuerySnapshot @@ -25,8 +24,25 @@ class FetchFeedByPopularityUseCase constructor( withContext(Dispatchers.IO) { try { - var query = firestore.collection(FirestoreCollection.AllDoubts) - .orderBy("net_votes", Query.Direction.DESCENDING) + var query = when (request.tag) { + FirestoreCollection.TAG_ALL -> { + firestore.collection(FirestoreCollection.AllDoubts) + .orderBy("net_votes", Query.Direction.DESCENDING) + } + + FirestoreCollection.TAG_MY_COLLEGE -> { + firestore.collection(FirestoreCollection.AllDoubts) + .whereEqualTo("author_college", request.user.local_user_attr?.college) + .orderBy("net_votes", Query.Direction.DESCENDING) + } + + else -> { + firestore.collection(FirestoreCollection.AllDoubts) + .whereArrayContains("tags", request.tag) + .orderBy("net_votes", Query.Direction.DESCENDING) + } + } + if (lastDoubtData != null && request.fetchFromPage1 == false) { query = query.startAfter(lastDoubtData!!.date) @@ -39,8 +55,7 @@ class FetchFeedByPopularityUseCase constructor( val result = query.get().await() val doubtDataList = getDoubtDataList(result) - if (doubtDataList.isNotEmpty()) - lastDoubtData = doubtDataList.last() + if (doubtDataList.isNotEmpty()) lastDoubtData = doubtDataList.last() return@withContext Result.Success(doubtDataList) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchHomeFeedUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchHomeFeedUseCase.kt index f5905d0..6859705 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchHomeFeedUseCase.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchHomeFeedUseCase.kt @@ -1,16 +1,17 @@ package com.doubtless.doubtless.screens.home.usecases +import android.os.UserManager import android.util.Log import androidx.annotation.WorkerThread import com.doubtless.doubtless.constants.FirestoreCollection import com.doubtless.doubtless.screens.auth.User import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.home.entities.FeedConfig -import com.google.firebase.firestore.* +import com.google.firebase.firestore.AggregateSource +import com.google.firebase.firestore.FirebaseFirestore import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.withContext -import java.lang.Math.abs import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -24,7 +25,10 @@ class FetchHomeFeedUseCase constructor( data class FetchHomeFeedRequest( val user: User, val pageSize: Int = 10, - val fetchFromPage1: Boolean = false + val fetchFromPage1: Boolean = false, + val tag: String + + ) sealed class Result { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 8ac0b9c..39d26cc 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -19,7 +19,11 @@ import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.main.MainActivity import com.doubtless.doubtless.screens.search.usecases.FetchSearchResultsUseCase import com.doubtless.doubtless.utils.hideSoftKeyboard -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class SearchFragment : Fragment() { @@ -41,9 +45,7 @@ class SearchFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSearchBinding.inflate(inflater) @@ -54,26 +56,20 @@ class SearchFragment : Fragment() { } if (!::adapter.isInitialized) { - adapter = - GenericFeedAdapter( - genericFeedEntities = mutableListOf(), - onLastItemReached = {}, - interactionListener = object : GenericFeedAdapter.InteractionListener { - override fun onSearchBarClicked() {} - - override fun onDoubtClicked(doubtData: DoubtData, position: Int) { - analyticsTracker.trackSearchedDoubtClicked(doubtData.copy()) - navigator.moveToDoubtDetailFragment(doubtData) - } + adapter = GenericFeedAdapter(genericFeedEntities = mutableListOf(), + onLastItemReached = {}, + interactionListener = object : GenericFeedAdapter.InteractionListener { + override fun onDoubtClicked(doubtData: DoubtData, position: Int) { + analyticsTracker.trackSearchedDoubtClicked(doubtData.copy()) + navigator.moveToDoubtDetailFragment(doubtData) + } - override fun onSignOutClicked() {} + override fun onSignOutClicked() {} - override fun onSubmitFeedbackClicked() {} + override fun onSubmitFeedbackClicked() {} - override fun onDeleteAccountClicked() {} - override fun onCreatePollClicked() {} - override fun onPollOptionClicked(position: Int, option: String) {} - }) + override fun onDeleteAccountClicked() {} + }) } binding.rvSearchResults.adapter = adapter @@ -89,7 +85,7 @@ class SearchFragment : Fragment() { binding.progressSearch.visibility = View.VISIBLE - if (it.toString().length <= 1){ + if (it.toString().length <= 1) { delay(1000L) binding.progressSearch.visibility = View.GONE return@launch @@ -111,10 +107,9 @@ class SearchFragment : Fragment() { Toast.makeText(requireContext(), results.message, Toast.LENGTH_SHORT).show() return@launch } - adapter.appendDoubts((results as FetchSearchResultsUseCase.Result.Success) - .searchResult.map { - it.toGenericEntity() - }) + adapter.appendDoubts((results as FetchSearchResultsUseCase.Result.Success).searchResult.map { + it.toGenericEntity() + }) binding.progressSearch.visibility = View.GONE requireView().hideSoftKeyboard() binding.etSearch.clearFocus() @@ -134,10 +129,9 @@ class SearchFragment : Fragment() { } adapter.clearCurrentList() - adapter.appendDoubts((results as FetchSearchResultsUseCase.Result.Success) - .searchResult.map { - it.toGenericEntity() - }) + adapter.appendDoubts((results as FetchSearchResultsUseCase.Result.Success).searchResult.map { + it.toGenericEntity() + }) } } diff --git a/app/src/main/res/layout/fragment_home_main_screen.xml b/app/src/main/res/layout/fragment_home_main_screen.xml new file mode 100644 index 0000000..4adcbe0 --- /dev/null +++ b/app/src/main/res/layout/fragment_home_main_screen.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml new file mode 100644 index 0000000..2028772 --- /dev/null +++ b/app/src/main/res/values/style.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file From 67bbafb39901faa705b26cfeda01cb2a941f7ba0 Mon Sep 17 00:00:00 2001 From: Siddharth sharma Date: Thu, 29 Jun 2023 15:08:49 +0530 Subject: [PATCH 21/35] development 0.1.4 merge fixes (#83) * added margin * home screen item repeating fix --------- Co-authored-by: sidsharma2002 --- app/build.gradle | 4 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 49 +++++------ .../screens/doubt/view/ViewDoubtsViewModel.kt | 88 ++++++++++--------- app/src/main/res/layout/answer_layout.xml | 1 + 4 files changed, 70 insertions(+), 72 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 904fcb9..66ddf84 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.doubtless.doubtless" minSdk 28 targetSdk 33 - versionCode 6 - versionName "0.1.3-inapp" + versionCode 7 + versionName "0.1.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 92c96a0..f93c1e8 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -86,7 +86,6 @@ class ViewDoubtsFragment : Fragment() { binding.layoutSwipe.setOnRefreshListener { if (System.currentTimeMillis() - lastRefreshed < feedConfig.feedDebounce) { - Log.d("feed debounced", feedConfig.feedDebounce.toString()) binding.layoutSwipe.isRefreshing = false return@setOnRefreshListener } @@ -130,40 +129,36 @@ class ViewDoubtsFragment : Fragment() { binding.doubtsRecyclerView.layoutManager = LinearLayoutManager(context) viewModel.fetchedHomeEntities.observe(viewLifecycleOwner) { result -> + binding.llProgressBar.visibility = View.GONE //hide progress bar binding.layoutSwipe.isRefreshing = false - result?.let { - when (result) { - is Resource.Success<*> -> { - result.data?.let { - adapter.appendDoubts(it) - } - } + if (result == null) return@observe - is Resource.Error<*> -> { - when (result.error) { - is UserNotFoundException -> { - if (!isAdded) return@observe - FirebaseCrashlytics.getInstance() - .recordException(Exception("current user is null")) - LoginUtilsImpl.logOutUser(analyticsTracker, requireActivity()) - result.message?.let { it1 -> showToast(it1) } - } - - else -> { - result.message?.let { - showToast(it) - } - } - } - } + when (result) { + + is Resource.Success<*> -> { + if (result.data == null) return@observe + adapter.appendDoubts(result.data) + viewModel.notifyFetchedDoubtsConsumed() + } - is Resource.Loading<*> -> { + is Resource.Error<*> -> { + + val message = result.message ?: "some error occurred!" + + if (result.error is UserNotFoundException) { + FirebaseCrashlytics.getInstance() + .recordException(Exception("current user is null")) + + LoginUtilsImpl.logOutUser(analyticsTracker, requireActivity()) } + + showToast(message) } - } + is Resource.Loading<*> -> {} + } } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 15181a9..a7d844f 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -48,50 +48,8 @@ class ViewDoubtsViewModel constructor( isLoading = true val currentUser = userManager.getCachedUserData() ?: userManager.getLoggedInUser() - currentUser?.let { user -> - val result = fetchHomeFeedUseCase.fetchFeedSync( - request = FetchHomeFeedRequest( - user = user, - fetchFromPage1 = forPageOne, - tag = feedTag - ) - ) - - if (result is Result.ListEnded || result is Result.Error) { - // ERROR CASE - _fetchedHomeEntities.postValue(Resource.Error()) - isLoading = false - return@launch - } - - result as Result.Success - - if (!forPageOne) { - analyticsTracker.trackFeedNextPage(homeEntities.size) - } else { - analyticsTracker.trackFeedRefresh() - } - - val entitiesFromServer = mutableListOf() - result.data.forEach { doubtData -> - - // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. - if (_homeEntitiesIds.contains(doubtData.id) == false) { - entitiesFromServer.add(doubtData.toHomeEntity()) - _homeEntitiesIds[doubtData.id!!] = 1 - } - } - - _homeEntities.addAll(entitiesFromServer) - _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) - fetchHomeFeedUseCase.notifyDistinctDocsFetched( - docsFetched = homeEntities.size - - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 - ) - isLoading = false - } ?: kotlin.run { - // current user is null + if (currentUser == null) { // ERROR CASE _fetchedHomeEntities.postValue( Resource.Error( @@ -101,7 +59,51 @@ class ViewDoubtsViewModel constructor( ) ) isLoading = false + + return@launch + } + + val result = fetchHomeFeedUseCase.fetchFeedSync( + request = FetchHomeFeedRequest( + user = currentUser, + fetchFromPage1 = forPageOne, + tag = feedTag + ) + ) + + if (result is Result.ListEnded || result is Result.Error) { + // ERROR CASE + _fetchedHomeEntities.postValue(Resource.Error()) + isLoading = false + return@launch + } + + result as Result.Success + + if (!forPageOne) { + analyticsTracker.trackFeedNextPage(homeEntities.size) + } else { + analyticsTracker.trackFeedRefresh() } + + val entitiesFromServer = mutableListOf() + + result.data.forEach { doubtData -> + + // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. + if (_homeEntitiesIds.contains(doubtData.id) == false) { + entitiesFromServer.add(doubtData.toHomeEntity()) + _homeEntitiesIds[doubtData.id!!] = 1 + } + } + + _homeEntities.addAll(entitiesFromServer) + _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) + fetchHomeFeedUseCase.notifyDistinctDocsFetched( + docsFetched = homeEntities.size + ) + + isLoading = false } fun refreshList(tag: String?) { diff --git a/app/src/main/res/layout/answer_layout.xml b/app/src/main/res/layout/answer_layout.xml index fa0a675..7adcd1a 100644 --- a/app/src/main/res/layout/answer_layout.xml +++ b/app/src/main/res/layout/answer_layout.xml @@ -106,6 +106,7 @@ android:layout_height="24dp" android:background="@drawable/upvote_icon" android:backgroundTint="@color/grey" + android:layout_marginTop="12dp" android:padding="6dp" android:button="@null" android:checked="false" From 0422c052097a4d44e920d0243e83e24f4e4d69ab Mon Sep 17 00:00:00 2001 From: Siddharth sharma Date: Thu, 29 Jun 2023 16:57:48 +0530 Subject: [PATCH 22/35] setted up year text (#85) Co-authored-by: sidsharma2002 --- .../answers/viewholder/AnswerViewHolder.kt | 15 ++++++++++++++- .../view/viewholder/DoubtPreviewViewHolder.kt | 6 +++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt index fbfb409..51560fc 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt @@ -35,6 +35,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact private val tvVotes: TextView private val upVote: CheckBox private val downVote: CheckBox + private val tvCollege: TextView init { authorName = itemView.findViewById(R.id.tv_author_name) @@ -45,6 +46,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact tvVotes = itemView.findViewById(R.id.tv_votes) upVote = itemView.findViewById(R.id.cb_upvote) downVote = itemView.findViewById(R.id.cb_downvote) + tvCollege = itemView.findViewById(R.id.tv_college) } fun setData(answerData: AnswerData, answerVotingUseCase: VotingUseCase) { @@ -62,8 +64,14 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact } description.text = answerData.description - tvYear.text = "| ${answerData.authorYear} Year |" + if (answerData.authorYear.equals("passout", ignoreCase = true)) { + tvYear.text = "| ${answerData.authorYear} |" + } else { + tvYear.text = "| ${answerData.authorYear} Year |" + } + + tvCollege.text = answerData.authorCollege setVotesUi(answerData, answerVotingUseCase) upVote.setOnClickListener { @@ -107,16 +115,21 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact private fun setVotesUi(answerData: AnswerData, votingUseCase: VotingUseCase) { tvVotes.text = floor(answerData.netVotes).toInt().toString() + CoroutineScope(Dispatchers.Main).launch { + when (votingUseCase.getUserCurrentState()) { + VotingUseCase.UPVOTED -> { downVote.isClickable = false upVote.isChecked = true } + VotingUseCase.DOWNVOTED -> { upVote.isClickable = false downVote.isChecked = true } + else -> { downVote.isClickable = true upVote.isClickable = true diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index 682ffa5..f2563ff 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -90,7 +90,11 @@ class DoubtPreviewViewHolder( tvNetVotes.text = kotlin.math.floor(doubtData.netVotes).toInt().toString() - tvYear.text = "| ${doubtData.year} Year |" + if (doubtData.year.equals("passout", ignoreCase = true)) { + tvYear.text = "| ${doubtData.year} |" + } else { + tvYear.text = "| ${doubtData.year} Year |" + } description.text = doubtData.description?.trim() description.isVisible = !doubtData.description.isNullOrEmpty() From ce6a87174f644bcb8c0026d534518337ff2b1450 Mon Sep 17 00:00:00 2001 From: Aditya <77337791+Aditya13s@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:29:56 +0530 Subject: [PATCH 23/35] change tag analytics name (#84) --- .../java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt index 40e2330..d9f9066 100644 --- a/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt +++ b/app/src/main/java/com/doubtless/doubtless/analytics/AnalyticsTracker.kt @@ -16,7 +16,7 @@ class AnalyticsTracker constructor( map["tag"] = tag - amplitude.track("tags", map) + amplitude.track("tag_feed_viewed", map) } From ec98ead52bfe60376212837aae0b2ca18e3359e9 Mon Sep 17 00:00:00 2001 From: Aditya <77337791+Aditya13s@users.noreply.github.com> Date: Sat, 1 Jul 2023 16:46:40 +0530 Subject: [PATCH 24/35] Badge Feature (#86) * Added Badge Feature * Added xpCount in the answer * Fixed Badges Problem * Code Reviews --- .../constants/GamificationConstants.kt | 5 +++ .../doubtless/screens/answers/AnswerData.kt | 30 ++++++++++----- .../screens/answers/AnswersFragment.kt | 12 +++--- .../answers/viewholder/AnswerViewHolder.kt | 5 ++- .../doubtless/doubtless/screens/auth/User.kt | 4 ++ .../screens/auth/usecases/UserManager.kt | 18 ++++++--- .../doubtless/screens/doubt/DoubtData.kt | 12 ++++-- .../screens/doubt/HomeMainScreenFragment.kt | 2 +- .../doubt/create/CreateDoubtFragment.kt | 38 +++++++------------ .../doubt/view/HomeMainScreenViewModel.kt | 7 ++-- .../screens/doubt/view/ViewDoubtsFragment.kt | 1 - .../view/viewholder/DoubtPreviewViewHolder.kt | 9 +++-- app/src/main/res/drawable/ic_verified.xml | 5 +++ app/src/main/res/layout/answer_layout.xml | 22 ++++++++--- app/src/main/res/layout/doubt_layout.xml | 30 +++++++-------- 15 files changed, 122 insertions(+), 78 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/constants/GamificationConstants.kt create mode 100644 app/src/main/res/drawable/ic_verified.xml diff --git a/app/src/main/java/com/doubtless/doubtless/constants/GamificationConstants.kt b/app/src/main/java/com/doubtless/doubtless/constants/GamificationConstants.kt new file mode 100644 index 0000000..156840d --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/constants/GamificationConstants.kt @@ -0,0 +1,5 @@ +package com.doubtless.doubtless.constants + +object GamificationConstants { + val MENTOR_XP_THRESHOLD = 1000 +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt index 9dd19e1..c1c2576 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswerData.kt @@ -1,11 +1,7 @@ package com.doubtless.doubtless.screens.answers -import android.os.Handler -import android.os.Looper -import android.widget.Toast import com.doubtless.doubtless.DoubtlessApp import com.google.errorprone.annotations.Keep -import com.google.firebase.firestore.DocumentSnapshot import com.google.firebase.firestore.PropertyName import com.google.firebase.firestore.QuerySnapshot import com.google.firebase.firestore.ServerTimestamp @@ -56,6 +52,10 @@ data class AnswerData( @get:PropertyName("created_on") @set:PropertyName("created_on") var date: Date? = null, + @SerializedName("xp_count") + @get:PropertyName("xp_count") + @set:PropertyName("xp_count") + var xpCount: Long? = 0, ) { companion object { @@ -77,7 +77,8 @@ data class AnswerData( authorYear = answerSnapshot.getField("author_year"), description = answerSnapshot.getField("description"), netVotes = (answerSnapshot.getField("net_votes") as Float?) ?: 0f, - date = answerSnapshot.getField("created_on") + date = answerSnapshot.getField("created_on"), + xpCount = (answerSnapshot.getField("xp_count") as Long?) ?: 0 ) ) } catch (e: Exception) { @@ -96,7 +97,8 @@ data class AnswerData( type = AnswerDoubtEntity.TYPE_ANSWER, doubt = null, answer = answerData, - answerVotingUseCase = DoubtlessApp.getInstance().getAppCompRoot().getAnswerVotingDoubtCase(answerData) + answerVotingUseCase = DoubtlessApp.getInstance().getAppCompRoot() + .getAnswerVotingDoubtCase(answerData) ) } } @@ -117,7 +119,9 @@ data class PublishAnswerRequest( @SerializedName("author_year") var authorYear: String? = null, @SerializedName("description") - var description: String? = null + var description: String? = null, + @SerializedName("xp_count") + var xpCount: Long = 0 ) { companion object { fun toAnswerData( @@ -133,7 +137,8 @@ data class PublishAnswerRequest( authorYear = publishAnswerRequest.authorYear, description = publishAnswerRequest.description, netVotes = 0f, - date = Date() + date = Date(), + xpCount = publishAnswerRequest.xpCount ) } } @@ -174,7 +179,11 @@ data class PublishAnswerResponse( @SerializedName("net_votes") @get:PropertyName("net_votes") @set:PropertyName("net_votes") - var netVotes: Float = 0f + var netVotes: Float = 0f, + @SerializedName("xp_count") + @get:PropertyName("xp_count") + @set:PropertyName("xp_count") + var xpCount: Long = 0 ) { fun toAnswerData(): AnswerData { return AnswerData( @@ -187,7 +196,8 @@ data class PublishAnswerResponse( authorYear = this.authorYear, description = this.description, netVotes = this.netVotes, - date = null + date = null, + xpCount = this.xpCount ) } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt index c2016df..f17fc90 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/AnswersFragment.kt @@ -1,16 +1,15 @@ package com.doubtless.doubtless.screens.answers import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import com.doubtless.doubtless.DoubtlessApp -import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker import com.doubtless.doubtless.databinding.FragmentAnswersBinding import com.doubtless.doubtless.navigation.FragNavigator @@ -18,7 +17,6 @@ import com.doubtless.doubtless.screens.answers.usecases.PublishAnswerUseCase import com.doubtless.doubtless.screens.answers.viewholder.EnterAnswerViewHolder import com.doubtless.doubtless.screens.auth.usecases.UserManager import com.doubtless.doubtless.screens.doubt.DoubtData -import com.doubtless.doubtless.screens.doubt.view.ViewDoubtsViewModel import com.doubtless.doubtless.screens.main.MainActivity import com.doubtless.doubtless.screens.main.MainFragment @@ -56,7 +54,7 @@ class AnswersFragment : Fragment() { // encapsulate this logic if (currentSelectedFrag is MainFragment.CurrentSelectedBottomNavFrag.HomeFrag) { - _navigator = DoubtlessApp.getInstance().getAppCompRoot() + _navigator = DoubtlessApp.getInstance().getAppCompRoot() .getHomeFragNavigator(requireActivity() as MainActivity) } @@ -120,7 +118,8 @@ class AnswersFragment : Fragment() { authorPhotoUrl = userManager.getCachedUserData()!!.photoUrl, authorCollege = userManager.getCachedUserData()!!.local_user_attr!!.college, authorYear = userManager.getCachedUserData()!!.local_user_attr!!.year, - description = publishAnswerDTO.description + description = publishAnswerDTO.description, + xpCount = userManager.getCachedUserData()!!.xpCount ?: 0 ) ) } @@ -162,7 +161,8 @@ class AnswersFragment : Fragment() { binding.progressPostAnswer.visibility = View.GONE - Toast.makeText(requireContext(), "Successfully posted!", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), "Successfully posted!", Toast.LENGTH_SHORT) + .show() adapter.appendAnswerAtFirst(answerData = it.answerData) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt index 51560fc..64e1e89 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/answers/viewholder/AnswerViewHolder.kt @@ -8,6 +8,7 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.doubtless.doubtless.R +import com.doubtless.doubtless.constants.GamificationConstants import com.doubtless.doubtless.screens.answers.AnswerData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.utils.Utils @@ -15,7 +16,6 @@ import com.doubtless.doubtless.utils.addStateListAnimation import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.util.* import java.util.Date import kotlin.math.floor @@ -36,6 +36,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact private val upVote: CheckBox private val downVote: CheckBox private val tvCollege: TextView + private val userBadge: ImageView init { authorName = itemView.findViewById(R.id.tv_author_name) @@ -47,6 +48,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact upVote = itemView.findViewById(R.id.cb_upvote) downVote = itemView.findViewById(R.id.cb_downvote) tvCollege = itemView.findViewById(R.id.tv_college) + userBadge = itemView.findViewById(R.id.user_badge) } fun setData(answerData: AnswerData, answerVotingUseCase: VotingUseCase) { @@ -63,6 +65,7 @@ class AnswerViewHolder(itemView: View, private val interactionListener: Interact time.isVisible = false } + userBadge.isVisible = answerData.xpCount!! > GamificationConstants.MENTOR_XP_THRESHOLD description.text = answerData.description if (answerData.authorYear.equals("passout", ignoreCase = true)) { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/User.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/User.kt index 6e04dde..b32f4f7 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/auth/User.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/User.kt @@ -29,6 +29,10 @@ data class User( @get:PropertyName("photoUrl") @set:PropertyName("photoUrl") var photoUrl: String? = null, + @SerializedName("xpCount") + @get:PropertyName("xpCount") + @set:PropertyName("xpCount") + var xpCount: Long = 0, @get:Exclude val document_id: String? = null, @get:Exclude val local_user_attr: UserAttributes? = null ) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/usecases/UserManager.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/usecases/UserManager.kt index be038cf..7f3d6ee 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/auth/usecases/UserManager.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/usecases/UserManager.kt @@ -8,7 +8,7 @@ import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.firebase.auth.FirebaseAuth import com.google.firebase.auth.FirebaseUser -import java.util.* +import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -19,7 +19,12 @@ class UserManager constructor( ) { sealed class Result(user: User? = null, isNewUser: Boolean?, error: String? = null) { - class Success(val user: User, val isNewUser: Boolean, val isOldUserWithNoOnboarding: Boolean) : Result(user, isNewUser) + class Success( + val user: User, + val isNewUser: Boolean, + val isOldUserWithNoOnboarding: Boolean + ) : Result(user, isNewUser) + class Error(val message: String) : Result(null, null, message) class LoggedOut : Result(null, null, null) } @@ -69,7 +74,8 @@ class UserManager constructor( if (result is UserDataServerUseCase.Result.NewUser) { userDataStorageUseCase.setUserData(result.newUser) cachedUserData = result.newUser - return Result.Success(result.newUser, + return Result.Success( + result.newUser, isNewUser = true, isOldUserWithNoOnboarding = false ) @@ -78,7 +84,8 @@ class UserManager constructor( if (result is UserDataServerUseCase.Result.OldUseWithNoOnboardingData) { userDataStorageUseCase.setUserData(result.oldUser) cachedUserData = result.oldUser - return Result.Success(result.oldUser, + return Result.Success( + result.oldUser, isNewUser = false, isOldUserWithNoOnboarding = true ) @@ -87,7 +94,8 @@ class UserManager constructor( if (result is UserDataServerUseCase.Result.OldUser) { userDataStorageUseCase.setUserData(result.oldUser) cachedUserData = result.oldUser - return Result.Success(result.oldUser, + return Result.Success( + result.oldUser, isNewUser = false, isOldUserWithNoOnboarding = false ) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt index cd4d8b4..be69a6c 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt @@ -64,8 +64,12 @@ data class DoubtData( @SerializedName("is_trending") @get:PropertyName("is_trending") @set:PropertyName("is_trending") - var isTrending: Boolean = false -): Parcelable { + var isTrending: Boolean = false, + @SerializedName("xp_count") + @get:PropertyName("xp_count") + @set:PropertyName("xp_count") + var xpCount: Long = 0 +) : Parcelable { companion object { fun parse(documentSnapshot: DocumentSnapshot?): DoubtData? { return try { @@ -101,5 +105,7 @@ data class PublishDoubtRequest( @SerializedName("tags") var tags: List? = null, @SerializedName("keywords") - var keywords: List? = null + var keywords: List? = null, + @SerializedName("xp_count") + var xpCount: Long = 0 ) \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt index d4f1415..e9d1637 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt @@ -77,7 +77,7 @@ class HomeMainScreenFragment : Fragment() { }.toMutableList() TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position -> - if (position == 0) { + if (position == 1) { tab.text = "${userManager.getCachedUserData()?.local_user_attr!!.college!!} only" return@TabLayoutMediator } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt index 8f2ba7d..9381330 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/create/CreateDoubtFragment.kt @@ -70,8 +70,7 @@ class CreateDoubtFragment : Fragment() { val backPressConsumed = navigator?.onBackPress() ?: false - return if (backPressConsumed) - true + return if (backPressConsumed) true else { (parentFragment as? MainFragment)?.selectHomeBottomNavElement() true @@ -96,8 +95,7 @@ class CreateDoubtFragment : Fragment() { navigator = appComp.getCreateFragmentNavigator(requireActivity() as MainActivity) viewModel = ViewModelProvider( - this, - CreateDoubtViewModel.Companion.Factory(postDoubtUseCase) + this, CreateDoubtViewModel.Companion.Factory(postDoubtUseCase) )[CreateDoubtViewModel::class.java] } @@ -126,11 +124,8 @@ class CreateDoubtFragment : Fragment() { } else { isButtonClicked = false binding.postButton.alpha = 1f - Toast.makeText( - /* context = */ context, - /* text = */ - "${(result as CreateDoubtViewModel.Result.Error).message}", - /* duration = */ + Toast.makeText(/* context = */ context,/* text = */ + "${(result as CreateDoubtViewModel.Result.Error).message}",/* duration = */ Toast.LENGTH_SHORT ).show() } @@ -253,11 +248,9 @@ class CreateDoubtFragment : Fragment() { val size = getSelectedTags().size - if (size > 3) - return "More than 3 Tags are not allowed!" + if (size > 3) return "More than 3 Tags are not allowed!" - if (size == 0) - return "Please select a tag!" + if (size == 0) return "Please select a tag!" return null } @@ -279,7 +272,8 @@ class CreateDoubtFragment : Fragment() { createDoubt( binding.doubtHeading.text.toString(), binding.doubtDescription.text.toString(), - tags, keywords, + tags, + keywords, userManager.getCachedUserData()!! ) dialogInterface.dismiss() @@ -297,8 +291,7 @@ class CreateDoubtFragment : Fragment() { val jsonObject = Gson().fromJson(maxCharLimit, Map::class.java) as Map<*, *> maxHeadingCharLimit = (jsonObject["max_heading_char_limit"] as Double).toInt() - maxDescriptionCharLimit = - (jsonObject["max_description_char_limit"] as Double).toInt() + maxDescriptionCharLimit = (jsonObject["max_description_char_limit"] as Double).toInt() maxKeywordsLimit = (jsonObject["keywords_limit"] as Double).toInt() setMaxCharacterLimit(maxHeadingCharLimit, maxDescriptionCharLimit, maxKeywordsLimit) @@ -309,9 +302,7 @@ class CreateDoubtFragment : Fragment() { } private fun setMaxCharacterLimit( - maxHeadingCharLimit: Int, - maxDescriptionCharLimit: Int, - maxKeywordsLimit: Int + maxHeadingCharLimit: Int, maxDescriptionCharLimit: Int, maxKeywordsLimit: Int ) { binding.doubtHeading.filters = arrayOf(InputFilter.LengthFilter(maxHeadingCharLimit)) @@ -320,11 +311,7 @@ class CreateDoubtFragment : Fragment() { } private fun createDoubt( - heading: String, - description: String, - tags: List, - keywords: List, - user: User + heading: String, description: String, tags: List, keywords: List, user: User ) { viewModel.postDoubt( PublishDoubtRequest( @@ -337,7 +324,8 @@ class CreateDoubtFragment : Fragment() { description = description, netVotes = 0f, tags = tags, - keywords = keywords + keywords = keywords, + xpCount = user.xpCount ) ) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt index 0430bdc..7e15713 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/HomeMainScreenViewModel.kt @@ -17,12 +17,14 @@ class HomeMainScreenViewModel constructor( private val _tags = MutableLiveData>() val tags: LiveData> = _tags + private val startTags = listOf(FirestoreCollection.TAG_ALL, FirestoreCollection.TAG_MY_COLLEGE) + fun fetchTags() = viewModelScope.launch(Dispatchers.IO) { val result = fetchFilterTagsUseCase.fetchTagsFromFirebase() if (result is FetchFilterTagsUseCase.Result.Error) { - _tags.postValue(listOf(FirestoreCollection.TAG_MY_COLLEGE, FirestoreCollection.TAG_ALL)) + _tags.postValue(startTags) return@launch } @@ -30,8 +32,7 @@ class HomeMainScreenViewModel constructor( val tags = result.data.toMutableList() tags.addAll( - 0, - listOf(FirestoreCollection.TAG_MY_COLLEGE, FirestoreCollection.TAG_ALL) + 0, startTags ) _tags.postValue(tags) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index f93c1e8..1e3ebae 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -1,7 +1,6 @@ package com.doubtless.doubtless.screens.doubt.view import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index f2563ff..719a38e 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R +import com.doubtless.doubtless.constants.GamificationConstants import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.utils.Utils @@ -18,7 +19,7 @@ import com.doubtless.doubtless.utils.addStateListAnimation import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.util.* +import java.util.Date import kotlin.math.floor class DoubtPreviewViewHolder( @@ -44,7 +45,7 @@ class DoubtPreviewViewHolder( private val tvTags: TextView private val tvCollege: TextView private val tvYear: TextView = itemView.findViewById(R.id.user_year) - private val icFire: ImageView = itemView.findViewById(R.id.ic_fire) + private val userBadge: ImageView = itemView.findViewById(R.id.user_badge) private val analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() @@ -109,7 +110,7 @@ class DoubtPreviewViewHolder( Glide.with(ivDp).load(doubtData.userPhotoUrl).circleCrop() .into(ivDp) - icFire.isVisible = doubtData.isTrending + userBadge.isVisible = doubtData.xpCount > GamificationConstants.MENTOR_XP_THRESHOLD val votingUseCase = DoubtlessApp.getInstance().getAppCompRoot() .getDoubtVotingDoubtCase(doubtData.copy()) @@ -162,10 +163,12 @@ class DoubtPreviewViewHolder( downvotes.isClickable = false upvotes.isChecked = true } + VotingUseCase.DOWNVOTED -> { upvotes.isClickable = false downvotes.isChecked = true } + else -> { downvotes.isClickable = true upvotes.isClickable = true diff --git a/app/src/main/res/drawable/ic_verified.xml b/app/src/main/res/drawable/ic_verified.xml new file mode 100644 index 0000000..4bf9fcf --- /dev/null +++ b/app/src/main/res/drawable/ic_verified.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/answer_layout.xml b/app/src/main/res/layout/answer_layout.xml index 7adcd1a..5c94d3c 100644 --- a/app/src/main/res/layout/answer_layout.xml +++ b/app/src/main/res/layout/answer_layout.xml @@ -53,12 +53,12 @@ android:id="@+id/user_year" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="4dp" + android:layout_marginStart="2dp" android:fontFamily="@font/poppins" android:text="| 1st year |" android:textColor="@color/grey" android:textSize="12dp" - app:layout_constraintStart_toEndOf="@id/tv_author_name" + app:layout_constraintStart_toEndOf="@+id/user_badge" app:layout_constraintTop_toTopOf="@id/tv_author_name" /> + + @@ -131,11 +143,11 @@ android:id="@+id/cb_downvote" android:layout_width="24dp" android:layout_height="24dp" + android:layout_marginStart="6dp" android:background="@drawable/upvote_icon" android:backgroundTint="@color/grey" android:button="@null" android:checked="false" - android:layout_marginStart="6dp" android:padding="6dp" android:rotation="180" app:layout_constraintStart_toEndOf="@id/tv_votes" diff --git a/app/src/main/res/layout/doubt_layout.xml b/app/src/main/res/layout/doubt_layout.xml index 6261b58..9dd7409 100644 --- a/app/src/main/res/layout/doubt_layout.xml +++ b/app/src/main/res/layout/doubt_layout.xml @@ -67,12 +67,12 @@ android:id="@+id/user_year" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="4dp" + android:layout_marginStart="2dp" android:fontFamily="@font/poppins" android:text="| 4th year |" android:textColor="@color/grey" android:textSize="12dp" - app:layout_constraintStart_toEndOf="@id/tv_username" + app:layout_constraintStart_toEndOf="@+id/user_badge" app:layout_constraintTop_toTopOf="@id/tv_username" /> @@ -187,27 +188,26 @@ android:id="@+id/cb_upvotes" android:layout_width="24dp" android:layout_height="24dp" + android:layout_marginTop="8dp" android:background="@drawable/upvote_icon" android:backgroundTint="@color/grey" - android:layout_marginTop="8dp" - android:padding="6dp" android:button="@null" android:checked="false" + android:padding="6dp" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/tv_tags" - /> + app:layout_constraintTop_toBottomOf="@id/tv_tags" /> From a11e900e53c2afbb54e85698dbeb320a93712af7 Mon Sep 17 00:00:00 2001 From: Aditya <77337791+Aditya13s@users.noreply.github.com> Date: Tue, 4 Jul 2023 21:29:58 +0530 Subject: [PATCH 25/35] fixed feed refresh issue (#87) --- .../doubtless/screens/doubt/view/ViewDoubtsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 1e3ebae..84bad98 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -73,7 +73,7 @@ class ViewDoubtsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - if (viewModel.fetchedHomeEntities.value?.data.isNullOrEmpty()) { + if (viewModel.homeEntities.isEmpty()) { viewModel.fetchDoubts(forPageOne = true, feedTag = tag) } From d1c5b5dd0ce3c50c1a27639d9f0a32af9505df5b Mon Sep 17 00:00:00 2001 From: Siddharth sharma Date: Tue, 11 Jul 2023 13:27:00 +0530 Subject: [PATCH 26/35] UI revamp (#88) * changed doubt + answer layout a but * profile ui impl wip * ui changes * ui finalised --------- Co-authored-by: sidsharma2002 --- app/build.gradle | 4 +- .../doubtless/di/AppCompositionRoot.kt | 14 +- .../doubtless/screens/auth/LoginActivity.kt | 3 + .../screens/common/GenericFeedAdapter.kt | 2 +- .../viewholder/UserProfileViewHolder.kt | 12 + .../doubtless/screens/doubt/DoubtData.kt | 3 +- .../screens/doubt/HomeMainScreenFragment.kt | 3 + .../FetchInteractedMentorDataForDoubt.kt | 36 +++ .../view/viewholder/DoubtPreviewViewHolder.kt | 39 ++- .../doubt/view/viewholder/PollsViewHolder.kt | 41 +++ .../home/usecases/FetchFeedByDateUseCase.kt | 19 +- .../usecases/FetchFeedByPopularityUseCase.kt | 19 +- .../com/doubtless/doubtless/utils/Utils.kt | 6 + app/src/main/res/drawable/bg_rec_rounded.xml | 7 + .../res/drawable/ic_outline_comment_24.xml | 5 + app/src/main/res/drawable/ic_verified.xml | 2 +- app/src/main/res/layout/activity_login.xml | 2 +- app/src/main/res/layout/answer_layout.xml | 22 +- .../main/res/layout/enter_answer_layout.xml | 7 +- app/src/main/res/layout/fragment_answers.xml | 11 +- .../res/layout/fragment_home_main_screen.xml | 19 +- .../main/res/layout/fragment_inapp_notif.xml | 49 ++-- app/src/main/res/layout/layout_polls.xml | 243 ++++++++++++++++++ .../main/res/layout/user_profile_layout.xml | 95 +++++-- app/src/main/res/values/colors.xml | 4 +- app/src/main/res/values/style.xml | 3 +- app/src/main/res/values/themes.xml | 2 +- 27 files changed, 569 insertions(+), 103 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchInteractedMentorDataForDoubt.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/PollsViewHolder.kt create mode 100644 app/src/main/res/drawable/bg_rec_rounded.xml create mode 100644 app/src/main/res/drawable/ic_outline_comment_24.xml create mode 100644 app/src/main/res/layout/layout_polls.xml diff --git a/app/build.gradle b/app/build.gradle index 66ddf84..3ec411b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.doubtless.doubtless" minSdk 28 targetSdk 33 - versionCode 7 - versionName "0.1.4" + versionCode 8 + versionName "0.1.5-ui-revamp" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug diff --git a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt index 5ac4ac5..d932728 100644 --- a/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt +++ b/app/src/main/java/com/doubtless/doubtless/di/AppCompositionRoot.kt @@ -24,11 +24,7 @@ import com.doubtless.doubtless.screens.dashboard.usecases.DeleteAccountUseCase import com.doubtless.doubtless.screens.dashboard.usecases.FetchUserDataUseCase import com.doubtless.doubtless.screens.dashboard.usecases.FetchUserFeedByDateUseCase import com.doubtless.doubtless.screens.doubt.DoubtData -import com.doubtless.doubtless.screens.doubt.usecases.DoubtDataSharedPrefUseCase -import com.doubtless.doubtless.screens.doubt.usecases.FetchDoubtDataFromDoubtIdUseCase -import com.doubtless.doubtless.screens.doubt.usecases.FetchFilterTagsUseCase -import com.doubtless.doubtless.screens.doubt.usecases.PostDoubtUseCase -import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase +import com.doubtless.doubtless.screens.doubt.usecases.* import com.doubtless.doubtless.screens.home.entities.FeedConfig import com.doubtless.doubtless.screens.home.usecases.FetchFeedByDateUseCase import com.doubtless.doubtless.screens.home.usecases.FetchFeedByPopularityUseCase @@ -91,11 +87,15 @@ class AppCompositionRoot(appContext: DoubtlessApp) { } private fun getFetchFeedByPopularityUseCase(): FetchFeedByPopularityUseCase { - return FetchFeedByPopularityUseCase(FirebaseFirestore.getInstance()) + return FetchFeedByPopularityUseCase(getMentorsWhoInteractedUseCase(), FirebaseFirestore.getInstance()) } private fun getFetchFeedByDataUseCase(): FetchFeedByDateUseCase { - return FetchFeedByDateUseCase(FirebaseFirestore.getInstance()) + return FetchFeedByDateUseCase(getMentorsWhoInteractedUseCase(), FirebaseFirestore.getInstance()) + } + + private fun getMentorsWhoInteractedUseCase(): FetchInteractedMentorDataForDoubt { + return FetchInteractedMentorDataForDoubt(FirebaseFirestore.getInstance()) } // --------- Answer Screen ------------ diff --git a/app/src/main/java/com/doubtless/doubtless/screens/auth/LoginActivity.kt b/app/src/main/java/com/doubtless/doubtless/screens/auth/LoginActivity.kt index d15e4bd..ea4de4b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/auth/LoginActivity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/auth/LoginActivity.kt @@ -6,6 +6,7 @@ import android.view.View import android.widget.ImageView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible import com.bumptech.glide.Glide import com.doubtless.doubtless.DoubtlessApp import com.doubtless.doubtless.R @@ -68,6 +69,8 @@ class LoginActivity : AppCompatActivity() { private fun setupUi() { window.statusBarColor = getColor(R.color.purple) + findViewById(R.id.view_sep).isVisible = false + Glide.with(this) .load("https://lh3.googleusercontent.com/a/AGNmyxaceCDqTACCSoa1e3VimHXoAEQ4IBSYbPk8YTU-J5U=s96-c") .circleCrop() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index c9350d2..a4a8495 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -46,7 +46,7 @@ class GenericFeedAdapter( val view = LayoutInflater.from(parent.context) .inflate(R.layout.doubt_layout, parent, false) return DoubtPreviewViewHolder(view = view, - showVotingLayout = false, + showVotingLayout = true, interactionListener = object : DoubtPreviewViewHolder.InteractionListener { override fun onDoubtClicked(doubtData: DoubtData, position: Int) { interactionListener.onDoubtClicked(doubtData, position) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/viewholder/UserProfileViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/viewholder/UserProfileViewHolder.kt index f0d560b..328691c 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/viewholder/UserProfileViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/viewholder/UserProfileViewHolder.kt @@ -8,6 +8,7 @@ import com.bumptech.glide.Glide import com.doubtless.doubtless.R import com.doubtless.doubtless.screens.auth.usecases.UserManager import com.doubtless.doubtless.theming.buttons.SecondaryButton +import com.doubtless.doubtless.utils.Utils.flatten class UserProfileViewHolder(view: View, private val interactionListener: InteractionListener) : RecyclerView.ViewHolder(view) { @@ -24,6 +25,7 @@ class UserProfileViewHolder(view: View, private val interactionListener: Interac private val signOutButton: SecondaryButton private val submitFeedback: TextView private val deleteAccount: TextView + private val tvBio: TextView init { @@ -33,6 +35,7 @@ class UserProfileViewHolder(view: View, private val interactionListener: Interac signOutButton = view.findViewById(R.id.btn_signout) submitFeedback = view.findViewById(R.id.btnFeedback) deleteAccount = view.findViewById(R.id.btn_delete_account) + tvBio = view.findViewById(R.id.tv_bio) } fun setData(userManager: UserManager) { @@ -51,6 +54,15 @@ class UserProfileViewHolder(view: View, private val interactionListener: Interac userName.text = userManager.getCachedUserData()!!.name userEmail.text = userManager.getCachedUserData()!!.email + + var tags = "" + + userManager.getCachedUserData()!!.local_user_attr!!.tags?.forEach { + tags += "#$it " + } + + tvBio.text = tags + Glide.with(userImage).load(userManager.getCachedUserData()!!.photoUrl).circleCrop() .into(userImage) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt index be69a6c..04f2baa 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt @@ -68,7 +68,8 @@ data class DoubtData( @SerializedName("xp_count") @get:PropertyName("xp_count") @set:PropertyName("xp_count") - var xpCount: Long = 0 + var xpCount: Long = 0, + var mentorsDpWhoInteracted: List = mutableListOf() ) : Parcelable { companion object { fun parse(documentSnapshot: DocumentSnapshot?): DoubtData? { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt index e9d1637..6e2aeb7 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/HomeMainScreenFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import com.doubtless.doubtless.DoubtlessApp +import com.doubtless.doubtless.R import com.doubtless.doubtless.analytics.AnalyticsTracker import com.doubtless.doubtless.databinding.FragmentHomeMainScreenBinding import com.doubtless.doubtless.navigation.FragNavigator @@ -30,6 +31,7 @@ class HomeMainScreenFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentHomeMainScreenBinding.inflate(inflater, container, false) + requireActivity().window.statusBarColor = requireContext().getColor(R.color.purple) return binding.root } @@ -88,6 +90,7 @@ class HomeMainScreenFragment : Fragment() { override fun onTabSelected(tab: TabLayout.Tab?) { analyticsTracker.trackTagsFragment(capitalizedTagList[tab!!.position]) } + override fun onTabUnselected(tab: TabLayout.Tab?) {} override fun onTabReselected(tab: TabLayout.Tab?) {} diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchInteractedMentorDataForDoubt.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchInteractedMentorDataForDoubt.kt new file mode 100644 index 0000000..f99724d --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/usecases/FetchInteractedMentorDataForDoubt.kt @@ -0,0 +1,36 @@ +package com.doubtless.doubtless.screens.doubt.usecases + +import com.doubtless.doubtless.constants.FirestoreCollection +import com.doubtless.doubtless.screens.answers.AnswerData +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.Query +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.withContext + +class FetchInteractedMentorDataForDoubt constructor( + private val firestore: FirebaseFirestore +) { + + suspend fun fetch(doubtId: String): List = withContext(Dispatchers.IO) { + return@withContext try { + + val docx = firestore.collection(FirestoreCollection.AllDoubts) + .document(doubtId) + .collection(FirestoreCollection.DoubtAnswer) + .whereGreaterThanOrEqualTo("xp_count", 1000) + .orderBy("xp_count", Query.Direction.DESCENDING) + .limit(5) + .get().await() + + val dps = AnswerData.parse(docx)?.map { + it.authorPhotoUrl ?: "" + } + + dps ?: listOf() + + } catch (e: Exception) { + listOf() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index 719a38e..772e482 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -2,8 +2,10 @@ package com.doubtless.doubtless.screens.doubt.view.viewholder import android.text.util.Linkify import android.view.View +import android.view.ViewGroup import android.widget.CheckBox import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView @@ -15,7 +17,9 @@ import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.usecases.VotingUseCase import com.doubtless.doubtless.utils.Utils import com.doubtless.doubtless.utils.Utils.flatten +import com.doubtless.doubtless.utils.Utils.toPx import com.doubtless.doubtless.utils.addStateListAnimation +import com.google.api.Distribution.BucketOptions.Linear import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -46,6 +50,7 @@ class DoubtPreviewViewHolder( private val tvCollege: TextView private val tvYear: TextView = itemView.findViewById(R.id.user_year) private val userBadge: ImageView = itemView.findViewById(R.id.user_badge) + private val llMentorsDp: LinearLayout = itemView.findViewById(R.id.ll_answered_mentor) private val analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() @@ -103,7 +108,7 @@ class DoubtPreviewViewHolder( tvAnswers.text = doubtData.no_answers.toString() if (!doubtData.tags.isNullOrEmpty()) - tvTags.text = "Related to : " + doubtData.tags!!.flatten() + tvTags.text = "tags : " + doubtData.tags!!.flatten() else tvTags.isVisible = false @@ -112,6 +117,8 @@ class DoubtPreviewViewHolder( userBadge.isVisible = doubtData.xpCount > GamificationConstants.MENTOR_XP_THRESHOLD + setupMentorsWhoInteractedDpUi(doubtData) + val votingUseCase = DoubtlessApp.getInstance().getAppCompRoot() .getDoubtVotingDoubtCase(doubtData.copy()) @@ -155,6 +162,36 @@ class DoubtPreviewViewHolder( } } + private fun setupMentorsWhoInteractedDpUi(doubtData: DoubtData) { + + // reset view state first! + val dpsCount = llMentorsDp.childCount - 3 + + if (dpsCount > 0) { + repeat(dpsCount) { + llMentorsDp.removeView(llMentorsDp.getChildAt(it + 3)) // 3,4,5.. + } + } + + // then setup ui + doubtData.mentorsDpWhoInteracted.forEach { + if (it.isEmpty()) return@forEach + + val view = ImageView(itemView.context) + view.layoutParams = LinearLayout.LayoutParams(22.toPx().toInt(), 22.toPx().toInt()) + .apply { + this.marginStart = 4.toPx().toInt() + } + + Glide.with(itemView.context).load(it).circleCrop().into(view) + + llMentorsDp.addView(view) + } + + llMentorsDp.isVisible = !doubtData.mentorsDpWhoInteracted.isEmpty() + + } + private fun setVotesUi(doubtData: DoubtData, votingUseCase: VotingUseCase) { tvNetVotes.text = floor(doubtData.netVotes).toInt().toString() CoroutineScope(Dispatchers.Main).launch { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/PollsViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/PollsViewHolder.kt new file mode 100644 index 0000000..1117e76 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/PollsViewHolder.kt @@ -0,0 +1,41 @@ +package com.doubtless.doubtless.screens.doubt.view.viewholder + +import android.view.View +import android.view.ViewGroup +import android.widget.CheckBox +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.doubtless.doubtless.R + +class PollsViewHolder constructor( + private val view: View +) : ViewHolder(view) { + + private val userName: TextView + private val time: TextView + private val heading: TextView + private val ivDp: ImageView + private val tvNetVotes: TextView + private val upvotes: CheckBox + private val downvotes: CheckBox + private val tvAnswers: TextView + private val tvCollege: TextView + private val tvYear: TextView = itemView.findViewById(R.id.user_year) + private val userBadge: ImageView = itemView.findViewById(R.id.user_badge) + + init { + userName = view.findViewById(R.id.tv_username) + time = view.findViewById(R.id.author_doubt_timestamp) + heading = view.findViewById(R.id.user_doubt_heading) + ivDp = view.findViewById(R.id.iv_dp) + tvNetVotes = view.findViewById(R.id.tv_votes) + tvAnswers = view.findViewById(R.id.tv_answers) + tvCollege = view.findViewById(R.id.user_college) + upvotes = view.findViewById(R.id.cb_upvotes) + downvotes = view.findViewById(R.id.cb_downvote) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt index 4f9480b..55fe491 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByDateUseCase.kt @@ -2,6 +2,7 @@ package com.doubtless.doubtless.screens.home.usecases import com.doubtless.doubtless.constants.FirestoreCollection import com.doubtless.doubtless.screens.doubt.DoubtData +import com.doubtless.doubtless.screens.doubt.usecases.FetchInteractedMentorDataForDoubt import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import com.google.firebase.firestore.QuerySnapshot @@ -10,6 +11,7 @@ import kotlinx.coroutines.tasks.await import kotlinx.coroutines.withContext class FetchFeedByDateUseCase constructor( + private val fetchInteractedMentorDataForDoubt: FetchInteractedMentorDataForDoubt, private val firestore: FirebaseFirestore ) { @@ -66,17 +68,18 @@ class FetchFeedByDateUseCase constructor( } @kotlin.jvm.Throws(Exception::class) - private fun getDoubtDataList(result: QuerySnapshot?): List { + private suspend fun getDoubtDataList(result: QuerySnapshot?): List = + withContext(Dispatchers.IO) { - val doubtDataList = mutableListOf() + val doubtDataList = mutableListOf() - result!!.documents.forEach { - val doubtData = DoubtData.parse(it) ?: return@forEach - doubtDataList.add(doubtData) - } + result!!.documents.forEach { + val doubtData = DoubtData.parse(it) ?: return@forEach + doubtDataList.add(doubtData) + } - return doubtDataList - } + return@withContext doubtDataList + } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt index a375e89..a239c92 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/usecases/FetchFeedByPopularityUseCase.kt @@ -2,6 +2,7 @@ package com.doubtless.doubtless.screens.home.usecases import com.doubtless.doubtless.constants.FirestoreCollection import com.doubtless.doubtless.screens.doubt.DoubtData +import com.doubtless.doubtless.screens.doubt.usecases.FetchInteractedMentorDataForDoubt import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import com.google.firebase.firestore.QuerySnapshot @@ -10,6 +11,7 @@ import kotlinx.coroutines.tasks.await import kotlinx.coroutines.withContext class FetchFeedByPopularityUseCase constructor( + private val fetchInteractedMentorDataForDoubt: FetchInteractedMentorDataForDoubt, private val firestore: FirebaseFirestore ) { @@ -65,17 +67,18 @@ class FetchFeedByPopularityUseCase constructor( } @kotlin.jvm.Throws(Exception::class) - private fun getDoubtDataList(result: QuerySnapshot?): List { + private suspend fun getDoubtDataList(result: QuerySnapshot?): List = + withContext(Dispatchers.IO) { - val doubtDataList = mutableListOf() + val doubtDataList = mutableListOf() - result!!.documents.forEach { - val doubtData = DoubtData.parse(it) ?: return@forEach - doubtDataList.add(doubtData) - } + result!!.documents.forEach { + val doubtData = DoubtData.parse(it) ?: return@forEach + doubtDataList.add(doubtData) + } - return doubtDataList - } + return@withContext doubtDataList + } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/utils/Utils.kt b/app/src/main/java/com/doubtless/doubtless/utils/Utils.kt index c22cfc8..c7f6c9f 100644 --- a/app/src/main/java/com/doubtless/doubtless/utils/Utils.kt +++ b/app/src/main/java/com/doubtless/doubtless/utils/Utils.kt @@ -25,6 +25,12 @@ object Utils { } } + fun Int.toPx() = TypedValue.applyDimension( + /* unit = */ TypedValue.COMPLEX_UNIT_DIP, + /* value = */ this.toFloat(), + /* metrics = */ Resources.getSystem().displayMetrics + ) + fun List.flatten(): String { var string = "" diff --git a/app/src/main/res/drawable/bg_rec_rounded.xml b/app/src/main/res/drawable/bg_rec_rounded.xml new file mode 100644 index 0000000..0e4826c --- /dev/null +++ b/app/src/main/res/drawable/bg_rec_rounded.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_outline_comment_24.xml b/app/src/main/res/drawable/ic_outline_comment_24.xml new file mode 100644 index 0000000..d0440be --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_comment_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_verified.xml b/app/src/main/res/drawable/ic_verified.xml index 4bf9fcf..84824b7 100644 --- a/app/src/main/res/drawable/ic_verified.xml +++ b/app/src/main/res/drawable/ic_verified.xml @@ -1,4 +1,4 @@ - diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 3bbafb7..f555fd8 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -11,7 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="30dp" - app:cardCornerRadius="12dp" + app:cardCornerRadius="8dp" app:cardElevation="12dp" app:layout_constraintBottom_toBottomOf="@id/iv_banner" app:layout_constraintTop_toTopOf="parent"> diff --git a/app/src/main/res/layout/answer_layout.xml b/app/src/main/res/layout/answer_layout.xml index 5c94d3c..8aa5de8 100644 --- a/app/src/main/res/layout/answer_layout.xml +++ b/app/src/main/res/layout/answer_layout.xml @@ -5,9 +5,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="0dp" - android:layout_marginEnd="0dp" - android:layout_marginBottom="0dp" android:padding="10dp" app:cardBackgroundColor="@color/cream" app:cardCornerRadius="0dp" @@ -16,17 +13,19 @@ + android:layout_height="8dp" + android:background="@color/separation_grey" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/enter_answer_layout.xml b/app/src/main/res/layout/enter_answer_layout.xml index f6ddc70..31a50f2 100644 --- a/app/src/main/res/layout/enter_answer_layout.xml +++ b/app/src/main/res/layout/enter_answer_layout.xml @@ -16,8 +16,9 @@ + android:layout_height="8dp" + android:visibility="gone" + android:background="@color/separation_grey" /> @@ -26,7 +27,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="22dp" - android:paddingTop="15dp" + android:paddingTop="24dp" android:paddingBottom="10dp"> + android:src="@drawable/ic_baseline_arrow_back_ios_24" + app:tint="@color/white" /> + android:textColor="@color/white" + android:textSize="16dp" /> diff --git a/app/src/main/res/layout/fragment_home_main_screen.xml b/app/src/main/res/layout/fragment_home_main_screen.xml index 4adcbe0..9816de9 100644 --- a/app/src/main/res/layout/fragment_home_main_screen.xml +++ b/app/src/main/res/layout/fragment_home_main_screen.xml @@ -10,7 +10,10 @@ + android:background="@color/purple" + android:paddingStart="12dp" + android:paddingEnd="12dp" + android:elevation="12dp"> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/white" /> @@ -38,10 +42,15 @@ + android:backgroundTint="@color/purple" + app:tabBackground="@color/purple" + app:tabMode="scrollable" + app:tabSelectedTextColor="@color/white" + app:tabTextColor="@color/separation_grey" + android:elevation="12dp" + app:tabTextAppearance="@style/FilterTagTabLayoutTextAppearance" /> - + android:background="@color/purple" + android:orientation="vertical" + app:layout_constraintTop_toTopOf="parent"> - + + + + + app:layout_constraintTop_toBottomOf="@id/ll_topbar" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/user_profile_layout.xml b/app/src/main/res/layout/user_profile_layout.xml index a29f929..8a64890 100644 --- a/app/src/main/res/layout/user_profile_layout.xml +++ b/app/src/main/res/layout/user_profile_layout.xml @@ -2,35 +2,41 @@ + android:elevation="0dp"> - + android:layout_height="wrap_content"> + + + app:cardCornerRadius="50dp" + app:cardElevation="1dp" + app:layout_constraintBottom_toBottomOf="@id/iv_thumbnail" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/iv_thumbnail"> @@ -39,19 +45,40 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:layout_marginTop="4dp" - android:text="User Name" + android:layout_marginTop="14dp" + android:fontFamily="@font/poppins" android:textColor="@color/black" - android:textSize="14dp" /> + android:textSize="20dp" + app:layout_constraintStart_toStartOf="@id/cv_user_image" + app:layout_constraintTop_toBottomOf="@id/cv_user_image" + tools:text="Siddharth Sharma" /> + android:layout_marginTop="0dp" + android:fontFamily="@font/poppins" + android:text="siddharthsharma@gmail.com" + android:textSize="14dp" + app:layout_constraintStart_toStartOf="@id/tv_name" + app:layout_constraintTop_toBottomOf="@id/tv_name" /> + + + android:textSize="12dp" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:textSize="12dp" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + - + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2c775ac..27bcd8e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -8,7 +8,7 @@ #e090c9 #fab679 - #A93AFF + #7f5eff #FAF4FF - #99B8B8B8 + #99E1E1E1 \ No newline at end of file diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml index 2028772..c263808 100644 --- a/app/src/main/res/values/style.xml +++ b/app/src/main/res/values/style.xml @@ -2,7 +2,8 @@ \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index ef567ae..a10bca0 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -14,7 +14,7 @@ @color/cream - true + false true @color/cream From f6b432a3c2aeb8c171f96284c16775917ca625af Mon Sep 17 00:00:00 2001 From: sidsharma2002 Date: Thu, 13 Jul 2023 15:31:29 +0530 Subject: [PATCH 27/35] image support in doubts and static thumbnail in user-profile --- app/build.gradle | 4 +-- .../doubtless/screens/doubt/DoubtData.kt | 6 +++- .../view/viewholder/DoubtPreviewViewHolder.kt | 32 +++++++++++++++--- .../res/drawable/user_profile_thumnail.png | Bin 0 -> 8077 bytes .../main/res/layout/user_profile_layout.xml | 5 +-- 5 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/drawable/user_profile_thumnail.png diff --git a/app/build.gradle b/app/build.gradle index 3ec411b..e6749cf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.doubtless.doubtless" minSdk 28 targetSdk 33 - versionCode 8 - versionName "0.1.5-ui-revamp" + versionCode 9 + versionName "0.1.6-feed-promo" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt index 04f2baa..861bf41 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/DoubtData.kt @@ -69,7 +69,11 @@ data class DoubtData( @get:PropertyName("xp_count") @set:PropertyName("xp_count") var xpCount: Long = 0, - var mentorsDpWhoInteracted: List = mutableListOf() + var mentorsDpWhoInteracted: List = mutableListOf(), + @SerializedName("iv_content") + @get:PropertyName("image_content_url") + @set:PropertyName("image_content_url") + var imageContentUrl: String? = null ) : Parcelable { companion object { fun parse(documentSnapshot: DocumentSnapshot?): DoubtData? { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt index 772e482..367066f 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/viewholder/DoubtPreviewViewHolder.kt @@ -2,7 +2,6 @@ package com.doubtless.doubtless.screens.doubt.view.viewholder import android.text.util.Linkify import android.view.View -import android.view.ViewGroup import android.widget.CheckBox import android.widget.ImageView import android.widget.LinearLayout @@ -19,7 +18,6 @@ import com.doubtless.doubtless.utils.Utils import com.doubtless.doubtless.utils.Utils.flatten import com.doubtless.doubtless.utils.Utils.toPx import com.doubtless.doubtless.utils.addStateListAnimation -import com.google.api.Distribution.BucketOptions.Linear import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -51,6 +49,7 @@ class DoubtPreviewViewHolder( private val tvYear: TextView = itemView.findViewById(R.id.user_year) private val userBadge: ImageView = itemView.findViewById(R.id.user_badge) private val llMentorsDp: LinearLayout = itemView.findViewById(R.id.ll_answered_mentor) + private val ivContent: ImageView = itemView.findViewById(R.id.iv_content) private val analyticsTracker = DoubtlessApp.getInstance().getAppCompRoot().getAnalyticsTracker() @@ -107,18 +106,41 @@ class DoubtPreviewViewHolder( tvAnswers.text = doubtData.no_answers.toString() - if (!doubtData.tags.isNullOrEmpty()) - tvTags.text = "tags : " + doubtData.tags!!.flatten() - else + if (!doubtData.tags.isNullOrEmpty()) { + var tags = "" + + doubtData.tags?.forEach { + tags += "#$it " + } + + tvTags.text = tags + + } else tvTags.isVisible = false Glide.with(ivDp).load(doubtData.userPhotoUrl).circleCrop() .into(ivDp) + // image content + if (!doubtData.imageContentUrl.isNullOrEmpty()) { + + ivContent.isVisible = true + + Glide.with(itemView.context) + .load(doubtData.imageContentUrl) + .into(ivContent) + + } else { + ivContent.isVisible = false + } + userBadge.isVisible = doubtData.xpCount > GamificationConstants.MENTOR_XP_THRESHOLD + setupMentorsWhoInteractedDpUi(doubtData) + // voting ui + val votingUseCase = DoubtlessApp.getInstance().getAppCompRoot() .getDoubtVotingDoubtCase(doubtData.copy()) diff --git a/app/src/main/res/drawable/user_profile_thumnail.png b/app/src/main/res/drawable/user_profile_thumnail.png new file mode 100644 index 0000000000000000000000000000000000000000..15ca8ee6c2f8fffed14565a1a5b28582ea47859d GIT binary patch literal 8077 zcmeHM`9IWK_#azHRCJTF6pErK$u?u8(1x^FvP6oq#@NO%!&O(VWp0V=qg+xLB*s3W zBFrNDGGv_zGZVuMX8V5J`~54v-}}SNd7bBT&htFy{XFM6=kxv~UAME5-Fa{)2n3S7 zeCgs15J-#$0*U-3wF8hqY_E6$jr8M7E-(;CMq&FW0?Nrd00>23H*73HSfcVzpd;>k z-u65QRE?J5c}ajkYU-CSp1&0(LZ?7e+}2w-E9u(bC2}P9*_`;;a`#z>KG3EU+i;+7nUB4fi3puTLGi_NJ9ETugm7wR zVbtYO7iRA=ZGsWZ~p<8r4)W&NOz@yB?PFbL-3KLZUnw@)q zMS-rzSxACFaiZd;Kxf}!GBBYHbbAS4ZVXU zP^bI-WkF=`Kg~e8U1h<`{2Fm?qaEKoA|^5qXC12s0%_1dz1M$Vc48XI!1$s46HS!z zloaLQ@8Kg;bOUd%Q3ItB-Rv)Y`3x7TRAs2F2gwK1%t z6=4q{JY3>STRY(e?_G{d5BR^549-<2{mhs$0)09F6bgg%13lU+^~7@svDg=tk|1ZT zl6o;v)#+BDtJw8Spq_XJ#&Ta#FymIB(rf>g5A1o#IG-x$ri@L_0<9;3WYC@YuFB;Z zcI%ldgIhBmOj1*dDd6TxaEIW3SH-rD#m+lzo+fce%Ax{rB#J2*1**{`i;OrYCG=Bs z7Y2>~2r;{|zY_6r@GQpTP)j98a}ti?P>1z)x8J|fnPx;-8OBX{j0;&q<_W6}HUbWOSxdiVC0`11-6dP}T{ei3-^M0$tfui(C zUXiaqw}LZc_*Bfbh~?VOh#-V24*HHU3)%WEAl*$v=X%WhvSJ`~ z%_}-9uW&K8mU^yZCrwvFp^=bT$X|YXe#BM- z&1o;DbD+7gPY74<(JOGB=%!Y%y>PYCmPIWae;!%m(%4~Mz#eH@ZKHV-V0qqVof~H- zoAo!0*iHP)M|)wp{oNcLEjjt69^(e_s$HTgajDNAf7{1mbltQ5c_#l{HJ=ih&_`tF zP(EMBj?+71%t>_3`}=?U$^8Ns3OZ{@4AhjuwauCoU8l&E?}v$7s;CBtsQ{DUU+SvI z3;V_M*%^M<>IJ$}-C0P2l{=>db=vS#of*CK4AUKf-bPGFcS;&7Bgoo6< zO)$>U zR#f7Q+q?R9su$%|7I6pRBp{^hVj8_4X<;hW(hFX8VUC9Cq?}-CX=!eq&aLWBdTL|e zPzNSCl>CJ;#dAptKO0KmeKo3g_?_wgbI=y#W8E~w^85%xkPpTwg`3kxl*74!f7`N4Jx!J_uqs%!-d(g$HMOnUyC90}a{c=^MrlPG$dz?SR46j%t zo6uSu`dH#@>RcV^?*6Uu@Z5aw%KR-=x_XQW95&+3UEB1z*I>^#COzBKga*^cL{J;+ z&t}=}Kg4@A%XjTAmyehGWc^U=*pV}YH42aKaHK!A2!3gK?JW;^ZdyH@-UV}NdLLm` z)QU+WUbfVk-W;uM!I_b$eozX^fYia4t}G5JLJU;Dnw<^M62MZ}-h#vQY6`UEtNTMN z0<~2%X_6E+&*`_-nPMVaOcs8RKN&AqE`M_(KBJGZp2tyo?swPNoE1QM{>iE<*(q5o zRcos3@`cJW*EodEeF9}C6F8Z&ql)rAFz@(0-H+}LpTw$# zRwSJJ-h^(VJoWl*$N`k~(n7KuzVglTJl&&#E;Vz^X5;+o%vRfzBA;AsFT{w%o7MFO zWIZAkW-S}+7P#;RHKDd}a1nuvkc>tWPvi`GPmY$9aolX1+BdywI|;f$Mck+{<6vB$ zSqk57CZ!1f!wvDSO?P=>Xs|7uUBT;B@oBmc_g>tz>Xw<#1I&Tuhm$Vh(UF=kLGr9W7S<7mO3a&wp z6^xqR5??UOEp*Z1_pw}>^tPkccZ8hck6bJ z!G#IZD z;3armMg*EnO@WGssZ9Ix0$i z0NX~`YY!Suf>sgK{;l&R5X%kk5H>s4J5H+}q&?g-}6v&U7A zlESk*bgOrj#Z1(?+CGpeXONt&P4vJ?BYDf+>QPJbOASFW14Hg~Z^34IwuDkeh3Zm` z91RlRI@dWVY{Ag=?;3w@N80zJ%4fy5A_W++@;&i#*XV_Pt0^y&L*H+7{;8Ib{gwB2 zYL|NKz|5cY_GJ20;OzSJPSTCd)V=n%2$^&BOKjeH#w5 z8FsR@Tdhrg^WbbOK3}a-OM5ko6<#yBm>kVR?buG%1LOVe;G%hqN2INZu4PlW^S$-Y z{t2E;y@A2*sc$Hf>%Dc7lT)xm#YJas7K{6qvKxVSCSj;^7bjN@U7YPoE8jj6bsrGR zkPiD{!F>wnzS(SZ_mb=Ri^k(E{5a{5gX#0uVxxz7E+uNdiyQBQT!!4;&eVvfL6l5L z0lz!+=fm!)7cpI&Pfcn0_zxtZ{nZ$hc~6fLm$pg6pB4{9RYr#U+b7OOlpcLOkaosq z#xU6^?a%e{KMJx4SALc)ucTKfjsFU=8CIC%XS9yMLu-USMFB2 z;ibm-$tlIy(NM7Qak289@p8+?u|fKc1Nc7-bfCT~MtVV>we!Bl8jI3Hj+Q65w^21E z(5Y;z$JF#TW}RaRUS@5yyZlMs@*`jqbA?CM2!ba;X~T+$FeqU$jHP{pF~IQgW$zZNJKNnWm=AZPlWeoA0f-G zO(0tN7|0#Mm>8mLg13FChN|<{ZR3asKkGp=Wq=vfHto#1{Eor~e#wIRH zcgENc&kVW$fF&5Vt3j`UYx|3vc}apIEr?>R0bz zCrX(=aWoBWyXR|>-3gl@?pGVa#ZcwBsE3u_B`4QU2{Y&OpX)cEPPK z?{BVbBa(Lz2N$FUL$pNKZ5~|Df?2i)yx#2QL=M1SpQ`@H%{-KsuyZcaFsJLnQyY%2)j+P9&1VSm?^|R19QeLrXaBV`>vdGYiGvbi!lZnz4Djx zT$O{W$4;85nP&;20)w@*(o5W{WAW){B6LLF%hRf?S4B&qV=onr8jg{ZZ#wVh1n-`d zU(7z9{%Be98_gTr(h7sENBBLgG$Drn%*N(*=D#CLe)A??6FE}nMqkNM+O6fG)(m|Z z-=SQD#!n!LLT~1&9qE4dSHczFUtJ!5BU`#?gXhohhMxV@eaNsFqGut9860|4#W?c? z0_Ex{D+%N_7VtO}Tj&^B%>Ks9SZWi+;VVRMVo zbUuqT`y^D*(oFx1R~WMJ5>*+paD2oJ#`qjD27#PUmehCb?CH9I>eIeCCIa#_ln!%t z$X3rXW_kn{_^*Gs()2DL;H08~*c3Vp=T^4YT?T+C#fOQkpS#atFY7EnP0W;K0uqGsxh7WtVR9#{pif>JM?#ePLS@hl#E(;*447 zIl^q^LBNWwbeMJtJ~by}+j|44Fzvt9s7j|vT@A*7sdX*#>t_O0YMp}^D2|{`ejSI* zn`)w*9o91fZ0^`gtGej&8iL64HLJ~p5VsWoKEH4X2|@$p;fNbFDDJ5O72ww}d&8Wo zsOViYV5aMgG6=MXR%KB%!}~f|(g3RgIJt9q7Ksu{zfp%z84>>RHqM{ywfD0L2=o{@ zyJ5!w)o~$GVObg>0CksgwXh@a8DPLuUQ}hlA`c+&(&cIT-rI_^{;)_?ie6|a+5Cd| zrw=Grx}4`b$D>{CfNuNpsb1W)_fx4cKIHcH%vlARzF79qWGc(P(uk0s3n2ArMBG&Q z`tnQU*Y7&ctH3XPKg}t=LQo${hvz4u;CE47j}r-wN{%Eag1|>^`31@ucgBd+^lnGdi-c(bG9;P zVJvZ5dQB?qKnKfa2O=NW%J=V&;dO2kHUIm>*kXp4&yi$mgdpb=+JHQXbJ%M<`$uUx4BQ|w6%>-k=Sj*w9>sEmG`ii0|*1)mIiSSE; zA@TtUOOyOs>*g3rz}_D9uOkH3lLr#{y=ss)JR(kq1qFcNt9Eb!_&dAIrZ$VoM1fS(ya9TV&iW>rU`jGnPl z?1a<9w$e%_!S)AYgpoJL0g$6pfO$&i(c&vI64rfOnT}Xw|3CylFRR8Ad&6frm;(Py zEMDIY3jx+sY7GoVg#!Z+tC`73R`$%M&NHO5iHkjNGEpzo|TKn6Wzdz z;?X7bdaI^ujP@`WhgQU008pS__ga;_)LR0iYiu*jSwN<9zR?XwXQ`5Iu|;}*qDVd+cwn)y5TZ*iY$5ZoS| zXsH+k9^5`k&d&V|S8o|qp%f%Cv||PCt^lhCD!W){;KT`%g)*>M^WBuC3FZ_KGmzt- z7QWcJ)&q7h`$7^d2f-2lbm5=zIsmv%$vF!JgX7~ZLk6`i++fjv7d8<)Ihx$u^lV%= z+=f&!34mS>fIa~k8*9nog0WVp`Om**EA)Ue7?+_z&Tje)kL;^s+zYA+T`QjMyqo*a zjKlFnOE4|P&o>8wXxY%!BsL9W#yx3yt}Hvl7R@-nDUSY%O%OaAeob_@BJ8jAl5tq3@64>;YUz4eaOu|&3S zJzZ}edxs+Bv9NDDnp&y_=X5+X$9sS%ECGA&G2daWnPmQRu;eXz>5*4K=gX%{WBk$G zFm(W6=U~zNVp`f8Uvcowc4*7cwWE9U#$DD_X$@0~dZ}_Oo85J4r1iGC;Iil$K%cX2 zJn@F+9fi5z7RUyVATwS>i+nrY$s3#RuusQaYzGe$sRvoDT-**XLB-%4u*0PJG#s8c zowy@&E}xUAK~7>p`6b7XYpm(DeiYtfSJ2aCjLhY?55F~W)2kVMG+?i~f$2m7o!l~} z>waj?=I+7r<^oR9{wV*qAE0B~NhJ4WUu5P94I4xFeUML2rl z4eRmykw6rD2>T+hT+;M?k1z|(2eq!YO>0103tK|(>7OvaoML0Ws>Y}OxPKmdT5%pQ z0G1BxzLe+NSI<~mh2B*O_;wD<9$RrD?XsThlwN(p!J(A$c6GU$MeB6|U|7l1UY1?RsWWc#~Lq3H@YiNHL+0krJf! z*&rNJR*oLq(v_duJlH<~Xan}-(W9*-kB2!oskuo<+o8hy`d5N7!hs@jTPo~IPkT@> zU)W|DELpv-1Y8}u#Z7gNyhg6}vbn&rxtTOfe& z&=C3%&}`RRp!x5U8<6_{eQ^Y8;{W>(uDspNb{&y|;)*{2FhG~B>@H$0J%9fn=6E2N literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/user_profile_layout.xml b/app/src/main/res/layout/user_profile_layout.xml index 8a64890..5395a10 100644 --- a/app/src/main/res/layout/user_profile_layout.xml +++ b/app/src/main/res/layout/user_profile_layout.xml @@ -14,8 +14,9 @@ Date: Thu, 13 Jul 2023 15:35:36 +0530 Subject: [PATCH 28/35] version change --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e6749cf..2194876 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.doubtless.doubtless" minSdk 28 targetSdk 33 - versionCode 9 - versionName "0.1.6-feed-promo" + versionCode 10 + versionName "0.1.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug From 7359167442c7f49f9bc7ac2eaf09df4e2a07e86a Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Sun, 25 Jun 2023 10:50:05 +0530 Subject: [PATCH 29/35] wip added ui for poll func --- .../screens/common/GenericFeedAdapter.kt | 16 +++++ .../screens/home/entities/FeedEntity.kt | 9 ++- .../screens/poll/CreatePollFragment.kt | 19 +---- .../screens/poll/InteractionPollFragment.kt | 26 +++++++ .../main/res/layout/fragment_create_poll.xml | 70 +++---------------- .../res/layout/fragment_interaction_poll.xml | 9 +++ 6 files changed, 69 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt create mode 100644 app/src/main/res/layout/fragment_interaction_poll.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index a4a8495..3cea474 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -22,6 +22,9 @@ class GenericFeedAdapter( fun onSignOutClicked() fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() + + fun onCreatePollButtonClicked() + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -65,6 +68,19 @@ class GenericFeedAdapter( } }) } + + FeedEntity.TYPE_CREATE_POLL -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.layout_home_buttons, parent, false) + return DoubtPreviewViewHolder( + view = view, + showVotingLayout = false, + interactionListener = object : DoubtPreviewViewHolder.InteractionListener { + override fun onDoubtClicked(doubtData: DoubtData, position: Int) { + interactionListener.onDoubtClicked(doubtData, position) + } + }) + } } throw Exception("type is not defined") diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index 01a6633..1c455b8 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -10,9 +10,12 @@ data class FeedEntity( ) { companion object { const val TYPE_DOUBT = 1 - const val TYPE_SEARCH_RESULT = 2 - const val TYPE_USER_PROFILE = 3 - + const val TYPE_SEARCH = 2 + const val TYPE_SEARCH_RESULT = 3 + const val TYPE_USER_PROFILE = 4 + fun getSearchEntity(): FeedEntity { + return FeedEntity(TYPE_SEARCH, null, null) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt index b4e1182..5987ab3 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt @@ -6,14 +6,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.doubtless.doubtless.R -import com.doubtless.doubtless.databinding.FragmentCreatePollBinding class CreatePollFragment : Fragment() { - private var _binding : FragmentCreatePollBinding? = null - private val binding get() = _binding!! - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -24,20 +20,7 @@ class CreatePollFragment : Fragment() { savedInstanceState: Bundle?, ): View? { // Inflate the layout for this fragment - _binding = FragmentCreatePollBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.topbarPoll.setOnClickListener { - requireActivity().onBackPressed() - } - } - - override fun onDestroy() { - super.onDestroy() - _binding=null + return inflater.inflate(R.layout.fragment_create_poll, container, false) } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt new file mode 100644 index 0000000..e353568 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt @@ -0,0 +1,26 @@ +package com.doubtless.doubtless.screens.poll + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.doubtless.doubtless.R + + +class InteractionPollFragment : Fragment() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_interaction_poll, container, false) + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_create_poll.xml b/app/src/main/res/layout/fragment_create_poll.xml index 61da021..b421a0b 100644 --- a/app/src/main/res/layout/fragment_create_poll.xml +++ b/app/src/main/res/layout/fragment_create_poll.xml @@ -37,7 +37,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginEnd="12dp" + android:layout_marginEnd="16dp" android:layout_marginStart="12dp" android:fontFamily="@font/poppins" android:text="Question" @@ -70,12 +70,10 @@ app:layout_constraintTop_toBottomOf="@id/question_header" tools:ignore="Autofill,HardcodedText,LabelFor,SpeakableTextPresentCheck,VisualLintTextFieldSize" /> - + + + - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_interaction_poll.xml b/app/src/main/res/layout/fragment_interaction_poll.xml new file mode 100644 index 0000000..6b0557d --- /dev/null +++ b/app/src/main/res/layout/fragment_interaction_poll.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file From 72bbc3ca03e6f5c614b468869f5c2978b4dba4ab Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Tue, 27 Jun 2023 19:30:24 +0530 Subject: [PATCH 30/35] update: ui for poll create and view --- .../screens/common/GenericFeedAdapter.kt | 4 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 2 +- .../screens/home/entities/FeedEntity.kt | 1 + .../screens/poll/InteractionPollFragment.kt | 26 ---- .../main/res/layout/fragment_create_poll.xml | 53 +++++-- .../res/layout/fragment_interaction_poll.xml | 9 -- app/src/main/res/layout/interaction_poll.xml | 133 ++++++++++++++++++ 7 files changed, 179 insertions(+), 49 deletions(-) delete mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt delete mode 100644 app/src/main/res/layout/fragment_interaction_poll.xml create mode 100644 app/src/main/res/layout/interaction_poll.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 3cea474..7aa321b 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -23,8 +23,6 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() - fun onCreatePollButtonClicked() - } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -69,7 +67,7 @@ class GenericFeedAdapter( }) } - FeedEntity.TYPE_CREATE_POLL -> { + FeedEntity.TYPE_BUTTONS -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.layout_home_buttons, parent, false) return DoubtPreviewViewHolder( diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 84bad98..509d560 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -43,7 +43,7 @@ class ViewDoubtsFragment : Fragment() { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) - //enterTransition = inflater.inflateTransition(R.transition.slide) + // enterTransition = inflater.inflateTransition(R.transition.slide) // exitTransition = inflater.inflateTransition(R.transition.fade) tag = arguments?.getString("tag")!! diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index 1c455b8..e521baf 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -13,6 +13,7 @@ data class FeedEntity( const val TYPE_SEARCH = 2 const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 + const val TYPE_BUTTONS = 5 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt deleted file mode 100644 index e353568..0000000 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/InteractionPollFragment.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.doubtless.doubtless.screens.poll - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.doubtless.doubtless.R - - -class InteractionPollFragment : Fragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?, - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_interaction_poll, container, false) - } - -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_create_poll.xml b/app/src/main/res/layout/fragment_create_poll.xml index b421a0b..1ac6e0c 100644 --- a/app/src/main/res/layout/fragment_create_poll.xml +++ b/app/src/main/res/layout/fragment_create_poll.xml @@ -70,10 +70,12 @@ app:layout_constraintTop_toBottomOf="@id/question_header" tools:ignore="Autofill,HardcodedText,LabelFor,SpeakableTextPresentCheck,VisualLintTextFieldSize" /> + - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_interaction_poll.xml b/app/src/main/res/layout/fragment_interaction_poll.xml deleted file mode 100644 index 6b0557d..0000000 --- a/app/src/main/res/layout/fragment_interaction_poll.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/interaction_poll.xml b/app/src/main/res/layout/interaction_poll.xml new file mode 100644 index 0000000..4a74ed3 --- /dev/null +++ b/app/src/main/res/layout/interaction_poll.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 024151e0704f78b0b0f223d29de3a4f5a3e15c66 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Tue, 27 Jun 2023 21:10:29 +0530 Subject: [PATCH 31/35] added create poll button in ViewDoubtsFragment --- .../screens/common/GenericFeedAdapter.kt | 17 +++++++++-------- .../screens/doubt/view/ViewDoubtsFragment.kt | 10 ++++++++++ .../screens/doubt/view/ViewDoubtsViewModel.kt | 17 +++++++++++++++++ .../screens/home/entities/FeedEntity.kt | 4 ++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 7aa321b..55b62db 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -22,6 +22,7 @@ class GenericFeedAdapter( fun onSignOutClicked() fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() + fun onCreatePollClicked() } @@ -70,14 +71,14 @@ class GenericFeedAdapter( FeedEntity.TYPE_BUTTONS -> { val view = LayoutInflater.from(parent.context) .inflate(R.layout.layout_home_buttons, parent, false) - return DoubtPreviewViewHolder( - view = view, - showVotingLayout = false, - interactionListener = object : DoubtPreviewViewHolder.InteractionListener { - override fun onDoubtClicked(doubtData: DoubtData, position: Int) { - interactionListener.onDoubtClicked(doubtData, position) - } - }) + return ExtraOptionsButtonHolder(view= view, + object : ExtraOptionsButtonHolder.InteractionListener{ + override fun onCreatePollClicked() { + interactionListener.onCreatePollClicked() + } + + } + ) } } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 509d560..497910a 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -122,6 +122,16 @@ class ViewDoubtsFragment : Fragment() { }) } +<<<<<<< HEAD +======= + override fun onDeleteAccountClicked() { + } + + override fun onCreatePollClicked() { + navigator.moveToCreatePollFragment() + } + }) +>>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) // how is rv restoring its scroll pos when switching tabs? binding.doubtsRecyclerView.adapter = adapter diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index a7d844f..998ac9e 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -97,6 +97,7 @@ class ViewDoubtsViewModel constructor( } } +<<<<<<< HEAD _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) fetchHomeFeedUseCase.notifyDistinctDocsFetched( @@ -105,6 +106,22 @@ class ViewDoubtsViewModel constructor( isLoading = false } +======= + // for page 1 call add search and options button entity + if (_homeEntities.isEmpty()) + entitiesFromServer.add(0, FeedEntity.getSearchEntity()) + if (_homeEntities.isEmpty()) + entitiesFromServer.add(1, FeedEntity.getOptionButtons()) + + _homeEntities.addAll(entitiesFromServer) + _fetchedHomeEntities.postValue(entitiesFromServer) + fetchHomeFeedUseCase.notifyDistinctDocsFetched( + docsFetched = homeEntities.size + - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 + ) + isLoading = false + } +>>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) fun refreshList(tag: String?) { _homeEntities.clear() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index e521baf..b4fd0dc 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -18,5 +18,9 @@ data class FeedEntity( fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) } + + fun getOptionButtons(): FeedEntity{ + return FeedEntity(TYPE_BUTTONS, null, null) + } } } \ No newline at end of file From aee9b5d1d3a3db9239a4635a48d91380c6968430 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Wed, 28 Jun 2023 20:59:36 +0530 Subject: [PATCH 32/35] view poll vieholder added , changes in ui --- .../screens/dashboard/DashboardFragment.kt | 4 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 2 + .../screens/doubt/view/ViewDoubtsViewModel.kt | 2 + .../screens/home/entities/FeedEntity.kt | 4 + .../screens/poll/CreatePollFragment.kt | 19 ++- .../screens/search/SearchFragment.kt | 1 + .../main/res/layout/fragment_create_poll.xml | 27 +++- app/src/main/res/layout/interaction_poll.xml | 133 ------------------ app/src/main/res/layout/view_poll.xml | 2 + 9 files changed, 52 insertions(+), 142 deletions(-) delete mode 100644 app/src/main/res/layout/interaction_poll.xml diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index 3ce3572..cc13640 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -107,9 +107,9 @@ class DashboardFragment : Fragment() { showBottomSheet() } - override fun onCreatePollClicked() {} + override fun onCreatePollClicked() { - override fun onPollOptionClicked(position: Int, option: String) {} + } }) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 497910a..4bfc752 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -130,6 +130,8 @@ class ViewDoubtsFragment : Fragment() { override fun onCreatePollClicked() { navigator.moveToCreatePollFragment() } + + }) >>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 998ac9e..7f717d0 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -112,6 +112,8 @@ class ViewDoubtsViewModel constructor( entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) + if(_homeEntities.isEmpty()) + entitiesFromServer.add(6, FeedEntity.getPollView() ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(entitiesFromServer) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index b4fd0dc..27ab211 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -14,6 +14,7 @@ data class FeedEntity( const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 const val TYPE_BUTTONS = 5 + const val TYPE_POLL_VOTE = 6 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) @@ -22,5 +23,8 @@ data class FeedEntity( fun getOptionButtons(): FeedEntity{ return FeedEntity(TYPE_BUTTONS, null, null) } + fun getPollView(): FeedEntity{ + return FeedEntity(TYPE_POLL_VOTE, null, null) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt index 5987ab3..b4e1182 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/CreatePollFragment.kt @@ -6,10 +6,14 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.doubtless.doubtless.R +import com.doubtless.doubtless.databinding.FragmentCreatePollBinding class CreatePollFragment : Fragment() { + private var _binding : FragmentCreatePollBinding? = null + private val binding get() = _binding!! + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -20,7 +24,20 @@ class CreatePollFragment : Fragment() { savedInstanceState: Bundle?, ): View? { // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_create_poll, container, false) + _binding = FragmentCreatePollBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.topbarPoll.setOnClickListener { + requireActivity().onBackPressed() + } + } + + override fun onDestroy() { + super.onDestroy() + _binding=null } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 39d26cc..82a35bf 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -69,6 +69,7 @@ class SearchFragment : Fragment() { override fun onSubmitFeedbackClicked() {} override fun onDeleteAccountClicked() {} + override fun onCreatePollClicked() {} }) } diff --git a/app/src/main/res/layout/fragment_create_poll.xml b/app/src/main/res/layout/fragment_create_poll.xml index 1ac6e0c..61da021 100644 --- a/app/src/main/res/layout/fragment_create_poll.xml +++ b/app/src/main/res/layout/fragment_create_poll.xml @@ -37,7 +37,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="12dp" android:layout_marginStart="12dp" android:fontFamily="@font/poppins" android:text="Question" @@ -144,7 +144,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="12dp" android:layout_marginStart="12dp" android:fontFamily="@font/poppins" android:text="Options" @@ -159,24 +159,39 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="12dp" - android:layout_marginTop="6dp" android:padding="4dp" + android:id="@+id/ll_option" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/option_header"> + app:layout_constraintTop_toBottomOf="@+id/option_header" + android:orientation="horizontal"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/interaction_poll.xml b/app/src/main/res/layout/interaction_poll.xml deleted file mode 100644 index 4a74ed3..0000000 --- a/app/src/main/res/layout/interaction_poll.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_poll.xml b/app/src/main/res/layout/view_poll.xml index 3498cf0..8867b18 100644 --- a/app/src/main/res/layout/view_poll.xml +++ b/app/src/main/res/layout/view_poll.xml @@ -99,11 +99,13 @@ android:layout_marginStart="16dp" android:layout_marginTop="6dp" android:layout_marginEnd="16dp" + android:background="@drawable/poll_item_border" android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_poll_description"> + From e9d058bc903266df6c9ba387ee790d267cda86fa Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Thu, 29 Jun 2023 10:44:08 +0530 Subject: [PATCH 33/35] added poll view in main feed with multiple options, poll func WIP --- .../screens/common/GenericFeedAdapter.kt | 21 ++++++++++++++- .../screens/dashboard/DashboardFragment.kt | 4 +-- .../screens/doubt/view/ViewDoubtsFragment.kt | 4 +++ .../screens/doubt/view/ViewDoubtsViewModel.kt | 14 ++-------- .../screens/home/entities/FeedEntity.kt | 9 ++++--- .../screens/poll/ViewPollViewHolder.kt | 26 ++----------------- .../screens/search/SearchFragment.kt | 5 +++- app/src/main/res/layout/view_poll.xml | 2 -- 8 files changed, 39 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt index 55b62db..98b49f8 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/common/GenericFeedAdapter.kt @@ -10,6 +10,8 @@ import com.doubtless.doubtless.screens.dashboard.viewholder.UserProfileViewHolde import com.doubtless.doubtless.screens.doubt.DoubtData import com.doubtless.doubtless.screens.doubt.view.viewholder.DoubtPreviewViewHolder import com.doubtless.doubtless.screens.home.entities.FeedEntity +import com.doubtless.doubtless.screens.home.viewholders.HomeSearchViewHolder +import com.doubtless.doubtless.screens.poll.ViewPollViewHolder class GenericFeedAdapter( private val genericFeedEntities: MutableList, @@ -23,7 +25,7 @@ class GenericFeedAdapter( fun onSubmitFeedbackClicked() fun onDeleteAccountClicked() fun onCreatePollClicked() - + fun onPollOptionClicked(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -80,6 +82,19 @@ class GenericFeedAdapter( } ) } + + FeedEntity.TYPE_POLL -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.view_poll, parent, false) + return ViewPollViewHolder( + view = view, + interactionListener = object : ViewPollViewHolder.InteractionListener { + override fun onPollOptionClicked(position: Int) { + interactionListener.onPollOptionClicked(position) + } + } + ) + } } throw Exception("type is not defined") @@ -98,6 +113,10 @@ class GenericFeedAdapter( genericFeedEntities[position].search_doubt!!.toDoubtData() ) + if (holder is ViewPollViewHolder && getItemViewType(position) == FeedEntity.TYPE_POLL) + holder.setData(genericFeedEntities[position]) + + if (position == itemCount - 1) { onLastItemReached.invoke() } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt index cc13640..d99e6cb 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/dashboard/DashboardFragment.kt @@ -107,9 +107,9 @@ class DashboardFragment : Fragment() { showBottomSheet() } - override fun onCreatePollClicked() { + override fun onCreatePollClicked() {} - } + override fun onPollOptionClicked(position: Int) {} }) } diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 4bfc752..797354d 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -131,6 +131,10 @@ class ViewDoubtsFragment : Fragment() { navigator.moveToCreatePollFragment() } + override fun onPollOptionClicked(position: Int) { + + } + }) >>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index 7f717d0..ba5b3ba 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -89,15 +89,6 @@ class ViewDoubtsViewModel constructor( val entitiesFromServer = mutableListOf() result.data.forEach { doubtData -> - - // we got the data for page 2 (lets say) now check if these posts existed on page 1 and add only unique ones. - if (_homeEntitiesIds.contains(doubtData.id) == false) { - entitiesFromServer.add(doubtData.toHomeEntity()) - _homeEntitiesIds[doubtData.id!!] = 1 - } - } - -<<<<<<< HEAD _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) fetchHomeFeedUseCase.notifyDistinctDocsFetched( @@ -106,14 +97,14 @@ class ViewDoubtsViewModel constructor( isLoading = false } -======= + val pollOptions = listOf("Option 1", "Option 2", "Option 3") // for page 1 call add search and options button entity if (_homeEntities.isEmpty()) entitiesFromServer.add(0, FeedEntity.getSearchEntity()) if (_homeEntities.isEmpty()) entitiesFromServer.add(1, FeedEntity.getOptionButtons()) if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollView() ) + entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(entitiesFromServer) @@ -123,7 +114,6 @@ class ViewDoubtsViewModel constructor( ) isLoading = false } ->>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) fun refreshList(tag: String?) { _homeEntities.clear() diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index 27ab211..a9bf04c 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -6,7 +6,8 @@ import com.doubtless.doubtless.screens.search.SearchResult data class FeedEntity( val type: Int, val doubt: DoubtData? = null, - val search_doubt: SearchResult? = null + val search_doubt: SearchResult? = null, + val pollOptions: List? = null ) { companion object { const val TYPE_DOUBT = 1 @@ -14,7 +15,7 @@ data class FeedEntity( const val TYPE_SEARCH_RESULT = 3 const val TYPE_USER_PROFILE = 4 const val TYPE_BUTTONS = 5 - const val TYPE_POLL_VOTE = 6 + const val TYPE_POLL = 6 fun getSearchEntity(): FeedEntity { return FeedEntity(TYPE_SEARCH, null, null) @@ -23,8 +24,8 @@ data class FeedEntity( fun getOptionButtons(): FeedEntity{ return FeedEntity(TYPE_BUTTONS, null, null) } - fun getPollView(): FeedEntity{ - return FeedEntity(TYPE_POLL_VOTE, null, null) + fun getPollEntity(pollOptions: List): FeedEntity { + return FeedEntity(TYPE_POLL, null, null, pollOptions) } } } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt index 9b48236..4ac224a 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/ViewPollViewHolder.kt @@ -13,7 +13,7 @@ import com.doubtless.doubtless.screens.home.entities.FeedEntity class ViewPollViewHolder(val view: View, val interactionListener: InteractionListener):RecyclerView.ViewHolder(view) { interface InteractionListener{ - fun onPollOptionClicked(position : Int, option: String) + fun onPollOptionClicked(position : Int) } private val userName: TextView @@ -53,7 +53,6 @@ class ViewPollViewHolder(val view: View, val interactionListener: InteractionLis } } } - private fun createOptionView(option: String, position: Int): View { val optionView = LayoutInflater.from(view.context) .inflate(R.layout.poll_options_layout, llOptions, false) @@ -61,31 +60,10 @@ class ViewPollViewHolder(val view: View, val interactionListener: InteractionLis tvOption.text = option optionView.setOnClickListener { - interactionListener.onPollOptionClicked(position, option) - updateProgress() + interactionListener.onPollOptionClicked(position) } return optionView } - private fun updateProgress() { - // Calculate the total number of votes for all options - var totalVotes = 0 - for (i in 0 until llOptions.childCount) { - val optionView = llOptions.getChildAt(i) as LinearLayout - val voteCount = optionView.findViewById(R.id.vote_count).text.toString().toInt() - totalVotes += voteCount - } - - // Update the progress percentage for each option - for (i in 0 until llOptions.childCount) { - val optionView = llOptions.getChildAt(i) as LinearLayout - val progressPoll = optionView.findViewById(R.id.progress_poll) - val voteCount = optionView.findViewById(R.id.vote_count).text.toString().toInt() - - val progress = if (totalVotes > 0) (voteCount.toFloat() / totalVotes * 100).toInt() else 0 - progressPoll.progress = progress - } - } - } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt index 82a35bf..7f165c9 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/search/SearchFragment.kt @@ -45,7 +45,9 @@ class SearchFragment : Fragment() { } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { _binding = FragmentSearchBinding.inflate(inflater) @@ -70,6 +72,7 @@ class SearchFragment : Fragment() { override fun onDeleteAccountClicked() {} override fun onCreatePollClicked() {} + override fun onPollOptionClicked(position: Int) {} }) } diff --git a/app/src/main/res/layout/view_poll.xml b/app/src/main/res/layout/view_poll.xml index 8867b18..3498cf0 100644 --- a/app/src/main/res/layout/view_poll.xml +++ b/app/src/main/res/layout/view_poll.xml @@ -99,13 +99,11 @@ android:layout_marginStart="16dp" android:layout_marginTop="6dp" android:layout_marginEnd="16dp" - android:background="@drawable/poll_item_border" android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_poll_description"> - From a29b47c307f6bfdc523c8f48080ec5b3a3eb9fa1 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Sun, 16 Jul 2023 18:01:14 +0530 Subject: [PATCH 34/35] changes --- .../screens/doubt/view/ViewDoubtsFragment.kt | 21 ++++++------------- .../screens/doubt/view/ViewDoubtsViewModel.kt | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 797354d..1fc6442 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -119,25 +119,16 @@ class ViewDoubtsFragment : Fragment() { override fun onDeleteAccountClicked() { } - }) - } - -<<<<<<< HEAD -======= - override fun onDeleteAccountClicked() { - } - - override fun onCreatePollClicked() { - navigator.moveToCreatePollFragment() - } - override fun onPollOptionClicked(position: Int) { + override fun onCreatePollClicked() { - } + } + override fun onPollOptionClicked(position: Int) { + } + }) + } - }) ->>>>>>> 620bd64 (added create poll button in ViewDoubtsFragment) // how is rv restoring its scroll pos when switching tabs? binding.doubtsRecyclerView.adapter = adapter diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index ba5b3ba..ee494be 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -107,7 +107,7 @@ class ViewDoubtsViewModel constructor( entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) _homeEntities.addAll(entitiesFromServer) - _fetchedHomeEntities.postValue(entitiesFromServer) + _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) fetchHomeFeedUseCase.notifyDistinctDocsFetched( docsFetched = homeEntities.size - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 From f3799f65937d01cff22e0cc48fc62a6922bd51c1 Mon Sep 17 00:00:00 2001 From: krishnaTech6 Date: Mon, 17 Jul 2023 21:14:02 +0530 Subject: [PATCH 35/35] changes --- app/build.gradle | 2 +- .../doubtless/navigation/FragNavigator.kt | 5 +++ .../doubtless/doubtless/navigation/Router.kt | 2 +- .../screens/doubt/view/ViewDoubtsFragment.kt | 2 +- .../screens/doubt/view/ViewDoubtsViewModel.kt | 40 +++++++++---------- .../screens/home/entities/FeedEntity.kt | 13 ++---- .../doubtless/screens/poll/PollData.kt | 9 +++++ .../doubtless/screens/poll/PollFuncUseCase.kt | 4 ++ 8 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/PollData.kt create mode 100644 app/src/main/java/com/doubtless/doubtless/screens/poll/PollFuncUseCase.kt diff --git a/app/build.gradle b/app/build.gradle index 2194876..9aaa9ca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { minSdk 28 targetSdk 33 versionCode 10 - versionName "0.1.7" + versionName "0.1.7-poll" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" signingConfig signingConfigs.debug diff --git a/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt b/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt index ddabab7..d1a5021 100644 --- a/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt +++ b/app/src/main/java/com/doubtless/doubtless/navigation/FragNavigator.kt @@ -47,6 +47,11 @@ class FragNavigator constructor( fun moveToCreatePollFragment(){ supportFragmentManager.beginTransaction() + .setCustomAnimations( + /* enter = */ R.anim.slide_in_right, + /* exit = */ R.anim.slide_out_left, + /* popEnter = */ R.anim.slide_in_left, + /* popExit = */ R.anim.slide_out_right) .replace(containerId, CreatePollFragment()) .addToBackStack(null) .setReorderingAllowed(true) diff --git a/app/src/main/java/com/doubtless/doubtless/navigation/Router.kt b/app/src/main/java/com/doubtless/doubtless/navigation/Router.kt index b650100..9244e0f 100644 --- a/app/src/main/java/com/doubtless/doubtless/navigation/Router.kt +++ b/app/src/main/java/com/doubtless/doubtless/navigation/Router.kt @@ -5,6 +5,7 @@ import android.content.Intent import com.doubtless.doubtless.screens.onboarding.OnBoardingActivity import com.doubtless.doubtless.screens.auth.LoginActivity import com.doubtless.doubtless.screens.main.MainActivity +import com.doubtless.doubtless.screens.poll.CreatePollFragment class Router { @@ -22,5 +23,4 @@ class Router { val i = Intent(activity, OnBoardingActivity::class.java) activity.startActivity(i) } - } \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt index 1fc6442..0034a17 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsFragment.kt @@ -121,7 +121,7 @@ class ViewDoubtsFragment : Fragment() { } override fun onCreatePollClicked() { - + navigator.moveToCreatePollFragment() } override fun onPollOptionClicked(position: Int) { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt index ee494be..c07f714 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/doubt/view/ViewDoubtsViewModel.kt @@ -22,7 +22,7 @@ import kotlin.collections.set class ViewDoubtsViewModel constructor( private val fetchHomeFeedUseCase: FetchHomeFeedUseCase, private val analyticsTracker: AnalyticsTracker, - private val userManager: UserManager + private val userManager: UserManager, ) : ViewModel() { private val _homeEntities = mutableListOf() @@ -88,32 +88,32 @@ class ViewDoubtsViewModel constructor( val entitiesFromServer = mutableListOf() + val pollOptions = listOf("Krishna", "Sneha", "Dhiraj") + if (_homeEntities.isEmpty()) { + entitiesFromServer.add(FeedEntity.getOptionButtons()) + entitiesFromServer.add(FeedEntity.getPollEntity(pollOptions)) + } + + result.data.forEach { doubtData -> + _homeEntities.addAll(entitiesFromServer) + _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) + fetchHomeFeedUseCase.notifyDistinctDocsFetched( + docsFetched = homeEntities.size + ) + + isLoading = false + } + + _homeEntities.addAll(entitiesFromServer) _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) fetchHomeFeedUseCase.notifyDistinctDocsFetched( docsFetched = homeEntities.size + - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 ) - isLoading = false } - val pollOptions = listOf("Option 1", "Option 2", "Option 3") - // for page 1 call add search and options button entity - if (_homeEntities.isEmpty()) - entitiesFromServer.add(0, FeedEntity.getSearchEntity()) - if (_homeEntities.isEmpty()) - entitiesFromServer.add(1, FeedEntity.getOptionButtons()) - if(_homeEntities.isEmpty()) - entitiesFromServer.add(6, FeedEntity.getPollEntity(pollOptions) ) - - _homeEntities.addAll(entitiesFromServer) - _fetchedHomeEntities.postValue(Resource.Success(entitiesFromServer)) - fetchHomeFeedUseCase.notifyDistinctDocsFetched( - docsFetched = homeEntities.size - - /* subtract one for search entity, ideally should have counted Type = Doubt size */ 1 - ) - isLoading = false - } fun refreshList(tag: String?) { _homeEntities.clear() @@ -125,7 +125,7 @@ class ViewDoubtsViewModel constructor( class Factory constructor( private val fetchHomeFeedUseCase: FetchHomeFeedUseCase, private val analyticsTracker: AnalyticsTracker, - private val userManager: UserManager + private val userManager: UserManager, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt index a9bf04c..9f90963 100644 --- a/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt +++ b/app/src/main/java/com/doubtless/doubtless/screens/home/entities/FeedEntity.kt @@ -11,15 +11,10 @@ data class FeedEntity( ) { companion object { const val TYPE_DOUBT = 1 - const val TYPE_SEARCH = 2 - const val TYPE_SEARCH_RESULT = 3 - const val TYPE_USER_PROFILE = 4 - const val TYPE_BUTTONS = 5 - const val TYPE_POLL = 6 - - fun getSearchEntity(): FeedEntity { - return FeedEntity(TYPE_SEARCH, null, null) - } + const val TYPE_SEARCH_RESULT = 2 + const val TYPE_USER_PROFILE = 3 + const val TYPE_BUTTONS = 4 + const val TYPE_POLL = 5 fun getOptionButtons(): FeedEntity{ return FeedEntity(TYPE_BUTTONS, null, null) diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/PollData.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/PollData.kt new file mode 100644 index 0000000..936c186 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/PollData.kt @@ -0,0 +1,9 @@ +package com.doubtless.doubtless.screens.poll + +data class PollData( + val id: String? = null, + val pollOptions: List? = null, + val pollOptionVotes: List? = null, + val totalVotes: String? = null, + + ) \ No newline at end of file diff --git a/app/src/main/java/com/doubtless/doubtless/screens/poll/PollFuncUseCase.kt b/app/src/main/java/com/doubtless/doubtless/screens/poll/PollFuncUseCase.kt new file mode 100644 index 0000000..2b83789 --- /dev/null +++ b/app/src/main/java/com/doubtless/doubtless/screens/poll/PollFuncUseCase.kt @@ -0,0 +1,4 @@ +package com.doubtless.doubtless.screens.poll + +class PollFuncUseCase { +} \ No newline at end of file