From e3a57be03a36da468e04fb7ccd230ce0a1f1e88f Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Wed, 25 Aug 2021 18:31:05 +0200 Subject: [PATCH 01/32] Boilerplate code for subscription view --- settings.gradle | 1 + .../activity/main/activitymain/build.gradle | 1 + .../activitymain/di/NavigationModule.kt | 7 +++ .../detail/ContactDetailNavigatorImpl.kt | 5 ++ .../detail/SubscriptionNavigatorImpl.kt | 14 +++++ .../res/navigation/main_detail_nav_graph.xml | 1 + .../navigation/ContactDetailNavigator.kt | 2 + .../ui/ContactDetailFragment.kt | 6 +- .../res/layout/fragment_contact_detail.xml | 55 +++++++++++++++++-- .../subscription/subscription/.gitignore | 1 + .../subscription/subscription/build.gradle | 46 ++++++++++++++++ .../subscription/consumer-rules.pro | 0 .../subscription/proguard-rules.pro | 21 +++++++ .../subscription/src/main/AndroidManifest.xml | 2 + .../navigation/SubscriptionNavigator.kt | 16 ++++++ .../navigation/ToSubscriptionDetail.kt | 19 +++++++ .../subscription/ui/SubscriptionFragment.kt | 44 +++++++++++++++ .../subscription/ui/SubscriptionViewModel.kt | 15 +++++ .../subscription/ui/SubscriptionViewState.kt | 7 +++ .../main/res/layout/fragment_subscription.xml | 27 +++++++++ .../res/navigation/subscription_nav_graph.xml | 17 ++++++ .../src/main/res/values-es/strings.xml | 4 ++ .../src/main/res/values-ja/strings.xml | 4 ++ .../src/main/res/values-zh/strings.xml | 4 ++ .../src/main/res/values/strings.xml | 4 ++ 25 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt create mode 100644 sphinx/screens-detail/subscription/subscription/.gitignore create mode 100644 sphinx/screens-detail/subscription/subscription/build.gradle create mode 100644 sphinx/screens-detail/subscription/subscription/consumer-rules.pro create mode 100644 sphinx/screens-detail/subscription/subscription/proguard-rules.pro create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/AndroidManifest.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/SubscriptionNavigator.kt create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/navigation/subscription_nav_graph.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml diff --git a/settings.gradle b/settings.gradle index 7457664d63..cf4dc45ad5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -134,6 +134,7 @@ include ':sphinx:screens-detail:podcast-player:podcast-player' include ':sphinx:screens-detail:qr-code:qr-code' include ':sphinx:screens-detail:scanner:scanner' include ':sphinx:screens-detail:scanner:scanner-view-model-coordinator' +include ':sphinx:screens-detail:subscription:subscription' include ':sphinx:screens-detail:support-ticket:support-ticket' include ':sphinx:screens-detail:transactions:transactions' include ':sphinx:screens-detail:tribe-members-list:tribe-members-list' diff --git a/sphinx/activity/main/activitymain/build.gradle b/sphinx/activity/main/activitymain/build.gradle index 1326b06879..168afe1a78 100644 --- a/sphinx/activity/main/activitymain/build.gradle +++ b/sphinx/activity/main/activitymain/build.gradle @@ -68,6 +68,7 @@ dependencies { api project(path: ':sphinx:screens-detail:podcast-player:podcast-player') api project(path: ':sphinx:screens-detail:qr-code:qr-code') api project(path: ':sphinx:screens-detail:scanner:scanner') + api project(path: ':sphinx:screens-detail:subscription:subscription') api project(path: ':sphinx:screens-detail:support-ticket:support-ticket') api project(path: ':sphinx:screens-detail:transactions:transactions') api project(path: ':sphinx:screens-detail:tribe-members-list:tribe-members-list') diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/di/NavigationModule.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/di/NavigationModule.kt index f7c3625649..06c862a78f 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/di/NavigationModule.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/di/NavigationModule.kt @@ -34,6 +34,7 @@ import chat.sphinx.profile.navigation.ProfileNavigator import chat.sphinx.qr_code.navigation.QRCodeNavigator import chat.sphinx.scanner.navigation.ScannerNavigator import chat.sphinx.splash.navigation.SplashNavigator +import chat.sphinx.subscription.navigation.SubscriptionNavigator import chat.sphinx.support_ticket.navigation.SupportTicketNavigator import chat.sphinx.transactions.navigation.TransactionsNavigator import chat.sphinx.tribe_detail.navigation.TribeDetailNavigator @@ -220,6 +221,12 @@ internal object NavigationModule { ): ProfileNavigator = profileNavigatorImpl + @Provides + fun provideSubscriptionNavigator( + subscriptionNavigatorImpl: SubscriptionNavigatorImpl + ): SubscriptionNavigator = + subscriptionNavigatorImpl + @Provides fun provideSupportTicketNavigator( supportTicketNavigatorImpl: SupportTicketNavigatorImpl diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/ContactDetailNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/ContactDetailNavigatorImpl.kt index 89ef84700e..6ca94f4db0 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/ContactDetailNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/ContactDetailNavigatorImpl.kt @@ -2,6 +2,7 @@ package chat.sphinx.activitymain.navigation.navigators.detail import chat.sphinx.activitymain.navigation.drivers.DetailNavigationDriver import chat.sphinx.contact_detail.navigation.ContactDetailNavigator +import chat.sphinx.subscription.navigation.ToSubscriptionDetail import javax.inject.Inject internal class ContactDetailNavigatorImpl @Inject constructor( @@ -10,4 +11,8 @@ internal class ContactDetailNavigatorImpl @Inject constructor( override suspend fun closeDetailScreen() { (navigationDriver as DetailNavigationDriver).closeDetailScreen() } + + override suspend fun toSubscribeDetailScreen() { + navigationDriver.submitNavigationRequest(ToSubscriptionDetail()) + } } diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt new file mode 100644 index 0000000000..235b2019a1 --- /dev/null +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt @@ -0,0 +1,14 @@ +package chat.sphinx.activitymain.navigation.navigators.detail + +import chat.sphinx.activitymain.navigation.drivers.DetailNavigationDriver +import chat.sphinx.subscription.navigation.SubscriptionNavigator +import javax.inject.Inject + +internal class SubscriptionNavigatorImpl @Inject constructor( + private val detailDriver: DetailNavigationDriver, +): SubscriptionNavigator(detailDriver) { + + override suspend fun closeDetailScreen() { + detailDriver.closeDetailScreen() + } +} diff --git a/sphinx/activity/main/activitymain/src/main/res/navigation/main_detail_nav_graph.xml b/sphinx/activity/main/activitymain/src/main/res/navigation/main_detail_nav_graph.xml index 90dce4cf09..63a88ade1c 100644 --- a/sphinx/activity/main/activitymain/src/main/res/navigation/main_detail_nav_graph.xml +++ b/sphinx/activity/main/activitymain/src/main/res/navigation/main_detail_nav_graph.xml @@ -16,6 +16,7 @@ + diff --git a/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/navigation/ContactDetailNavigator.kt b/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/navigation/ContactDetailNavigator.kt index 51dc1810a1..c8a95b5376 100644 --- a/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/navigation/ContactDetailNavigator.kt +++ b/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/navigation/ContactDetailNavigator.kt @@ -8,4 +8,6 @@ abstract class ContactDetailNavigator( navigationDriver: BaseNavigationDriver ): Navigator(navigationDriver) { abstract suspend fun closeDetailScreen() + + abstract suspend fun toSubscribeDetailScreen() } diff --git a/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/ui/ContactDetailFragment.kt b/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/ui/ContactDetailFragment.kt index b495be471b..5de3bac7c4 100644 --- a/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/ui/ContactDetailFragment.kt +++ b/sphinx/screens-detail/chat-detail/contact-detail/src/main/java/chat/sphinx/contact_detail/ui/ContactDetailFragment.kt @@ -23,11 +23,11 @@ internal class ContactDetailFragment: BaseFragment< override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.includeContactDetailHeader.apply { + binding.apply { textViewDetailScreenHeaderName.text = getString(R.string.contact_detail_header_name) - textViewDetailScreenClose.setOnClickListener { + textViewDetailScreenSubscribe.setOnClickListener { lifecycleScope.launch(viewModel.mainImmediate) { - viewModel.navigator.closeDetailScreen() + viewModel.navigator.toSubscribeDetailScreen() } } } diff --git a/sphinx/screens-detail/chat-detail/contact-detail/src/main/res/layout/fragment_contact_detail.xml b/sphinx/screens-detail/chat-detail/contact-detail/src/main/res/layout/fragment_contact_detail.xml index f16516914e..fc98a0f830 100644 --- a/sphinx/screens-detail/chat-detail/contact-detail/src/main/res/layout/fragment_contact_detail.xml +++ b/sphinx/screens-detail/chat-detail/contact-detail/src/main/res/layout/fragment_contact_detail.xml @@ -7,12 +7,59 @@ android:background="@drawable/background_detail_screen" tools:context="chat.sphinx.contact_detail.ui.ContactDetailFragment"> - + android:background="@drawable/background_detail_screen" + app:layout_constraintTop_toTopOf="parent" > + + + + + + + + + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/SubscriptionNavigator.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/SubscriptionNavigator.kt new file mode 100644 index 0000000000..87c1590a0e --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/SubscriptionNavigator.kt @@ -0,0 +1,16 @@ +package chat.sphinx.subscription.navigation + +import androidx.navigation.NavController +import io.matthewnelson.android_feature_navigation.requests.PopBackStack +import io.matthewnelson.concept_navigation.BaseNavigationDriver +import io.matthewnelson.concept_navigation.Navigator + +abstract class SubscriptionNavigator( + detailNavigationDriver: BaseNavigationDriver +): Navigator(detailNavigationDriver) { + abstract suspend fun closeDetailScreen() + + suspend fun popBackStack() { + navigationDriver.submitNavigationRequest(PopBackStack()) + } +} \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt new file mode 100644 index 0000000000..e758ea7363 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt @@ -0,0 +1,19 @@ +package chat.sphinx.subscription.navigation + +import androidx.navigation.NavController +import chat.sphinx.detail_resources.DetailNavOptions +import chat.sphinx.subscription.R +import io.matthewnelson.concept_navigation.NavigationRequest + +class ToSubscriptionDetail(): NavigationRequest() { + override fun navigate(controller: NavController) { + controller.navigate( + R.id.subscription_nav_graph, + null, + DetailNavOptions.default.apply { + setEnterAnim(io.matthewnelson.android_feature_navigation.R.anim.slide_in_left) + setPopExitAnim(io.matthewnelson.android_feature_navigation.R.anim.slide_out_right) + }.build() + ) + } +} diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt new file mode 100644 index 0000000000..be270c8f62 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -0,0 +1,44 @@ +package chat.sphinx.subscription.ui + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import by.kirich1409.viewbindingdelegate.viewBinding +import chat.sphinx.subscription.R +import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding +import dagger.hilt.android.AndroidEntryPoint +import io.matthewnelson.android_feature_screens.ui.base.BaseFragment +import io.matthewnelson.android_feature_screens.util.visible +import kotlinx.coroutines.launch + +@AndroidEntryPoint +internal class SubscriptionFragment: BaseFragment< + SubscriptionViewState, + SubscriptionViewModel, + FragmentSubscriptionBinding + >(R.layout.fragment_subscription) +{ + override val viewModel: SubscriptionViewModel by viewModels() + override val binding: FragmentSubscriptionBinding by viewBinding(FragmentSubscriptionBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.includeSubscriptionHeader.apply { + textViewDetailScreenHeaderName.text = getString(R.string.subscription_header_name) + textViewDetailScreenClose.setOnClickListener { + lifecycleScope.launch { viewModel.navigator.closeDetailScreen() } + } + textViewDetailScreenHeaderNavBack.apply { + visible + setOnClickListener { + lifecycleScope.launch { viewModel.navigator.popBackStack() } + } + } + } + } + + override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { +// TODO("Not yet implemented") + } +} diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt new file mode 100644 index 0000000000..0a914bccf1 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -0,0 +1,15 @@ +package chat.sphinx.subscription.ui + +import chat.sphinx.subscription.navigation.SubscriptionNavigator +import dagger.hilt.android.lifecycle.HiltViewModel +import io.matthewnelson.android_feature_viewmodel.BaseViewModel +import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import javax.inject.Inject + +@HiltViewModel +internal class SubscriptionViewModel @Inject constructor( + dispatchers: CoroutineDispatchers, + val navigator: SubscriptionNavigator +): BaseViewModel(dispatchers, SubscriptionViewState.Idle) +{ +} diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt new file mode 100644 index 0000000000..ab8d4ab75c --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -0,0 +1,7 @@ +package chat.sphinx.subscription.ui + +import io.matthewnelson.concept_views.viewstate.ViewState + +internal sealed class SubscriptionViewState: ViewState() { + object Idle: SubscriptionViewState() +} diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml new file mode 100644 index 0000000000..dbd7ec631c --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/navigation/subscription_nav_graph.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/navigation/subscription_nav_graph.xml new file mode 100644 index 0000000000..e57a2932a8 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/navigation/subscription_nav_graph.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..6e9f77b77b --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml @@ -0,0 +1,4 @@ + + + SUBSCRIPCIÓN RECURRENTE + \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..0a641e7c62 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml @@ -0,0 +1,4 @@ + + + 定期購読の繰り返し + \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000000..7243732c16 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml @@ -0,0 +1,4 @@ + + + 定期訂閱 + \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml new file mode 100644 index 0000000000..a3978ca403 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + RECURRING SUBSCRIPTION + \ No newline at end of file From 6481a1c6832ef73a8f80906c872634e8cb13fd32 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Fri, 27 Aug 2021 22:34:01 +0200 Subject: [PATCH 02/32] Code to handled merge from develop. --- .../detail/EditContactNavigatorImpl.kt | 8 +++ .../chat/sphinx/contact/ui/ContactFragment.kt | 12 +++- .../layout_contact_detail_screen_header.xml | 68 +++++++++++++++++++ .../navigation/EditContactNavigator.kt | 5 +- .../edit_contact/ui/EditContactFragment.kt | 22 +++++- .../edit_contact/ui/EditContactViewModel.kt | 4 ++ .../main/res/layout/fragment_edit_contact.xml | 2 +- .../new_contact/ui/NewContactFragment.kt | 7 +- .../main/res/layout/fragment_new_contact.xml | 55 ++------------- 9 files changed, 122 insertions(+), 61 deletions(-) create mode 100644 sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt index 4a238dbc9d..4f8d43993a 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt @@ -3,6 +3,8 @@ package chat.sphinx.activitymain.navigation.navigators.detail import chat.sphinx.activitymain.navigation.drivers.DetailNavigationDriver import chat.sphinx.edit_contact.navigation.EditContactNavigator import chat.sphinx.qr_code.navigation.ToQRCodeDetail +import chat.sphinx.subscription.navigation.ToSubscriptionDetail +import chat.sphinx.wrapper_common.dashboard.ContactId import javax.inject.Inject internal class EditContactNavigatorImpl @Inject constructor( @@ -22,4 +24,10 @@ internal class EditContactNavigatorImpl @Inject constructor( ) ) } + + override suspend fun toSubscribeDetailScreen(contactId: ContactId) { + navigationDriver.submitNavigationRequest( + ToSubscriptionDetail() + ) + } } diff --git a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt index 623126a90b..9e916a14ef 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt +++ b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt @@ -17,7 +17,7 @@ import chat.sphinx.concept_image_loader.Transformation import chat.sphinx.concept_user_colors_helper.UserColorsHelper import chat.sphinx.contact.R import chat.sphinx.contact.databinding.LayoutContactBinding -import chat.sphinx.detail_resources.databinding.LayoutDetailScreenHeaderBinding +import chat.sphinx.contact.databinding.LayoutContactDetailScreenHeaderBinding import chat.sphinx.insetter_activity.InsetterActivity import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.resources.getRandomHexCode @@ -47,7 +47,7 @@ abstract class ContactFragment< VB >(layoutId) { - abstract val headerBinding: LayoutDetailScreenHeaderBinding + abstract val headerBinding: LayoutContactDetailScreenHeaderBinding abstract val contactBinding: LayoutContactBinding abstract val userColorsHelper: UserColorsHelper @@ -87,11 +87,19 @@ abstract class ContactFragment< } } + textViewDetailScreenClose.goneIfTrue(viewModel.isExistingContact()) textViewDetailScreenClose.setOnClickListener { lifecycleScope.launch(viewModel.mainImmediate) { viewModel.navigator.closeDetailScreen() } } + + textViewDetailScreenSubscribe.goneIfFalse(viewModel.isExistingContact()) + textViewDetailScreenSubscribe.setOnClickListener { + lifecycleScope.launch(viewModel.mainImmediate) { + viewModel.navigator.closeDetailScreen() + } + } } contactBinding.apply { diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml new file mode 100644 index 0000000000..ee29226e49 --- /dev/null +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + diff --git a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/navigation/EditContactNavigator.kt b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/navigation/EditContactNavigator.kt index bac3a1829f..1465164a7d 100644 --- a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/navigation/EditContactNavigator.kt +++ b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/navigation/EditContactNavigator.kt @@ -2,8 +2,11 @@ package chat.sphinx.edit_contact.navigation import androidx.navigation.NavController import chat.sphinx.contact.navigation.ContactNavigator +import chat.sphinx.wrapper_common.dashboard.ContactId import io.matthewnelson.concept_navigation.BaseNavigationDriver abstract class EditContactNavigator( detailNavigationDriver: BaseNavigationDriver -): ContactNavigator(detailNavigationDriver) \ No newline at end of file +): ContactNavigator(detailNavigationDriver) { + abstract suspend fun toSubscribeDetailScreen(contactId: ContactId) +} \ No newline at end of file diff --git a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactFragment.kt b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactFragment.kt index 49c8ee36e3..ffddf3cffb 100644 --- a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactFragment.kt +++ b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactFragment.kt @@ -1,14 +1,18 @@ package chat.sphinx.edit_contact.ui +import android.os.Bundle +import android.view.View import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import by.kirich1409.viewbindingdelegate.viewBinding import chat.sphinx.concept_user_colors_helper.UserColorsHelper import chat.sphinx.contact.databinding.LayoutContactBinding +import chat.sphinx.contact.databinding.LayoutContactDetailScreenHeaderBinding import chat.sphinx.contact.ui.ContactFragment -import chat.sphinx.detail_resources.databinding.LayoutDetailScreenHeaderBinding import chat.sphinx.edit_contact.R import chat.sphinx.edit_contact.databinding.FragmentEditContactBinding import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -27,8 +31,8 @@ internal class EditContactFragment : ContactFragment< override val viewModel: EditContactViewModel by viewModels() override val binding: FragmentEditContactBinding by viewBinding(FragmentEditContactBinding::bind) - override val headerBinding: LayoutDetailScreenHeaderBinding by viewBinding( - LayoutDetailScreenHeaderBinding::bind, R.id.include_edit_contact_header + override val headerBinding: LayoutContactDetailScreenHeaderBinding by viewBinding( + LayoutContactDetailScreenHeaderBinding::bind, R.id.include_edit_contact_header ) override val contactBinding: LayoutContactBinding by viewBinding( LayoutContactBinding::bind, R.id.include_edit_contact_layout @@ -37,4 +41,16 @@ internal class EditContactFragment : ContactFragment< override fun getHeaderText(): String = getString(R.string.edit_contact_header_name) override fun getSaveButtonText(): String = getString(R.string.save_contact_button) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + headerBinding.apply { + textViewDetailScreenSubscribe.setOnClickListener { + lifecycleScope.launch(viewModel.mainImmediate) { + viewModel.toSubscriptionDetailScreen() + } + } + } + } } diff --git a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt index 16f0eeb201..682bf2de37 100644 --- a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt +++ b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt @@ -72,6 +72,10 @@ internal class EditContactViewModel @Inject constructor( } } + suspend fun toSubscriptionDetailScreen() { + (navigator as EditContactNavigator).toSubscribeDetailScreen(contactId) + } + override fun saveContact( contactAlias: ContactAlias, lightningNodePubKey: LightningNodePubKey, diff --git a/sphinx/screens-detail/contact/edit-contact/src/main/res/layout/fragment_edit_contact.xml b/sphinx/screens-detail/contact/edit-contact/src/main/res/layout/fragment_edit_contact.xml index a0185460b5..7a8652e13a 100644 --- a/sphinx/screens-detail/contact/edit-contact/src/main/res/layout/fragment_edit_contact.xml +++ b/sphinx/screens-detail/contact/edit-contact/src/main/res/layout/fragment_edit_contact.xml @@ -10,7 +10,7 @@ diff --git a/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactFragment.kt b/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactFragment.kt index f71fa91398..fd0491e027 100644 --- a/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactFragment.kt +++ b/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactFragment.kt @@ -4,8 +4,8 @@ import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import chat.sphinx.concept_user_colors_helper.UserColorsHelper import chat.sphinx.contact.databinding.LayoutContactBinding +import chat.sphinx.contact.databinding.LayoutContactDetailScreenHeaderBinding import chat.sphinx.contact.ui.ContactFragment -import chat.sphinx.detail_resources.databinding.LayoutDetailScreenHeaderBinding import chat.sphinx.new_contact.R import chat.sphinx.new_contact.databinding.FragmentNewContactBinding import dagger.hilt.android.AndroidEntryPoint @@ -28,9 +28,10 @@ internal class NewContactFragment: ContactFragment< override val viewModel: NewContactViewModel by viewModels() override val binding: FragmentNewContactBinding by viewBinding(FragmentNewContactBinding::bind) - override val headerBinding: LayoutDetailScreenHeaderBinding by viewBinding( - LayoutDetailScreenHeaderBinding::bind, R.id.include_new_contact_header + override val headerBinding: LayoutContactDetailScreenHeaderBinding by viewBinding( + LayoutContactDetailScreenHeaderBinding::bind, R.id.include_new_contact_header ) + override val contactBinding: LayoutContactBinding by viewBinding( LayoutContactBinding::bind, R.id.include_new_contact_layout ) diff --git a/sphinx/screens-detail/contact/new-contact/src/main/res/layout/fragment_new_contact.xml b/sphinx/screens-detail/contact/new-contact/src/main/res/layout/fragment_new_contact.xml index a41413f694..7b7193ee3d 100644 --- a/sphinx/screens-detail/contact/new-contact/src/main/res/layout/fragment_new_contact.xml +++ b/sphinx/screens-detail/contact/new-contact/src/main/res/layout/fragment_new_contact.xml @@ -8,59 +8,12 @@ android:background="@drawable/background_detail_screen" tools:context=".ui.NewContactFragment"> - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" /> Date: Sat, 28 Aug 2021 00:29:45 +0200 Subject: [PATCH 03/32] Subscription layout --- .../res/drawable/edit_text_background.xml | 10 + .../src/main/res/drawable/ic_calendar.xml | 10 + .../main/res/layout/fragment_subscription.xml | 297 +++++++++++++++++- 3 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/drawable/edit_text_background.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/drawable/ic_calendar.xml diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/edit_text_background.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/edit_text_background.xml new file mode 100644 index 0000000000..c9f27d6a6f --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/edit_text_background.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/ic_calendar.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/ic_calendar.xml new file mode 100644 index 0000000000..1cf969c70f --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/drawable/ic_calendar.xml @@ -0,0 +1,10 @@ + + + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index dbd7ec631c..cc7cae85e7 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -14,14 +14,299 @@ android:layout_height="@dimen/default_header_height" app:layout_constraintTop_toTopOf="parent" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintBottom_toBottomOf="parent"> + + + + + From 06fdcd344540587389cb088fa5a47c8e166a92f8 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Sat, 28 Aug 2021 23:21:49 +0200 Subject: [PATCH 04/32] Created custom radio group that extends constraint layout Also extract strings and handling interactions --- .../subscription/subscription/build.gradle | 1 + .../subscription/ui/SubscriptionFragment.kt | 98 ++++++ .../subscription/ui/SubscriptionViewModel.kt | 17 + .../ui/widgets/SphinxRadioGroup.kt | 320 ++++++++++++++++++ .../main/res/layout/fragment_subscription.xml | 254 ++++++++------ .../src/main/res/values-es/strings.xml | 13 + .../src/main/res/values-ja/strings.xml | 13 + .../src/main/res/values-zh/strings.xml | 13 + .../src/main/res/values/strings.xml | 13 + 9 files changed, 631 insertions(+), 111 deletions(-) create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt diff --git a/sphinx/screens-detail/subscription/subscription/build.gradle b/sphinx/screens-detail/subscription/subscription/build.gradle index f86d8960b6..5489f43670 100644 --- a/sphinx/screens-detail/subscription/subscription/build.gradle +++ b/sphinx/screens-detail/subscription/subscription/build.gradle @@ -34,6 +34,7 @@ dependencies { // KotlinAndroid implementation project(path: ':android:features:android-feature-screens') // Sphinx + implementation project(path: ':sphinx:activity:insetter-activity') implementation project(path: ':sphinx:application:common:logger') implementation project(path: ':sphinx:application:common:wrappers:wrapper-common') implementation project(path: ':sphinx:screens-detail:common:detail-resources') diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index be270c8f62..664f236472 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -1,16 +1,24 @@ package chat.sphinx.subscription.ui +import android.app.DatePickerDialog import android.os.Bundle import android.view.View +import android.widget.DatePicker import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import by.kirich1409.viewbindingdelegate.viewBinding +import chat.sphinx.insetter_activity.InsetterActivity +import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.subscription.R import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.base.BaseFragment import io.matthewnelson.android_feature_screens.util.visible import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date +import java.util.Locale @AndroidEntryPoint internal class SubscriptionFragment: BaseFragment< @@ -36,6 +44,96 @@ internal class SubscriptionFragment: BaseFragment< } } } + + binding.apply { + val calendar = Calendar.getInstance() + + editTextPayUntil.setOnClickListener { + this@SubscriptionFragment.context?.let { context -> + val datePickerDialog = DatePickerDialog( + context, + { _, year, month, dayOfMonth -> + calendar.set(Calendar.YEAR, year) + calendar.set(Calendar.MONTH, month) + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + + editTextPayUntil.setText( + SimpleDateFormat("dd/MM/yyyy", Locale.US).format(calendar.time) + ) + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), + ) + datePickerDialog.datePicker.minDate = calendar.timeInMillis + datePickerDialog.show() + } + + } + + radioButtonCustomAmount.setOnCheckedChangeListener { _, isChecked -> + editTextCustomAmount.isEnabled = isChecked + } + + radioButtonMake.setOnCheckedChangeListener { _, isChecked -> + editTextMakeQuantity.isEnabled = isChecked + } + + radioButtonUntil.setOnCheckedChangeListener { _, isChecked -> + editTextPayUntil.isEnabled = isChecked + } + + buttonSave.setOnClickListener { + + val amount: Int? = when { + radioGroupAmount.checkedRadioButtonId == R.id.radio_button_500_sats -> { + 500 + } + radioGroupAmount.checkedRadioButtonId == R.id.radio_button_1000_sats -> { + 1000 + } + radioGroupAmount.checkedRadioButtonId == R.id.radio_button_2000_sats -> { + 2000 + } + radioGroupAmount.checkedRadioButtonId == R.id.radio_button_custom_amount -> { + editTextCustomAmount.text?.toString()?.toIntOrNull() + } + else -> null + } + + val cron: String? = when { + radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_daily -> { + "daily" + } + radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_weekly -> { + "weekly" + } + radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_monthly -> { + "monthly" + } + else -> null + } + + val endDate: Date? = when { + radioGroupEndRule.checkedRadioButtonId == R.id.radio_button_make -> { + // TODO: Calculate depending on the timeInterval + Calendar.getInstance().time + } + radioGroupEndRule.checkedRadioButtonId == R.id.radio_button_until -> { + // TODO: Load editTextPayUntil.text into date + Calendar.getInstance().time + } + else -> null + } + + viewModel.saveSubscription( + amount, + cron, + endDate + ) + } + (requireActivity() as InsetterActivity).addNavigationBarPadding(layoutConstraintSubscription) + } } override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 0a914bccf1..98549f7c22 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -4,6 +4,7 @@ import chat.sphinx.subscription.navigation.SubscriptionNavigator import dagger.hilt.android.lifecycle.HiltViewModel import io.matthewnelson.android_feature_viewmodel.BaseViewModel import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import java.util.* import javax.inject.Inject @HiltViewModel @@ -12,4 +13,20 @@ internal class SubscriptionViewModel @Inject constructor( val navigator: SubscriptionNavigator ): BaseViewModel(dispatchers, SubscriptionViewState.Idle) { + + fun saveSubscription( + amount: Int?, + cron: String?, + endDate: Date? + ) { + if (amount == null) { + // TODO: Let user know amount is required + return + } + + if (cron == null) { + // TODO: Let user know interval is required + return + } + } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt new file mode 100644 index 0000000000..c93cf499bb --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt @@ -0,0 +1,320 @@ +package chat.sphinx.subscription.ui.widgets + +import android.content.Context +import kotlin.jvm.JvmOverloads +import androidx.constraintlayout.widget.ConstraintLayout +import android.widget.CompoundButton +import android.view.ViewGroup.OnHierarchyChangeListener +import android.view.ViewGroup +import android.widget.RadioButton +import android.widget.RadioGroup +import chat.sphinx.subscription.ui.widgets.SphinxRadioGroup +import android.view.accessibility.AccessibilityNodeInfo +import android.text.TextUtils +import android.content.res.TypedArray +import android.util.AttributeSet +import android.view.View +import androidx.annotation.IdRes + +class SphinxRadioGroup @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { + /** + * + * Returns the identifier of the selected radio button in this group. + * Upon empty selection, the returned value is -1. + * + * @return the unique id of the selected radio button in this group + * + * @see .check + * @see .clearCheck + * @attr ref android.R.styleable#RadioGroup_checkedButton + */ + // holds the checked id; the selection is empty by default + @get:IdRes + var checkedRadioButtonId = -1 + private set + + // tracks children radio buttons checked state + private var mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null + + // when true, mOnCheckedChangeListener discards events + private var mProtectFromCheckedChange = false + private var mOnCheckedChangeListener: OnCheckedChangeListener? = null + private var mPassThroughListener: PassThroughHierarchyChangeListener? = null + + // Indicates whether the child was set from resources or dynamically, so it can be used + // to sanitize autofill requests. + private val mInitialCheckedId = NO_ID + private fun init() { + mChildOnCheckedChangeListener = CheckedStateTracker() + mPassThroughListener = PassThroughHierarchyChangeListener() + super.setOnHierarchyChangeListener(mPassThroughListener) + } + + /** + * {@inheritDoc} + */ + override fun setOnHierarchyChangeListener(listener: OnHierarchyChangeListener) { + // the user listener is delegated to our pass-through listener + mPassThroughListener!!.mOnHierarchyChangeListener = listener + } + + /** + * {@inheritDoc} + */ + override fun onFinishInflate() { + super.onFinishInflate() + + // checks the appropriate radio button as requested in the XML file + if (checkedRadioButtonId != -1) { + mProtectFromCheckedChange = true + setCheckedStateForView(checkedRadioButtonId, true) + mProtectFromCheckedChange = false + setCheckedId(checkedRadioButtonId) + } + } + + override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) { + if (child is RadioButton) { + val button = child + if (button.isChecked) { + mProtectFromCheckedChange = true + if (checkedRadioButtonId != -1) { + setCheckedStateForView(checkedRadioButtonId, false) + } + mProtectFromCheckedChange = false + setCheckedId(button.id) + } + } + super.addView(child, index, params) + } + + /** + * + * Sets the selection to the radio button whose identifier is passed in + * parameter. Using -1 as the selection identifier clears the selection; + * such an operation is equivalent to invoking [.clearCheck]. + * + * @param id the unique id of the radio button to select in this group + * + * @see .getCheckedRadioButtonId + * @see .clearCheck + */ + fun check(@IdRes id: Int) { + // don't even bother + if (id != -1 && id == checkedRadioButtonId) { + return + } + if (checkedRadioButtonId != -1) { + setCheckedStateForView(checkedRadioButtonId, false) + } + if (id != -1) { + setCheckedStateForView(id, true) + } + setCheckedId(id) + } + + private fun setCheckedId(@IdRes id: Int) { + val changed = id != checkedRadioButtonId + checkedRadioButtonId = id + if (mOnCheckedChangeListener != null) { + mOnCheckedChangeListener!!.onCheckedChanged(this, checkedRadioButtonId) + } + // if (changed) { +// final AutofillManager afm = context.getSystemService(AutofillManager.class); +// if (afm != null) { +// afm.notifyValueChanged(this); +// } +// } + } + + private fun setCheckedStateForView(viewId: Int, checked: Boolean) { + val checkedView = findViewById(viewId) + if (checkedView != null && checkedView is RadioButton) { + checkedView.isChecked = checked + } + } + + /** + * + * Clears the selection. When the selection is cleared, no radio button + * in this group is selected and [.getCheckedRadioButtonId] returns + * null. + * + * @see .check + * @see .getCheckedRadioButtonId + */ + fun clearCheck() { + check(-1) + } + + /** + * + * Register a callback to be invoked when the checked radio button + * changes in this group. + * + * @param listener the callback to call on checked state change + */ + fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) { + mOnCheckedChangeListener = listener + } + + override fun getAccessibilityClassName(): CharSequence { + return RadioGroup::class.java.name + } + + /** + * + * Interface definition for a callback to be invoked when the checked + * radio button changed in this group. + */ + interface OnCheckedChangeListener { + /** + * + * Called when the checked radio button has changed. When the + * selection is cleared, checkedId is -1. + * + * @param group the group in which the checked radio button has changed + * @param checkedId the unique identifier of the newly checked radio button + */ + fun onCheckedChanged(group: SphinxRadioGroup?, @IdRes checkedId: Int) + } + + private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener { + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + // prevents from infinite recursion + if (mProtectFromCheckedChange) { + return + } + mProtectFromCheckedChange = true + if (checkedRadioButtonId != -1) { + setCheckedStateForView(checkedRadioButtonId, false) + } + mProtectFromCheckedChange = false + val id = buttonView.id + setCheckedId(id) + } + } + + /** + * + * A pass-through listener acts upon the events and dispatches them + * to another listener. This allows the table layout to set its own internal + * hierarchy change listener without preventing the user to setup his. + */ + private inner class PassThroughHierarchyChangeListener : OnHierarchyChangeListener { + var mOnHierarchyChangeListener: OnHierarchyChangeListener? = null + + /** + * {@inheritDoc} + */ + override fun onChildViewAdded(parent: View, child: View) { + if (parent === this@SphinxRadioGroup && child is RadioButton) { + var id = child.getId() + // generates an id if it's missing + if (id == NO_ID) { + id = generateViewId() + child.setId(id) + } + child.setOnCheckedChangeListener( + mChildOnCheckedChangeListener + ) + } + mOnHierarchyChangeListener?.onChildViewAdded(parent, child) + } + + /** + * {@inheritDoc} + */ + override fun onChildViewRemoved(parent: View, child: View) { + if (parent === this@SphinxRadioGroup && child is RadioButton) { + child.setOnCheckedChangeListener(null) + } + mOnHierarchyChangeListener?.onChildViewRemoved(parent, child) + } + } + + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) { + super.onInitializeAccessibilityNodeInfo(info) + info.collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain( + visibleChildWithTextCount, + 1, false, + AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE + ) + } + + private val visibleChildWithTextCount: Int + private get() { + var count = 0 + for (i in 0 until childCount) { + if (getChildAt(i) is RadioButton) { + if (isVisibleWithText(getChildAt(i) as RadioButton)) { + count++ + } + } + } + return count + } + + fun getIndexWithinVisibleButtons(child: View?): Int { + if (child !is RadioButton) { + return -1 + } + var index = 0 + for (i in 0 until childCount) { + if (getChildAt(i) is RadioButton) { + val button = getChildAt(i) as RadioButton + if (button === child) { + return index + } + if (isVisibleWithText(button)) { + index++ + } + } + } + return -1 + } + + private fun isVisibleWithText(button: RadioButton): Boolean { + return button.visibility == VISIBLE && !TextUtils.isEmpty(button.text) + } + + companion object { + private val LOG_TAG = RadioGroup::class.java.simpleName + } + /** + * {@inheritDoc} + */ + /** + * {@inheritDoc} + */ + /** + * {@inheritDoc} + */ + /** + * {@inheritDoc} + */ + init { + importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES + + // retrieve selected radio button as requested by the user in the + // XML layout file + var attributes: TypedArray + // attributes = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RadioGroup, com.android.internal.R.attr.radioButtonStyle, 0); +// saveAttributeDataForStyleable(context, com.android.internal.R.styleable.RadioGroup, attrs, attributes, com.android.internal.R.attr.radioButtonStyle, 0); + +// int value = attributes.getResourceId(R.styleable.RadioGroup_checkedButton, View.NO_ID); +// if (value != View.NO_ID) { +// mCheckedId = value; +// mInitialCheckedId = value; +// } +// final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL); + +// attributes.recycle(); + init() + } +} \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index cc7cae85e7..7929d1dfcf 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -2,6 +2,7 @@ - + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + /> + android:layout_height="wrap_content" + app:layout_constraintTop_toBottomOf="@+id/radio_button_500_sats" + app:layout_constraintStart_toStartOf="parent" + /> + android:layout_height="wrap_content" + app:layout_constraintTop_toBottomOf="@+id/radio_button_1000_sats" + app:layout_constraintStart_toStartOf="parent" + /> - - - - - - + + + + + + + android:text="@string/daily"/> + android:text="@string/weekly"/> + android:text="@string/monthly"/> @@ -164,7 +180,7 @@ app:layout_constraintTop_toBottomOf="@+id/layout_constraint_time_interval_container" > - + + + + + + + - - - - - - - + - + - + - + SUBSCRIPCIÓN RECURRENTE + Monto + Custom amount: + 500 sats + 1000 sats + 2000 sats + INTERVALO DE TIEMPO + Cotidiano + Semanalmente + Mensual + REGLA FINAL + Hacer + Pagos + Paga hasta \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml index 0a641e7c62..36fd9d0e81 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml @@ -1,4 +1,17 @@ 定期購読の繰り返し + + カスタム金額: + 500 sats + 1000 sats + 2000 sats + 時間間隔 + 毎日 + 毎週 + 毎月 + エンドルール + 作る + 支払い + まで支払う \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml index 7243732c16..8b9ee8715c 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml @@ -1,4 +1,17 @@ 定期訂閱 + 數量 + 自定義金額: + 500 sats + 1000 sats + 2000 sats + 時間間隔 + 日常的 + 每週 + 每月 + 結束規則 + 製作 + 付款 + 支付至 \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index a3978ca403..67653d3647 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -1,4 +1,17 @@ RECURRING SUBSCRIPTION + Amount + 500 sats + 1000 sats + 2000 sats + Custom amount: + TIME INTERVAL + Daily + Weekly + Monthly + END RULE + Make + Payments + Pay until \ No newline at end of file From 15087b0c119af569c172028402ec1cab296c3b49 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Sun, 29 Aug 2021 15:01:11 +0200 Subject: [PATCH 05/32] Improve UX on radioButtons on checked change --- .../subscription/ui/SubscriptionFragment.kt | 47 ++++---- .../ui/widgets/SphinxRadioGroup.kt | 105 ++++++++++-------- .../main/res/layout/fragment_subscription.xml | 18 ++- 3 files changed, 94 insertions(+), 76 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 664f236472..7ffa062f04 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -3,7 +3,6 @@ package chat.sphinx.subscription.ui import android.app.DatePickerDialog import android.os.Bundle import android.view.View -import android.widget.DatePicker import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import by.kirich1409.viewbindingdelegate.viewBinding @@ -16,9 +15,7 @@ import io.matthewnelson.android_feature_screens.ui.base.BaseFragment import io.matthewnelson.android_feature_screens.util.visible import kotlinx.coroutines.launch import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Date -import java.util.Locale +import java.util.* @AndroidEntryPoint internal class SubscriptionFragment: BaseFragment< @@ -71,55 +68,55 @@ internal class SubscriptionFragment: BaseFragment< } - radioButtonCustomAmount.setOnCheckedChangeListener { _, isChecked -> - editTextCustomAmount.isEnabled = isChecked - } + radioGroupAmount.setOnCheckedChangeListenerWithInputInteraction( + radioButtonCustomAmount, editTextCustomAmount + ) - radioButtonMake.setOnCheckedChangeListener { _, isChecked -> - editTextMakeQuantity.isEnabled = isChecked - } + radioGroupEndRule.setOnCheckedChangeListenerWithInputInteraction( + radioButtonMake, editTextMakeQuantity + ) - radioButtonUntil.setOnCheckedChangeListener { _, isChecked -> - editTextPayUntil.isEnabled = isChecked - } + radioGroupEndRule.setOnCheckedChangeListenerWithDatePickerInputInteraction( + radioButtonUntil, editTextPayUntil + ) buttonSave.setOnClickListener { - val amount: Int? = when { - radioGroupAmount.checkedRadioButtonId == R.id.radio_button_500_sats -> { + val amount: Int? = when (radioGroupAmount.checkedRadioButtonId) { + R.id.radio_button_500_sats -> { 500 } - radioGroupAmount.checkedRadioButtonId == R.id.radio_button_1000_sats -> { + R.id.radio_button_1000_sats -> { 1000 } - radioGroupAmount.checkedRadioButtonId == R.id.radio_button_2000_sats -> { + R.id.radio_button_2000_sats -> { 2000 } - radioGroupAmount.checkedRadioButtonId == R.id.radio_button_custom_amount -> { + R.id.radio_button_custom_amount -> { editTextCustomAmount.text?.toString()?.toIntOrNull() } else -> null } - val cron: String? = when { - radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_daily -> { + val cron: String? = when (radioGroupTimeInterval.checkedRadioButtonId) { + R.id.radio_button_daily -> { "daily" } - radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_weekly -> { + R.id.radio_button_weekly -> { "weekly" } - radioGroupTimeInterval.checkedRadioButtonId == R.id.radio_button_monthly -> { + R.id.radio_button_monthly -> { "monthly" } else -> null } - val endDate: Date? = when { - radioGroupEndRule.checkedRadioButtonId == R.id.radio_button_make -> { + val endDate: Date? = when (radioGroupEndRule.checkedRadioButtonId) { + R.id.radio_button_make -> { // TODO: Calculate depending on the timeInterval Calendar.getInstance().time } - radioGroupEndRule.checkedRadioButtonId == R.id.radio_button_until -> { + R.id.radio_button_until -> { // TODO: Load editTextPayUntil.text into date Calendar.getInstance().time } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt index c93cf499bb..45a4bf5496 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt @@ -1,20 +1,20 @@ package chat.sphinx.subscription.ui.widgets import android.content.Context -import kotlin.jvm.JvmOverloads -import androidx.constraintlayout.widget.ConstraintLayout -import android.widget.CompoundButton -import android.view.ViewGroup.OnHierarchyChangeListener -import android.view.ViewGroup -import android.widget.RadioButton -import android.widget.RadioGroup -import chat.sphinx.subscription.ui.widgets.SphinxRadioGroup -import android.view.accessibility.AccessibilityNodeInfo import android.text.TextUtils -import android.content.res.TypedArray import android.util.AttributeSet import android.view.View +import android.view.ViewGroup +import android.view.accessibility.AccessibilityNodeInfo +import android.view.inputmethod.InputMethodManager +import android.widget.CompoundButton +import android.widget.EditText +import android.widget.RadioButton +import android.widget.RadioGroup import androidx.annotation.IdRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import chat.sphinx.subscription.ui.widgets.SphinxRadioGroup class SphinxRadioGroup @JvmOverloads constructor( context: Context, @@ -48,7 +48,6 @@ class SphinxRadioGroup @JvmOverloads constructor( // Indicates whether the child was set from resources or dynamically, so it can be used // to sanitize autofill requests. - private val mInitialCheckedId = NO_ID private fun init() { mChildOnCheckedChangeListener = CheckedStateTracker() mPassThroughListener = PassThroughHierarchyChangeListener() @@ -124,12 +123,6 @@ class SphinxRadioGroup @JvmOverloads constructor( if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener!!.onCheckedChanged(this, checkedRadioButtonId) } - // if (changed) { -// final AutofillManager afm = context.getSystemService(AutofillManager.class); -// if (afm != null) { -// afm.notifyValueChanged(this); -// } -// } } private fun setCheckedStateForView(viewId: Int, checked: Boolean) { @@ -184,7 +177,7 @@ class SphinxRadioGroup @JvmOverloads constructor( fun onCheckedChanged(group: SphinxRadioGroup?, @IdRes checkedId: Int) } - private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener { + private abstract inner class AbstractCheckedStateTracker : CompoundButton.OnCheckedChangeListener { override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { // prevents from infinite recursion if (mProtectFromCheckedChange) { @@ -200,6 +193,47 @@ class SphinxRadioGroup @JvmOverloads constructor( } } + private inner class CheckedStateTracker : AbstractCheckedStateTracker(){ + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + super.onCheckedChanged(buttonView, isChecked) + + if (isChecked) { + // Hide soft keyboard + context?.let { + val inputMethodManager = ContextCompat.getSystemService(it, InputMethodManager::class.java) + inputMethodManager?.hideSoftInputFromWindow(buttonView.windowToken, 0) + } + } + } + } + + private inner class CheckedStateWithEditTextTracker(val editText: EditText) : AbstractCheckedStateTracker() { + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + super.onCheckedChanged(buttonView, isChecked) + + editText.isEnabled = isChecked + if (editText.isEnabled) { + editText.requestFocus() + context?.let { + val inputMethodManager = ContextCompat.getSystemService(it, InputMethodManager::class.java) + inputMethodManager?.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) + } + } + } + } + + private inner class CheckedStateWithDatePickerTracker(val editText: EditText) : AbstractCheckedStateTracker() { + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + super.onCheckedChanged(buttonView, isChecked) + + editText.isEnabled = isChecked + if (editText.isEnabled) { + editText.requestFocus() + editText.callOnClick() + } + } + } + /** * * A pass-through listener acts upon the events and dispatches them @@ -248,7 +282,7 @@ class SphinxRadioGroup @JvmOverloads constructor( } private val visibleChildWithTextCount: Int - private get() { + get() { var count = 0 for (i in 0 until childCount) { if (getChildAt(i) is RadioButton) { @@ -283,38 +317,19 @@ class SphinxRadioGroup @JvmOverloads constructor( return button.visibility == VISIBLE && !TextUtils.isEmpty(button.text) } - companion object { - private val LOG_TAG = RadioGroup::class.java.simpleName - } - /** - * {@inheritDoc} - */ - /** - * {@inheritDoc} - */ - /** - * {@inheritDoc} - */ /** * {@inheritDoc} */ init { importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES + init() + } - // retrieve selected radio button as requested by the user in the - // XML layout file - var attributes: TypedArray - // attributes = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RadioGroup, com.android.internal.R.attr.radioButtonStyle, 0); -// saveAttributeDataForStyleable(context, com.android.internal.R.styleable.RadioGroup, attrs, attributes, com.android.internal.R.attr.radioButtonStyle, 0); - -// int value = attributes.getResourceId(R.styleable.RadioGroup_checkedButton, View.NO_ID); -// if (value != View.NO_ID) { -// mCheckedId = value; -// mInitialCheckedId = value; -// } -// final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL); + fun setOnCheckedChangeListenerWithInputInteraction(radioButton: RadioButton, editText: EditText) { + radioButton.setOnCheckedChangeListener(CheckedStateWithEditTextTracker(editText)) + } -// attributes.recycle(); - init() + fun setOnCheckedChangeListenerWithDatePickerInputInteraction(radioButton: RadioButton, editText: EditText) { + radioButton.setOnCheckedChangeListener(CheckedStateWithDatePickerTracker(editText)) } } \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index 7929d1dfcf..fafba00a23 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -144,7 +144,7 @@ app:layout_constraintTop_toBottomOf="@+id/layout_constraint_time_interval_header" > - + android:text="@string/daily" + app:layout_constraintTop_toTopOf="parent" + /> + android:text="@string/weekly" + app:layout_constraintTop_toBottomOf="@+id/radio_button_daily" + /> - + android:text="@string/monthly" + app:layout_constraintTop_toBottomOf="@+id/radio_button_weekly" + /> + Date: Sun, 29 Aug 2021 19:28:07 +0200 Subject: [PATCH 06/32] Add Subscription side effects --- .../subscription/ui/SubscriptionFragment.kt | 10 ++++- .../subscription/ui/SubscriptionSideEffect.kt | 17 ++++++++ .../subscription/ui/SubscriptionViewModel.kt | 42 +++++++++++++++---- 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 7ffa062f04..76537227f6 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -1,6 +1,7 @@ package chat.sphinx.subscription.ui import android.app.DatePickerDialog +import android.content.Context import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels @@ -12,13 +13,16 @@ import chat.sphinx.subscription.R import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.base.BaseFragment +import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment import io.matthewnelson.android_feature_screens.util.visible import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.* @AndroidEntryPoint -internal class SubscriptionFragment: BaseFragment< +internal class SubscriptionFragment: SideEffectFragment< + Context, + SubscriptionSideEffect, SubscriptionViewState, SubscriptionViewModel, FragmentSubscriptionBinding @@ -136,4 +140,8 @@ internal class SubscriptionFragment: BaseFragment< override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { // TODO("Not yet implemented") } + + override suspend fun onSideEffectCollect(sideEffect: SubscriptionSideEffect) { + sideEffect.execute(requireActivity()) + } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt new file mode 100644 index 0000000000..7f974c0417 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt @@ -0,0 +1,17 @@ +package chat.sphinx.subscription.ui + +import android.content.Context +import chat.sphinx.resources.SphinxToastUtils +import io.matthewnelson.android_feature_toast_utils.show +import io.matthewnelson.concept_views.sideeffect.SideEffect + +internal sealed class SubscriptionSideEffect: SideEffect() { + class Notify( + private val msg: String, + private val notificationLengthLong: Boolean = true + ): SubscriptionSideEffect() { + override suspend fun execute(value: Context) { + SphinxToastUtils(toastLengthLong = notificationLengthLong).show(value, msg) + } + } +} diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 98549f7c22..5de571ef82 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -1,9 +1,14 @@ package chat.sphinx.subscription.ui +import android.content.Context +import androidx.lifecycle.viewModelScope import chat.sphinx.subscription.navigation.SubscriptionNavigator import dagger.hilt.android.lifecycle.HiltViewModel import io.matthewnelson.android_feature_viewmodel.BaseViewModel +import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel +import io.matthewnelson.android_feature_viewmodel.submitSideEffect import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import kotlinx.coroutines.launch import java.util.* import javax.inject.Inject @@ -11,7 +16,11 @@ import javax.inject.Inject internal class SubscriptionViewModel @Inject constructor( dispatchers: CoroutineDispatchers, val navigator: SubscriptionNavigator -): BaseViewModel(dispatchers, SubscriptionViewState.Idle) +): SideEffectViewModel< + Context, + SubscriptionSideEffect, + SubscriptionViewState + >(dispatchers, SubscriptionViewState.Idle) { fun saveSubscription( @@ -19,14 +28,31 @@ internal class SubscriptionViewModel @Inject constructor( cron: String?, endDate: Date? ) { - if (amount == null) { - // TODO: Let user know amount is required - return - } + viewModelScope.launch(mainImmediate) { + + if (amount == null) { + submitSideEffect( + SubscriptionSideEffect.Notify( + "Amount is required" + ) + ) + return@launch + } + + if (cron == null) { + submitSideEffect( + SubscriptionSideEffect.Notify( + "Time Interval is required" + ) + ) + return@launch + } - if (cron == null) { - // TODO: Let user know interval is required - return + submitSideEffect( + SubscriptionSideEffect.Notify( + "Subscription Saved successfully" + ) + ) } } } From 5763b964c0885ec37a9b7f8b3f04dac5f008f450 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Mon, 30 Aug 2021 18:41:27 +0200 Subject: [PATCH 07/32] Add Subscription table to the database --- .../wrapper_common/subscription/Cron.kt | 10 ++ .../wrapper_common/subscription/EndNumber.kt | 10 ++ .../subscription/SubscriptionCount.kt | 10 ++ .../sphinx/concept_coredb/SphinxDatabase.sq | 98 +++++++++++++++++++ .../chat/sphinx/feature_coredb/CoreDBImpl.kt | 30 ++++-- .../adapters/common/CommonAdapters.kt | 22 +++++ .../subscription/SubscriptionAdapters.kt | 36 +++++++ 7 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt create mode 100644 sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/EndNumber.kt create mode 100644 sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/SubscriptionCount.kt create mode 100644 sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/subscription/SubscriptionAdapters.kt diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt new file mode 100644 index 0000000000..f3e9d8a29b --- /dev/null +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt @@ -0,0 +1,10 @@ +package chat.sphinx.wrapper_common.subscription + +@JvmInline +value class Cron(val value: String) { + init { + require(value.isNotEmpty()) { + "SubscriptionCount cannot be empty" + } + } +} \ No newline at end of file diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/EndNumber.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/EndNumber.kt new file mode 100644 index 0000000000..723526699c --- /dev/null +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/EndNumber.kt @@ -0,0 +1,10 @@ +package chat.sphinx.wrapper_common.subscription + +@JvmInline +value class EndNumber(val value: Long) { + init { + require(value >= 0) { + "EndNumber must be greater than or equal to 0" + } + } +} diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/SubscriptionCount.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/SubscriptionCount.kt new file mode 100644 index 0000000000..b54aaf6354 --- /dev/null +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/SubscriptionCount.kt @@ -0,0 +1,10 @@ +package chat.sphinx.wrapper_common.subscription + +@JvmInline +value class SubscriptionCount(val value: Long) { + init { + require(value >= 0) { + "SubscriptionCount must be greater than or equal to 0" + } + } +} \ No newline at end of file diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq index a40ca1ffc9..dff1f45956 100644 --- a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq @@ -25,6 +25,10 @@ import chat.sphinx.wrapper_common.lightning.LightningRouteHint; import chat.sphinx.wrapper_common.lightning.Sat; import chat.sphinx.wrapper_common.message.MessageId; import chat.sphinx.wrapper_common.message.MessageUUID; +import chat.sphinx.wrapper_common.subscription.Cron; +import chat.sphinx.wrapper_common.subscription.EndNumber; +import chat.sphinx.wrapper_common.subscription.SubscriptionCount; +import chat.sphinx.wrapper_common.subscription.SubscriptionId; import chat.sphinx.wrapper_contact.ContactAlias; import chat.sphinx.wrapper_contact.ContactStatus; import chat.sphinx.wrapper_contact.DeviceId; @@ -731,3 +735,97 @@ WHERE id = ?; messageMediaDeleteByChatId: DELETE FROM messageMediaDbo WHERE chat_id = ?; + +CREATE TABLE subscriptionDbo( + id INTEGER AS SubscriptionId NOT NULL PRIMARY KEY, + cron TEXT AS Cron NOT NULL, + amount INTEGER AS Sat NOT NULL, + end_number INTEGER AS EndNumber, + count INTEGER AS SubscriptionCount NOT NULL, + end_date INTEGER AS DateTime, + ended INTEGER AS Boolean DEFAULT 0 NOT NULL, + paused INTEGER AS Boolean DEFAULT 0 NOT NULL, + created_at INTEGER AS DateTime NOT NULL, + updated_at INTEGER AS DateTime NOT NULL, + chat_id INTEGER AS ChatId NOT NULL, + contact_id INTEGER AS ContactId NOT NULL +); + +subscriptionGetById: +SELECT * +FROM subscriptionDbo +WHERE id = ?; + +subscriptionGetByContactId: +SELECT * +FROM subscriptionDbo +WHERE contact_id = ?; + +subscriptionGetByChatId: +SELECT * +FROM subscriptionDbo +WHERE chat_id = ?; + +subscriptionGetAll: +SELECT * +FROM subscriptionDbo; + +subscriptionUpsert { + UPDATE subscriptionDbo + SET id = :id, + cron = :cron, + amount = :amount, + end_number = :end_number, + count = :count, + end_date = :end_date, + ended = :ended, + paused = :paused, + created_at = :created_at, + updated_at = :updated_at, + chat_id = :chat_id, + contact_id = :contact_id + WHERE id = :id; + + INSERT OR IGNORE INTO subscriptionDbo( + id, + cron, + amount, + end_number, + count, + end_date, + ended, + paused, + created_at, + updated_at, + chat_id, + contact_id + ) + VALUES ( + :id, + :cron, + :amount, + :end_number, + :count, + :end_date, + :ended, + :paused, + :created_at, + :updated_at, + :chat_id, + :contact_id + ); +} + +subscriptionDeleteById: +DELETE FROM subscriptionDbo +WHERE id = ?; + +subscriptionUpdatePaused: +UPDATE subscriptionDbo +SET paused = :paused +WHERE id = ?; + +subscriptionUpdateEnded: +UPDATE subscriptionDbo +SET ended = :ended +WHERE id = ?; \ No newline at end of file diff --git a/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/CoreDBImpl.kt b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/CoreDBImpl.kt index c50c08c02b..acfd5c6d4e 100644 --- a/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/CoreDBImpl.kt +++ b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/CoreDBImpl.kt @@ -6,19 +6,21 @@ import chat.sphinx.conceptcoredb.* import chat.sphinx.feature_coredb.adapters.chat.* import chat.sphinx.feature_coredb.adapters.common.* import chat.sphinx.feature_coredb.adapters.contact.* -import chat.sphinx.feature_coredb.adapters.contact.ContactAliasAdapter -import chat.sphinx.feature_coredb.adapters.contact.ContactOwnerAdapter -import chat.sphinx.feature_coredb.adapters.contact.LightningNodeAliasAdapter -import chat.sphinx.feature_coredb.adapters.contact.LightningRouteHintAdapter -import chat.sphinx.feature_coredb.adapters.contact.PrivatePhotoAdapter import chat.sphinx.feature_coredb.adapters.invite.InviteStringAdapter -import chat.sphinx.feature_coredb.adapters.media.* +import chat.sphinx.feature_coredb.adapters.media.MediaKeyAdapter +import chat.sphinx.feature_coredb.adapters.media.MediaKeyDecryptedAdapter +import chat.sphinx.feature_coredb.adapters.media.MediaTokenAdapter +import chat.sphinx.feature_coredb.adapters.media.MediaTypeAdapter import chat.sphinx.feature_coredb.adapters.message.* +import chat.sphinx.feature_coredb.adapters.subscription.CronAdapter +import chat.sphinx.feature_coredb.adapters.subscription.EndNumberAdapter +import chat.sphinx.feature_coredb.adapters.subscription.SubscriptionCountAdapter import com.squareup.moshi.Moshi import com.squareup.sqldelight.db.SqlDriver import io.matthewnelson.concept_encryption_key.EncryptionKey -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collect abstract class CoreDBImpl(private val moshi: Moshi): CoreDB() { @@ -144,6 +146,18 @@ abstract class CoreDBImpl(private val moshi: Moshi): CoreDB() { media_tokenAdapter = MediaTokenAdapter(), local_fileAdapter = FileAdapter.getInstance(), ), + subscriptionDboAdapter = SubscriptionDbo.Adapter( + idAdapter = SubscriptionIdAdapter.getInstance(), + cronAdapter = CronAdapter(), + amountAdapter = SatAdapter.getInstance(), + end_numberAdapter = EndNumberAdapter(), + countAdapter = SubscriptionCountAdapter(), + end_dateAdapter = DateTimeAdapter.getInstance(), + created_atAdapter = DateTimeAdapter.getInstance(), + updated_atAdapter = DateTimeAdapter.getInstance(), + chat_idAdapter = ChatIdAdapter.getInstance(), + contact_idAdapter = ContactIdAdapter.getInstance() + ) ).sphinxDatabaseQueries } } diff --git a/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/common/CommonAdapters.kt b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/common/CommonAdapters.kt index 99cb556ccb..5817648ef5 100644 --- a/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/common/CommonAdapters.kt +++ b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/common/CommonAdapters.kt @@ -9,6 +9,7 @@ import chat.sphinx.wrapper_common.lightning.LightningPaymentHash import chat.sphinx.wrapper_common.lightning.LightningPaymentRequest import chat.sphinx.wrapper_common.lightning.Sat import chat.sphinx.wrapper_common.message.MessageId +import chat.sphinx.wrapper_common.subscription.SubscriptionId import com.squareup.sqldelight.ColumnAdapter import java.io.File @@ -317,3 +318,24 @@ internal class SeenAdapter private constructor(): ColumnAdapter { return value.value.toLong() } } + +internal class SubscriptionIdAdapter private constructor(): ColumnAdapter { + + companion object { + @Volatile + private var instance: SubscriptionIdAdapter? = null + fun getInstance(): SubscriptionIdAdapter = + instance ?: synchronized(this) { + instance ?: SubscriptionIdAdapter() + .also { instance = it } + } + } + + override fun decode(databaseValue: Long): SubscriptionId { + return SubscriptionId(databaseValue) + } + + override fun encode(value: SubscriptionId): Long { + return value.value + } +} diff --git a/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/subscription/SubscriptionAdapters.kt b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/subscription/SubscriptionAdapters.kt new file mode 100644 index 0000000000..fa67cbb3f0 --- /dev/null +++ b/sphinx/application/data/features/feature-coredb/src/main/java/chat/sphinx/feature_coredb/adapters/subscription/SubscriptionAdapters.kt @@ -0,0 +1,36 @@ +package chat.sphinx.feature_coredb.adapters.subscription + +import chat.sphinx.wrapper_common.subscription.Cron +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_common.subscription.SubscriptionCount +import com.squareup.sqldelight.ColumnAdapter + +internal class CronAdapter: ColumnAdapter { + override fun decode(databaseValue: String): Cron { + return Cron(databaseValue) + } + + override fun encode(value: Cron): String { + return value.value + } +} + +internal class EndNumberAdapter: ColumnAdapter { + override fun decode(databaseValue: Long): EndNumber { + return EndNumber(databaseValue) + } + + override fun encode(value: EndNumber): Long { + return value.value + } +} + +internal class SubscriptionCountAdapter: ColumnAdapter { + override fun decode(databaseValue: Long): SubscriptionCount { + return SubscriptionCount(databaseValue) + } + + override fun encode(value: SubscriptionCount): Long { + return value.value + } +} \ No newline at end of file From c87944edd4c83084e4543e547cfafa01ae5f6168 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 16:48:31 +0200 Subject: [PATCH 08/32] Add SubscriptionRepository with stubbed functions --- settings.gradle | 1 + .../wrapper_subscription/Subscription.kt | 25 ++++++ .../.gitignore | 1 + .../build.gradle | 12 +++ .../SubscriptionRepository.kt | 43 +++++++++++ .../features/feature-repository/build.gradle | 1 + .../feature_repository/SphinxRepository.kt | 70 +++++++++++++++++ .../SubscriptionDboPresenterMapper.kt | 50 ++++++++++++ .../NetworkQuerySubscription.kt | 25 +++++- .../NetworkQuerySubscriptionImpl.kt | 63 ++++++++++++++- ...sponse.kt => SubscriptionRelayResponse.kt} | 2 +- .../java/chat/sphinx/di/RepositoryModule.kt | 14 +++- .../subscription/subscription/build.gradle | 3 +- .../subscription/ui/SubscriptionFragment.kt | 24 +++--- .../subscription/ui/SubscriptionViewModel.kt | 76 +++++++++++++++++-- .../ui/widgets/SphinxRadioGroup.kt | 4 +- 16 files changed, 383 insertions(+), 31 deletions(-) create mode 100644 sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt create mode 100644 sphinx/application/data/concepts/repositories/concept-repository-subscription/.gitignore create mode 100644 sphinx/application/data/concepts/repositories/concept-repository-subscription/build.gradle create mode 100644 sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt create mode 100644 sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt rename sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/{GetSubscriptionRelayResponse.kt => SubscriptionRelayResponse.kt} (91%) diff --git a/settings.gradle b/settings.gradle index 61f70e67a2..9032bc088f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -50,6 +50,7 @@ include ':sphinx:application:data:concepts:repositories:concept-repository-dashb include ':sphinx:application:data:concepts:repositories:concept-repository-lightning' include ':sphinx:application:data:concepts:repositories:concept-repository-media' include ':sphinx:application:data:concepts:repositories:concept-repository-message' +include ':sphinx:application:data:concepts:repositories:concept-repository-subscription' include ':sphinx:application:data:features:feature-repository' include ':sphinx:application:data:features:feature-repository-android' diff --git a/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt b/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt new file mode 100644 index 0000000000..ba42690a65 --- /dev/null +++ b/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt @@ -0,0 +1,25 @@ +package chat.sphinx.wrapper_subscription + +import chat.sphinx.wrapper_common.DateTime +import chat.sphinx.wrapper_common.dashboard.ChatId +import chat.sphinx.wrapper_common.dashboard.ContactId +import chat.sphinx.wrapper_common.lightning.Sat +import chat.sphinx.wrapper_common.subscription.Cron +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_common.subscription.SubscriptionCount +import chat.sphinx.wrapper_common.subscription.SubscriptionId + +data class Subscription( + val id: SubscriptionId, + val cron: Cron, + val amount: Sat, + val end_number: EndNumber?, + val count: SubscriptionCount, + val end_date: DateTime?, + val ended: Boolean, + val paused: Boolean, + val created_at: DateTime, + val updated_at: DateTime, + val chat_id: ChatId, + val contact_id: ContactId +) \ No newline at end of file diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/.gitignore b/sphinx/application/data/concepts/repositories/concept-repository-subscription/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/build.gradle b/sphinx/application/data/concepts/repositories/concept-repository-subscription/build.gradle new file mode 100644 index 0000000000..324e9fa260 --- /dev/null +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'java-library' + id 'kotlin' +} + +dependencies { + api project(path: ':sphinx:application:common:kotlin-response') + api project(path: ':sphinx:application:common:wrappers:wrapper-subscription') + + implementation deps.kotlin.coroutinesCore + implementation project(path: ':kotlin:crypto:crypto-common') +} diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt new file mode 100644 index 0000000000..35d941ceea --- /dev/null +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -0,0 +1,43 @@ +package chat.sphinx.concept_repository_subscription + +import chat.sphinx.kotlin_response.Response +import chat.sphinx.kotlin_response.ResponseError +import chat.sphinx.wrapper_common.dashboard.ChatId +import chat.sphinx.wrapper_common.dashboard.ContactId +import chat.sphinx.wrapper_common.lightning.Sat +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_common.subscription.SubscriptionId +import chat.sphinx.wrapper_subscription.Subscription +import kotlinx.coroutines.flow.Flow + + +interface SubscriptionRepository { + suspend fun getSubscriptionByContactId( + contactId: ContactId + ): Flow + + suspend fun createSubscription( + amount: Sat, + interval: String, + contactId: ContactId, + chatId: ChatId?, + endDate: String?, + endNumber: EndNumber? + ): Response + + suspend fun updateSubscription( + subscription: Subscription + ): Response + + suspend fun restartSubscription( + subscriptionId: SubscriptionId + ): Response + + suspend fun pauseSubscription( + subscriptionId: SubscriptionId + ): Response + + suspend fun deleteSubscription( + subscriptionId: SubscriptionId + ): Response +} diff --git a/sphinx/application/data/features/feature-repository/build.gradle b/sphinx/application/data/features/feature-repository/build.gradle index a172102090..55abc71627 100644 --- a/sphinx/application/data/features/feature-repository/build.gradle +++ b/sphinx/application/data/features/feature-repository/build.gradle @@ -23,6 +23,7 @@ dependencies { api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-lightning') api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-media') api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-message') + api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-subscription') api project(path: ':sphinx:application:network:concepts:queries:concept-network-query-meme-server') api project(path: ':sphinx:application:network:concepts:queries:concept-network-query-chat') diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 28bf54bb1e..b7bc323c1a 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -32,6 +32,7 @@ import chat.sphinx.concept_repository_message.MessageRepository import chat.sphinx.concept_repository_message.model.AttachmentInfo import chat.sphinx.concept_repository_message.model.SendMessage import chat.sphinx.concept_repository_message.model.SendPayment +import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.concept_socket_io.SocketIOManager import chat.sphinx.concept_socket_io.SphinxSocketIOMessage import chat.sphinx.concept_socket_io.SphinxSocketIOMessageListener @@ -41,6 +42,7 @@ import chat.sphinx.feature_repository.mappers.contact.ContactDboPresenterMapper import chat.sphinx.feature_repository.mappers.invite.InviteDboPresenterMapper import chat.sphinx.feature_repository.mappers.mapListFrom import chat.sphinx.feature_repository.mappers.message.MessageDboPresenterMapper +import chat.sphinx.feature_repository.mappers.subscription.SubscriptionDboPresenterMapper import chat.sphinx.feature_repository.model.MessageDboWrapper import chat.sphinx.feature_repository.model.MessageMediaDboWrapper import chat.sphinx.feature_repository.util.* @@ -59,6 +61,8 @@ import chat.sphinx.wrapper_common.dashboard.toChatId import chat.sphinx.wrapper_common.invite.InviteStatus import chat.sphinx.wrapper_common.lightning.* import chat.sphinx.wrapper_common.message.* +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_common.subscription.SubscriptionId import chat.sphinx.wrapper_contact.* import chat.sphinx.wrapper_invite.Invite import chat.sphinx.wrapper_io_utils.InputStreamProvider @@ -74,6 +78,7 @@ import chat.sphinx.wrapper_relay.AuthorizationToken import chat.sphinx.wrapper_relay.RelayUrl import chat.sphinx.wrapper_rsa.RsaPrivateKey import chat.sphinx.wrapper_rsa.RsaPublicKey +import chat.sphinx.wrapper_subscription.Subscription import com.squareup.moshi.Moshi import com.squareup.sqldelight.runtime.coroutines.asFlow import com.squareup.sqldelight.runtime.coroutines.mapToList @@ -116,6 +121,7 @@ abstract class SphinxRepository( ContactRepository, LightningRepository, MessageRepository, + SubscriptionRepository, RepositoryDashboard, RepositoryMedia, CoroutineDispatchers by dispatchers, @@ -3550,4 +3556,68 @@ abstract class SphinxRepository( return response } + + /*** + * Subscriptions + */ + + private val subscriptionLock = Mutex() + private val subscriptionDboPresenterMapper: SubscriptionDboPresenterMapper by lazy { + SubscriptionDboPresenterMapper(dispatchers) + } + + override suspend fun getSubscriptionByContactId(contactId: ContactId): Flow = flow { + emitAll( + coreDB.getSphinxDatabaseQueries().subscriptionGetByContactId(contactId) + .asFlow() + .mapToOneOrNull(io) + .map { it?.let { subscriptionDboPresenterMapper.mapFrom(it) } } + .distinctUntilChanged() + ) + } + + override suspend fun createSubscription( + amount: Sat, + interval: String, + contactId: ContactId, + chatId: ChatId?, + endDate: String?, + endNumber: EndNumber? + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to create subscription"))) + + return response + } + + override suspend fun updateSubscription( + subscription: Subscription + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to update subsccription"))) + + return response + } + + override suspend fun restartSubscription( + subscriptionId: SubscriptionId + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to restart subscription"))) + + return response + } + + override suspend fun pauseSubscription( + subscriptionId: SubscriptionId + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to pause subscription"))) + + return response + } + + override suspend fun deleteSubscription( + subscriptionId: SubscriptionId + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to delete subscription"))) + + return response + } } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt new file mode 100644 index 0000000000..a66b442ae4 --- /dev/null +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt @@ -0,0 +1,50 @@ +package chat.sphinx.feature_repository.mappers.subscription + +import chat.sphinx.conceptcoredb.SubscriptionDbo +import chat.sphinx.feature_repository.mappers.ClassMapper +import chat.sphinx.wrapper_subscription.Subscription +import io.matthewnelson.concept_coroutines.CoroutineDispatchers + +@Suppress("NOTHING_TO_INLINE") +inline fun SubscriptionDbo.toSubscription(): Subscription = + Subscription( + id, + cron, + amount, + end_number, + count, + end_date, + ended, + paused, + created_at, + updated_at, + chat_id, + contact_id + ) + +internal class SubscriptionDboPresenterMapper( + dispatchers: CoroutineDispatchers +): ClassMapper(dispatchers) { + override suspend fun mapFrom(value: SubscriptionDbo): Subscription { + return value.toSubscription() + } + + override suspend fun mapTo(value: Subscription): SubscriptionDbo { + value.apply { + return SubscriptionDbo( + id, + cron, + amount, + end_number, + count, + end_date, + ended, + paused, + created_at, + updated_at, + chat_id, + contact_id + ) + } + } +} diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt index 130ffce2e0..2296b893d7 100644 --- a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt @@ -1,8 +1,8 @@ package chat.sphinx.concept_network_query_subscription import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto -import chat.sphinx.kotlin_response.ResponseError import chat.sphinx.kotlin_response.LoadResponse +import chat.sphinx.kotlin_response.ResponseError import chat.sphinx.wrapper_common.dashboard.ContactId import chat.sphinx.wrapper_common.subscription.SubscriptionId import chat.sphinx.wrapper_relay.AuthorizationToken @@ -34,14 +34,35 @@ abstract class NetworkQuerySubscription { // app.put('/subscription/:id', subcriptions.editSubscription) // app.put('/subscription/:id/pause', subcriptions.pauseSubscription) // app.put('/subscription/:id/restart', subcriptions.restartSubscription) + abstract fun putSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? = null + ): Flow> + + abstract fun putPauseSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? = null + ): Flow> + + abstract fun putRestartSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? = null + ): Flow> //////////// /// POST /// //////////// // app.post('/subscriptions', subcriptions.createSubscription) + abstract fun postSubscription( + subscriptionDto: SubscriptionDto, + relayData: Pair? = null + ): Flow> ////////////// /// DELETE /// ////////////// -// app.delete('/subscription/:id', subcriptions.deleteSubscription) + abstract fun deleteSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? = null + ): Flow> } \ No newline at end of file diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt index ae2e69ed73..45d4e1cf77 100644 --- a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt @@ -3,8 +3,8 @@ package chat.sphinx.feature_network_query_subscription import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_relay_call.NetworkRelayCall -import chat.sphinx.feature_network_query_subscription.model.GetSubscriptionRelayResponse import chat.sphinx.feature_network_query_subscription.model.GetSubscriptionsRelayResponse +import chat.sphinx.feature_network_query_subscription.model.SubscriptionRelayResponse import chat.sphinx.kotlin_response.LoadResponse import chat.sphinx.kotlin_response.ResponseError import chat.sphinx.wrapper_common.dashboard.ContactId @@ -51,7 +51,7 @@ class NetworkQuerySubscriptionImpl( relayData: Pair? ): Flow> = networkRelayCall.relayGet( - responseJsonClass = GetSubscriptionRelayResponse::class.java, + responseJsonClass = SubscriptionRelayResponse::class.java, relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}", relayData = relayData ) @@ -73,13 +73,68 @@ class NetworkQuerySubscriptionImpl( // app.put('/subscription/:id/pause', subcriptions.pauseSubscription) // app.put('/subscription/:id/restart', subcriptions.restartSubscription) + override fun putSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? + ): Flow> = + networkRelayCall.relayPut( + responseJsonClass = SubscriptionRelayResponse::class.java, + relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}", + requestBodyJsonClass = Map::class.java, + requestBody = mapOf(Pair("", "")), + relayData = relayData + ) + + override fun putPauseSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? + ): Flow> = + networkRelayCall.relayPut( + responseJsonClass = SubscriptionRelayResponse::class.java, + relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}/pause", + requestBodyJsonClass = Map::class.java, + requestBody = mapOf(Pair("", "")), + relayData = relayData + ) + + override fun putRestartSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? + ): Flow> = + networkRelayCall.relayPut( + responseJsonClass = SubscriptionRelayResponse::class.java, + relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}/restart", + requestBodyJsonClass = Map::class.java, + requestBody = mapOf(Pair("", "")), + relayData = relayData + ) + //////////// /// POST /// //////////// -// app.post('/subscriptions', subcriptions.createSubscription) + override fun postSubscription( + subscriptionDto: SubscriptionDto, + relayData: Pair? + ): Flow> = + networkRelayCall.relayPost( + responseJsonClass = SubscriptionRelayResponse::class.java, + relayEndpoint = ENDPOINT_SUBSCRIPTIONS, + requestBodyJsonClass = SubscriptionDto::class.java, + requestBody = subscriptionDto, + relayData = relayData + ) ////////////// /// DELETE /// ////////////// -// app.delete('/subscription/:id', subcriptions.deleteSubscription) + override fun deleteSubscription( + subscriptionId: SubscriptionId, + relayData: Pair? + ): Flow> = + networkRelayCall.relayDelete( + responseJsonClass = SubscriptionRelayResponse::class.java, + relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}", + requestBody = null, + relayData = relayData + ) } diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/GetSubscriptionRelayResponse.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/SubscriptionRelayResponse.kt similarity index 91% rename from sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/GetSubscriptionRelayResponse.kt rename to sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/SubscriptionRelayResponse.kt index 37b127e3cf..8447157e6e 100644 --- a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/GetSubscriptionRelayResponse.kt +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/SubscriptionRelayResponse.kt @@ -5,7 +5,7 @@ import chat.sphinx.concept_network_relay_call.RelayResponse import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class GetSubscriptionRelayResponse( +data class SubscriptionRelayResponse( override val success: Boolean, override val response: SubscriptionDto?, override val error: String? diff --git a/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt b/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt index 583d2f19ab..5ff5ff3a9f 100644 --- a/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt +++ b/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt @@ -3,20 +3,20 @@ package chat.sphinx.di import android.content.Context import chat.sphinx.concept_crypto_rsa.RSA import chat.sphinx.concept_meme_server.MemeServerTokenHandler -import chat.sphinx.concept_network_query_meme_server.NetworkQueryMemeServer -import chat.sphinx.concept_repository_chat.ChatRepository -import chat.sphinx.concept_repository_message.MessageRepository import chat.sphinx.concept_network_query_chat.NetworkQueryChat import chat.sphinx.concept_network_query_contact.NetworkQueryContact import chat.sphinx.concept_network_query_invite.NetworkQueryInvite import chat.sphinx.concept_network_query_lightning.NetworkQueryLightning +import chat.sphinx.concept_network_query_meme_server.NetworkQueryMemeServer import chat.sphinx.concept_network_query_message.NetworkQueryMessage import chat.sphinx.concept_network_query_verify_external.NetworkQueryAuthorizeExternal -import chat.sphinx.concept_relay.RelayDataHandler +import chat.sphinx.concept_repository_chat.ChatRepository import chat.sphinx.concept_repository_contact.ContactRepository import chat.sphinx.concept_repository_dashboard_android.RepositoryDashboardAndroid import chat.sphinx.concept_repository_lightning.LightningRepository import chat.sphinx.concept_repository_media.RepositoryMedia +import chat.sphinx.concept_repository_message.MessageRepository +import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.concept_socket_io.SocketIOManager import chat.sphinx.database.SphinxCoreDBImpl import chat.sphinx.feature_coredb.CoreDBImpl @@ -175,6 +175,12 @@ object RepositoryModule { ): MessageRepository = sphinxRepositoryAndroid + @Provides + fun provideSubscriptionRepository( + sphinxRepositoryAndroid: SphinxRepositoryAndroid + ): SubscriptionRepository = + sphinxRepositoryAndroid + @Provides @Suppress("UNCHECKED_CAST") fun provideRepositoryDashboardAndroid( diff --git a/sphinx/screens-detail/subscription/subscription/build.gradle b/sphinx/screens-detail/subscription/subscription/build.gradle index 5489f43670..b3d5703a8c 100644 --- a/sphinx/screens-detail/subscription/subscription/build.gradle +++ b/sphinx/screens-detail/subscription/subscription/build.gradle @@ -38,7 +38,8 @@ dependencies { implementation project(path: ':sphinx:application:common:logger') implementation project(path: ':sphinx:application:common:wrappers:wrapper-common') implementation project(path: ':sphinx:screens-detail:common:detail-resources') - + implementation project(path: ':sphinx:application:data:concepts:repositories:concept-repository-subscription') + implementation project(path: ':sphinx:application:data:concepts:repositories:concept-repository-contact') implementation deps.androidx.lifecycle.hilt implementation deps.google.hilt diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 76537227f6..d3d6c8a962 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -11,8 +11,8 @@ import chat.sphinx.insetter_activity.InsetterActivity import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.subscription.R import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding +import chat.sphinx.wrapper_common.lightning.Sat import dagger.hilt.android.AndroidEntryPoint -import io.matthewnelson.android_feature_screens.ui.base.BaseFragment import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment import io.matthewnelson.android_feature_screens.util.visible import kotlinx.coroutines.launch @@ -86,18 +86,20 @@ internal class SubscriptionFragment: SideEffectFragment< buttonSave.setOnClickListener { - val amount: Int? = when (radioGroupAmount.checkedRadioButtonId) { + val amount: Sat? = when (radioGroupAmount.checkedRadioButtonId) { R.id.radio_button_500_sats -> { - 500 + Sat(500) } R.id.radio_button_1000_sats -> { - 1000 + Sat(1000) } R.id.radio_button_2000_sats -> { - 2000 + Sat(2000) } R.id.radio_button_custom_amount -> { - editTextCustomAmount.text?.toString()?.toIntOrNull() + editTextCustomAmount.text?.toString()?.toLongOrNull()?.let { + Sat(it) + } } else -> null } @@ -115,10 +117,13 @@ internal class SubscriptionFragment: SideEffectFragment< else -> null } + var endNumber: Long? = null val endDate: Date? = when (radioGroupEndRule.checkedRadioButtonId) { R.id.radio_button_make -> { - // TODO: Calculate depending on the timeInterval - Calendar.getInstance().time + editTextMakeQuantity.text?.toString()?.let { + endNumber = it.toLongOrNull() + } + null } R.id.radio_button_until -> { // TODO: Load editTextPayUntil.text into date @@ -130,7 +135,8 @@ internal class SubscriptionFragment: SideEffectFragment< viewModel.saveSubscription( amount, cron, - endDate + endDate, + endNumber ) } (requireActivity() as InsetterActivity).addNavigationBarPadding(layoutConstraintSubscription) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 5de571ef82..c8e03dc2fd 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -1,13 +1,23 @@ package chat.sphinx.subscription.ui import android.content.Context +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import chat.sphinx.concept_repository_contact.ContactRepository +import chat.sphinx.concept_repository_subscription.SubscriptionRepository +import chat.sphinx.kotlin_response.Response import chat.sphinx.subscription.navigation.SubscriptionNavigator +import chat.sphinx.wrapper_common.dashboard.ContactId +import chat.sphinx.wrapper_common.lightning.Sat +import chat.sphinx.wrapper_common.subscription.Cron +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_subscription.Subscription import dagger.hilt.android.lifecycle.HiltViewModel -import io.matthewnelson.android_feature_viewmodel.BaseViewModel +import io.matthewnelson.android_feature_navigation.util.navArgs import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import java.util.* import javax.inject.Inject @@ -15,6 +25,9 @@ import javax.inject.Inject @HiltViewModel internal class SubscriptionViewModel @Inject constructor( dispatchers: CoroutineDispatchers, + savedStateHandle: SavedStateHandle, + private val contactRepository: ContactRepository, + private val subscriptionRepository: SubscriptionRepository, val navigator: SubscriptionNavigator ): SideEffectViewModel< Context, @@ -22,11 +35,13 @@ internal class SubscriptionViewModel @Inject constructor( SubscriptionViewState >(dispatchers, SubscriptionViewState.Idle) { + private val args: SubscriptionFragmentArgs by savedStateHandle.navArgs() fun saveSubscription( - amount: Int?, + amount: Sat?, cron: String?, - endDate: Date? + endDate: Date?, + endNumber: Long? ) { viewModelScope.launch(mainImmediate) { @@ -48,11 +63,58 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } - submitSideEffect( - SubscriptionSideEffect.Notify( - "Subscription Saved successfully" + // TODO: Can't have both endNumber and EndDate null... + if (endNumber == null && endDate == null) { + submitSideEffect( + SubscriptionSideEffect.Notify( + "Please set either the number of payments to make or end date" + ) ) - ) + return@launch + } + + subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + val loadResponse = if (subscription == null) { + subscriptionRepository.createSubscription( + amount = amount, + interval = "daily", + contactId = ContactId(args.argContactId), + chatId = null, + endDate = null, // TODO: Fix this + endNumber = endNumber?.let { EndNumber(it) } + ) + } else { + subscriptionRepository.updateSubscription( + Subscription( + id = subscription.id, + cron = Cron(cron), + amount = amount, + end_number = subscription.end_number, + count = subscription.count, + end_date = subscription.end_date, + ended = subscription.ended, + paused = subscription.paused, + created_at = subscription.created_at, + updated_at = subscription.updated_at, + chat_id = subscription.chat_id, + contact_id = subscription.contact_id + ) + ) + } + + when (loadResponse) { + is Response.Error -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Failed to save subscription") + ) + } + is Response.Success -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Saved subscription successfully") + ) + } + } + } } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt index 45a4bf5496..0ff10b79fd 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt @@ -10,7 +10,6 @@ import android.view.inputmethod.InputMethodManager import android.widget.CompoundButton import android.widget.EditText import android.widget.RadioButton -import android.widget.RadioGroup import androidx.annotation.IdRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat @@ -118,7 +117,6 @@ class SphinxRadioGroup @JvmOverloads constructor( } private fun setCheckedId(@IdRes id: Int) { - val changed = id != checkedRadioButtonId checkedRadioButtonId = id if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener!!.onCheckedChanged(this, checkedRadioButtonId) @@ -157,7 +155,7 @@ class SphinxRadioGroup @JvmOverloads constructor( } override fun getAccessibilityClassName(): CharSequence { - return RadioGroup::class.java.name + return this::class.java.name } /** From 9054926ff0400fbdb8376dee97284d5043e69a3d Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 18:30:33 +0200 Subject: [PATCH 09/32] Adding header and viewState functionality --- .../subscription/ui/SubscriptionFragment.kt | 94 +++++++++++++- .../subscription/ui/SubscriptionSideEffect.kt | 17 +++ .../subscription/ui/SubscriptionViewModel.kt | 86 ++++++++++++- .../subscription/ui/SubscriptionViewState.kt | 5 + .../main/res/layout/fragment_subscription.xml | 121 +++++++++++++++--- 5 files changed, 299 insertions(+), 24 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index d3d6c8a962..a749676553 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -14,6 +14,7 @@ import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding import chat.sphinx.wrapper_common.lightning.Sat import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment +import io.matthewnelson.android_feature_screens.util.gone import io.matthewnelson.android_feature_screens.util.visible import kotlinx.coroutines.launch import java.text.SimpleDateFormat @@ -33,20 +34,34 @@ internal class SubscriptionFragment: SideEffectFragment< override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.includeSubscriptionHeader.apply { + + binding.apply { textViewDetailScreenHeaderName.text = getString(R.string.subscription_header_name) - textViewDetailScreenClose.setOnClickListener { - lifecycleScope.launch { viewModel.navigator.closeDetailScreen() } - } + textViewDetailScreenHeaderNavBack.apply { visible setOnClickListener { lifecycleScope.launch { viewModel.navigator.popBackStack() } } } - } - binding.apply { + textViewDetailSubscriptionDelete.setOnClickListener { + viewModel.deleteSubscription() + } + + switchSubscriptionEnablement.setOnClickListener { + // Toggle checked status + switchSubscriptionEnablement.isChecked = !switchSubscriptionEnablement.isChecked + + if (switchSubscriptionEnablement.isChecked) { + // We are about to pause the subscription ask for confirmation + viewModel.pauseSubscription() + } else { + // We should restart the subscription + viewModel.restartSubscription() + } + } + val calendar = Calendar.getInstance() editTextPayUntil.setOnClickListener { @@ -141,10 +156,75 @@ internal class SubscriptionFragment: SideEffectFragment< } (requireActivity() as InsetterActivity).addNavigationBarPadding(layoutConstraintSubscription) } + + viewModel.initSubscription() } override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { -// TODO("Not yet implemented") + when(viewState) { + SubscriptionViewState.Idle -> { + // Setup for new subscription + binding.apply { + progressBarSubscriptionSave.gone + textViewDetailSubscriptionDelete.gone + layoutConstraintSubscriptionEnablement.gone + } + } + is SubscriptionViewState.SubscriptionLoaded -> { + binding.apply { + progressBarSubscriptionSave.gone + textViewDetailSubscriptionDelete.visible + layoutConstraintSubscriptionEnablement.visible + + switchSubscriptionEnablement.isChecked = !viewState.subscription.ended && !viewState.subscription.paused + + // Populate Amount + when (viewState.subscription.amount.value) { + 500L -> { + radioButton500Sats.isChecked = true + } + 1000L -> { + radioButton1000Sats.isChecked = true + } + 2000L -> { + radioButton2000Sats.isChecked = true + } + else -> { + radioButtonCustomAmount.isChecked = true + editTextCustomAmount.setText(viewState.subscription.amount.toString()) + } + } + + // Populate Time Interval + when { + viewState.subscription.cron.value.startsWith("* * *") -> { + // Daily... + radioButtonDaily.isChecked = true + } + viewState.subscription.cron.value.startsWith("* *") -> { + // Monthly + radioButtonMonthly.isChecked = true + } + else -> { + // Weekly + radioButtonWeekly.isChecked = true + } + } + + // Populate End Rule + when { + viewState.subscription.end_number != null -> { + radioButtonMake.isChecked = true + editTextMakeQuantity.setText(viewState.subscription.end_number?.value.toString()) + } + viewState.subscription.end_date != null -> { + radioButtonUntil.isChecked = true + editTextPayUntil.setText(viewState.subscription.end_date.toString()) + } + } + } + } + } } override suspend fun onSideEffectCollect(sideEffect: SubscriptionSideEffect) { diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt index 7f974c0417..c629cf4cc2 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt @@ -1,5 +1,6 @@ package chat.sphinx.subscription.ui +import android.app.AlertDialog import android.content.Context import chat.sphinx.resources.SphinxToastUtils import io.matthewnelson.android_feature_toast_utils.show @@ -14,4 +15,20 @@ internal sealed class SubscriptionSideEffect: SideEffect() { SphinxToastUtils(toastLengthLong = notificationLengthLong).show(value, msg) } } + + class AlertConfirmDeleteSubscription( + private val callback: () -> Unit + ): SubscriptionSideEffect() { + + override suspend fun execute(value: Context) { + val builder = AlertDialog.Builder(value) + builder.setTitle("Delete subscription to contact") + builder.setMessage("Are yuo sure you want to delete this subscription") + builder.setNegativeButton(android.R.string.cancel) { _,_ -> } + builder.setPositiveButton(android.R.string.ok) { _, _ -> + callback() + } + builder.show() + } + } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index c8e03dc2fd..2e9e345b0f 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -16,6 +16,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import io.matthewnelson.android_feature_navigation.util.navArgs import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect +import io.matthewnelson.android_feature_viewmodel.updateViewState import io.matthewnelson.concept_coroutines.CoroutineDispatchers import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch @@ -37,6 +38,12 @@ internal class SubscriptionViewModel @Inject constructor( { private val args: SubscriptionFragmentArgs by savedStateHandle.navArgs() + fun initSubscription() { + updateViewState( + SubscriptionViewState.Idle + ) + } + fun saveSubscription( amount: Sat?, cron: String?, @@ -63,7 +70,6 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } - // TODO: Can't have both endNumber and EndDate null... if (endNumber == null && endDate == null) { submitSideEffect( SubscriptionSideEffect.Notify( @@ -117,4 +123,82 @@ internal class SubscriptionViewModel @Inject constructor( } } } + + fun deleteSubscription() { + viewModelScope.launch(mainImmediate) { + submitSideEffect( + SubscriptionSideEffect.AlertConfirmDeleteSubscription() { + viewModelScope.launch(mainImmediate) { + submitSideEffect( + SubscriptionSideEffect.Notify("Deleting subscription") + ) + } + } + ) + } + } + + fun pauseSubscription() { + viewModelScope.launch(mainImmediate) { + subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + if (subscription == null) { + submitSideEffect( + SubscriptionSideEffect.Notify("Failed to pause subscription") + ) + } else { + when (subscriptionRepository.pauseSubscription(subscription.id)) { + is Response.Error -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Failed to pause subscription") + ) + } + is Response.Success -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Successfully paused subscription") + ) + // TODO: Set subscription to viewState... +// updateViewState( +// SubscriptionViewState.Subscription( +// +// ) +// ) + } + } + } + } + + + } + } + + fun restartSubscription() { + viewModelScope.launch(mainImmediate) { + subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + if (subscription == null) { + submitSideEffect( + SubscriptionSideEffect.Notify("Failed to restart subscription") + ) + } else { + when (subscriptionRepository.pauseSubscription(subscription.id)) { + is Response.Error -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Failed to restart subscription") + ) + } + is Response.Success -> { + submitSideEffect( + SubscriptionSideEffect.Notify("Successfully restarted subscription") + ) + // TODO: Set subscription to viewState +// updateViewState( +// SubscriptionViewState.Subscription( +// +// ) +// ) + } + } + } + } + } + } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index ab8d4ab75c..1a04d913d3 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -1,7 +1,12 @@ package chat.sphinx.subscription.ui +import chat.sphinx.wrapper_subscription.Subscription import io.matthewnelson.concept_views.viewstate.ViewState internal sealed class SubscriptionViewState: ViewState() { object Idle: SubscriptionViewState() + + class SubscriptionLoaded( + val subscription: Subscription + ) : SubscriptionViewState() } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index fafba00a23..8820aff6ea 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -8,19 +8,106 @@ android:background="@drawable/background_detail_screen" tools:context=".ui.SubscriptionFragment"> - + app:layout_constraintTop_toTopOf="parent" + > + + + + + + + + + + + + + + + @@ -93,9 +179,12 @@ android:id="@+id/layout_constraint_custom_amount" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="@dimen/default_layout_margin" + android:background="@drawable/edit_text_background" + app:layout_constraintTop_toTopOf="@+id/radio_button_custom_amount" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent"> + app:layout_constraintStart_toEndOf="@+id/radio_button_custom_amount"> @@ -270,7 +359,6 @@ android:textColor="@color/secondaryText" android:text="@string/pay_until" app:layout_constraintTop_toBottomOf="@+id/radio_button_make" - app:layout_constraintEnd_toStartOf="@+id/layout_constraint_pay_until" app:layout_constraintStart_toStartOf="parent" /> @@ -279,11 +367,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_layout_margin" + android:layout_marginStart="@dimen/default_layout_margin" android:paddingStart="10dp" android:background="@drawable/edit_text_background" + app:layout_constraintStart_toEndOf="@+id/radio_button_until" app:layout_constraintBottom_toBottomOf="@+id/radio_button_until" app:layout_constraintTop_toTopOf="@+id/radio_button_until" - app:layout_constraintEnd_toEndOf="parent" > + /> From d3550c0ba02fa3b89843d4839069cce3fe039d1c Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 19:01:50 +0200 Subject: [PATCH 10/32] Extract strings --- .../subscription/ui/SubscriptionSideEffect.kt | 5 +-- .../subscription/ui/SubscriptionViewModel.kt | 35 ++++++++++--------- .../src/main/res/values-es/strings.xml | 14 +++++++- .../src/main/res/values-ja/strings.xml | 14 +++++++- .../src/main/res/values-zh/strings.xml | 14 +++++++- .../src/main/res/values/strings.xml | 14 +++++++- 6 files changed, 74 insertions(+), 22 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt index c629cf4cc2..333e1b68d4 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionSideEffect.kt @@ -3,6 +3,7 @@ package chat.sphinx.subscription.ui import android.app.AlertDialog import android.content.Context import chat.sphinx.resources.SphinxToastUtils +import chat.sphinx.subscription.R import io.matthewnelson.android_feature_toast_utils.show import io.matthewnelson.concept_views.sideeffect.SideEffect @@ -22,8 +23,8 @@ internal sealed class SubscriptionSideEffect: SideEffect() { override suspend fun execute(value: Context) { val builder = AlertDialog.Builder(value) - builder.setTitle("Delete subscription to contact") - builder.setMessage("Are yuo sure you want to delete this subscription") + builder.setTitle(value.getString(R.string.delete_subscription)) + builder.setMessage(value.getString(R.string.are_you_sure_you_want_to_delete_subscription)) builder.setNegativeButton(android.R.string.cancel) { _,_ -> } builder.setPositiveButton(android.R.string.ok) { _, _ -> callback() diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 2e9e345b0f..81adb50bd2 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -1,11 +1,13 @@ package chat.sphinx.subscription.ui +import android.app.Application import android.content.Context import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import chat.sphinx.concept_repository_contact.ContactRepository import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.kotlin_response.Response +import chat.sphinx.subscription.R import chat.sphinx.subscription.navigation.SubscriptionNavigator import chat.sphinx.wrapper_common.dashboard.ContactId import chat.sphinx.wrapper_common.lightning.Sat @@ -25,6 +27,7 @@ import javax.inject.Inject @HiltViewModel internal class SubscriptionViewModel @Inject constructor( + val app: Application, dispatchers: CoroutineDispatchers, savedStateHandle: SavedStateHandle, private val contactRepository: ContactRepository, @@ -46,7 +49,7 @@ internal class SubscriptionViewModel @Inject constructor( fun saveSubscription( amount: Sat?, - cron: String?, + interval: String?, endDate: Date?, endNumber: Long? ) { @@ -55,16 +58,16 @@ internal class SubscriptionViewModel @Inject constructor( if (amount == null) { submitSideEffect( SubscriptionSideEffect.Notify( - "Amount is required" + app.getString(R.string.amount_is_required) ) ) return@launch } - if (cron == null) { + if (interval == null) { submitSideEffect( SubscriptionSideEffect.Notify( - "Time Interval is required" + app.getString(R.string.time_interval_is_required) ) ) return@launch @@ -73,7 +76,7 @@ internal class SubscriptionViewModel @Inject constructor( if (endNumber == null && endDate == null) { submitSideEffect( SubscriptionSideEffect.Notify( - "Please set either the number of payments to make or end date" + app.getString(R.string.please_set_either_the_number_of_payments_to_make_or_end_date) ) ) return@launch @@ -83,7 +86,7 @@ internal class SubscriptionViewModel @Inject constructor( val loadResponse = if (subscription == null) { subscriptionRepository.createSubscription( amount = amount, - interval = "daily", + interval = interval, contactId = ContactId(args.argContactId), chatId = null, endDate = null, // TODO: Fix this @@ -93,7 +96,7 @@ internal class SubscriptionViewModel @Inject constructor( subscriptionRepository.updateSubscription( Subscription( id = subscription.id, - cron = Cron(cron), + cron = Cron(interval), amount = amount, end_number = subscription.end_number, count = subscription.count, @@ -111,12 +114,12 @@ internal class SubscriptionViewModel @Inject constructor( when (loadResponse) { is Response.Error -> { submitSideEffect( - SubscriptionSideEffect.Notify("Failed to save subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_save_subscription)) ) } is Response.Success -> { submitSideEffect( - SubscriptionSideEffect.Notify("Saved subscription successfully") + SubscriptionSideEffect.Notify(app.getString(R.string.saved_subscription_successfully)) ) } } @@ -130,7 +133,7 @@ internal class SubscriptionViewModel @Inject constructor( SubscriptionSideEffect.AlertConfirmDeleteSubscription() { viewModelScope.launch(mainImmediate) { submitSideEffect( - SubscriptionSideEffect.Notify("Deleting subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.deleting_subscription)) ) } } @@ -143,18 +146,18 @@ internal class SubscriptionViewModel @Inject constructor( subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> if (subscription == null) { submitSideEffect( - SubscriptionSideEffect.Notify("Failed to pause subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_pause_subscription)) ) } else { when (subscriptionRepository.pauseSubscription(subscription.id)) { is Response.Error -> { submitSideEffect( - SubscriptionSideEffect.Notify("Failed to pause subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_pause_subscription)) ) } is Response.Success -> { submitSideEffect( - SubscriptionSideEffect.Notify("Successfully paused subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.successfully_paused_subscription)) ) // TODO: Set subscription to viewState... // updateViewState( @@ -176,18 +179,18 @@ internal class SubscriptionViewModel @Inject constructor( subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> if (subscription == null) { submitSideEffect( - SubscriptionSideEffect.Notify("Failed to restart subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_restart_subscription)) ) } else { when (subscriptionRepository.pauseSubscription(subscription.id)) { is Response.Error -> { submitSideEffect( - SubscriptionSideEffect.Notify("Failed to restart subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_restart_subscription)) ) } is Response.Success -> { submitSideEffect( - SubscriptionSideEffect.Notify("Successfully restarted subscription") + SubscriptionSideEffect.Notify(app.getString(R.string.successfully_restarted_subscription)) ) // TODO: Set subscription to viewState // updateViewState( diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml index 9959bdf55b..2db901e283 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml @@ -1,6 +1,6 @@ - SUBSCRIPCIÓN RECURRENTE + PERIÓDICA Monto Custom amount: 500 sats @@ -14,4 +14,16 @@ Hacer Pagos Paga hasta + Eliminar suscripción + ¿Está seguro de que desea eliminar esta suscripción? + Se requiere la cantidad + Se requiere un intervalo de tiempo + Establezca la cantidad de pagos a realizar o la fecha de finalización + No se pudo guardar la suscripción + Suscripción guardada correctamente + Eliminando suscripción + No se pudo pausar la suscripción + Suscripción pausada con éxito + No se pudo reiniciar la suscripción + Suscripción reiniciada correctamente \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml index 36fd9d0e81..807e91b285 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml @@ -1,6 +1,6 @@ - 定期購読の繰り返し + 繰り返し カスタム金額: 500 sats @@ -14,4 +14,16 @@ 作る 支払い まで支払う + サブスクリプションを削除する + このサブスクリプションを削除してもよろしいですか + 金額が必要です + 時間間隔が必要です + お支払い回数または終了日を設定してください + サブスクリプションの保存に失敗しました + サブスクリプションを正常に保存しました + サブスクリプションの削除 + サブスクリプションの一時停止に失敗しました + サブスクリプションを正常に一時停止しました + サブスクリプションの再開に失敗しました + サブスクリプションが正常に再開されました \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml index 8b9ee8715c..5b9855b400 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml @@ -1,6 +1,6 @@ - 定期訂閱 + 再次發生的 數量 自定義金額: 500 sats @@ -14,4 +14,16 @@ 製作 付款 支付至 + 刪除訂閱 + 您確定要刪除此訂閱嗎 + 金額為必填項 + 需要時間間隔 + 請設置付款次數或結束日期 + 無法保存訂閱 + 已成功保存訂閱 + 刪除訂閱 + 未能暫停訂閱 + 已成功暫停訂閱 + 重新開始訂閱失敗 + 成功重啟訂閱 \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index 67653d3647..4fee3c2a61 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - RECURRING SUBSCRIPTION + RECURRING Amount 500 sats 1000 sats @@ -14,4 +14,16 @@ Make Payments Pay until + Delete subscription + Are you sure you want to delete this subscription + Amount is required + Time Interval is required + Please set either the number of payments to make or end date + Failed to save subscription + Saved subscription successfully + Deleting subscription + Failed to pause subscription + Successfully paused subscription + Failed to restart subscription + Successfully restarted subscription \ No newline at end of file From 0b781fe9d77e4212d2d00e19fdb66028435de869 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 20:20:27 +0200 Subject: [PATCH 11/32] Pass contactId when navigating --- .../navigators/detail/EditContactNavigatorImpl.kt | 2 +- .../chat/sphinx/wrapper_common/dashboard/ContactId.kt | 4 +++- .../subscription/navigation/ToSubscriptionDetail.kt | 8 ++++++-- .../chat/sphinx/subscription/ui/SubscriptionViewModel.kt | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt index 4f8d43993a..d0e5d63a8a 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/EditContactNavigatorImpl.kt @@ -27,7 +27,7 @@ internal class EditContactNavigatorImpl @Inject constructor( override suspend fun toSubscribeDetailScreen(contactId: ContactId) { navigationDriver.submitNavigationRequest( - ToSubscriptionDetail() + ToSubscriptionDetail(contactId) ) } } diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/dashboard/ContactId.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/dashboard/ContactId.kt index 1e7f7e9d72..6516f1f63d 100644 --- a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/dashboard/ContactId.kt +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/dashboard/ContactId.kt @@ -9,7 +9,9 @@ inline fun Long.toContactId(): ContactId? = } @JvmInline -value class ContactId(override val value: Long): DashboardItemId { +value class ContactId( + override val value: Long + ): DashboardItemId { companion object { const val NULL_CONTACT_ID = Long.MAX_VALUE } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt index e758ea7363..64cb36f664 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/navigation/ToSubscriptionDetail.kt @@ -3,13 +3,17 @@ package chat.sphinx.subscription.navigation import androidx.navigation.NavController import chat.sphinx.detail_resources.DetailNavOptions import chat.sphinx.subscription.R +import chat.sphinx.subscription.ui.SubscriptionFragmentArgs +import chat.sphinx.wrapper_common.dashboard.ContactId import io.matthewnelson.concept_navigation.NavigationRequest -class ToSubscriptionDetail(): NavigationRequest() { +class ToSubscriptionDetail( + private val contactId: ContactId +): NavigationRequest() { override fun navigate(controller: NavController) { controller.navigate( R.id.subscription_nav_graph, - null, + SubscriptionFragmentArgs.Builder(contactId.value).build().toBundle(), DetailNavOptions.default.apply { setEnterAnim(io.matthewnelson.android_feature_navigation.R.anim.slide_in_left) setPopExitAnim(io.matthewnelson.android_feature_navigation.R.anim.slide_out_right) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 81adb50bd2..b4f0f4e2e2 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -82,7 +82,9 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } - subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + subscriptionRepository.getSubscriptionByContactId( + ContactId(args.argContactId) + ).firstOrNull().let { subscription -> val loadResponse = if (subscription == null) { subscriptionRepository.createSubscription( amount = amount, From 52246bee431c466e861ad3726629a43f0c7e56b9 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 20:21:46 +0200 Subject: [PATCH 12/32] Add migration for the subscriptionDbo --- .../sphinx/concept_coredb/SphinxDatabase.sq | 7 +++++- .../src/main/sqldelight/migrations/2.sqm | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/migrations/2.sqm diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq index dff1f45956..91d474f614 100644 --- a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq @@ -759,7 +759,12 @@ WHERE id = ?; subscriptionGetByContactId: SELECT * FROM subscriptionDbo -WHERE contact_id = ?; +WHERE ended = 0 AND contact_id = ?; + +activeSubscriptionGetByContactId: +SELECT * +FROM subscriptionDbo +WHERE ended = 0 AND contact_id = ?; subscriptionGetByChatId: SELECT * diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/migrations/2.sqm b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/migrations/2.sqm new file mode 100644 index 0000000000..ad5eee5502 --- /dev/null +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/migrations/2.sqm @@ -0,0 +1,23 @@ +import chat.sphinx.wrapper_common.DateTime; +import chat.sphinx.wrapper_common.dashboard.ChatId; +import chat.sphinx.wrapper_common.dashboard.ContactId; +import chat.sphinx.wrapper_common.lightning.Sat; +import chat.sphinx.wrapper_common.subscription.Cron; +import chat.sphinx.wrapper_common.subscription.EndNumber; +import chat.sphinx.wrapper_common.subscription.SubscriptionCount; +import chat.sphinx.wrapper_common.subscription.SubscriptionId; + +CREATE TABLE subscriptionDbo( + id INTEGER AS SubscriptionId NOT NULL PRIMARY KEY, + cron TEXT AS Cron NOT NULL, + amount INTEGER AS Sat NOT NULL, + end_number INTEGER AS EndNumber, + count INTEGER AS SubscriptionCount NOT NULL, + end_date INTEGER AS DateTime, + ended INTEGER AS Boolean DEFAULT 0 NOT NULL, + paused INTEGER AS Boolean DEFAULT 0 NOT NULL, + created_at INTEGER AS DateTime NOT NULL, + updated_at INTEGER AS DateTime NOT NULL, + chat_id INTEGER AS ChatId NOT NULL, + contact_id INTEGER AS ContactId NOT NULL +); From c74e332a40f33becdd8d47fb72be31fda9bc8188 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 22:13:26 +0200 Subject: [PATCH 13/32] Create subscription functionality --- .../SubscriptionRepository.kt | 2 +- .../SphinxRepositoryAndroid.kt | 6 ++- .../feature_repository/SphinxRepository.kt | 43 ++++++++++++++++++- .../feature_repository/util/Extensions.kt | 25 ++++++++++- .../NetworkQuerySubscription.kt | 3 +- .../model/PostSubscriptionDto.kt | 13 ++++++ .../NetworkQuerySubscriptionImpl.kt | 7 +-- .../java/chat/sphinx/di/RepositoryModule.kt | 3 ++ 8 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PostSubscriptionDto.kt diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt index 35d941ceea..b3a35b4a4c 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -23,7 +23,7 @@ interface SubscriptionRepository { chatId: ChatId?, endDate: String?, endNumber: EndNumber? - ): Response + ): Response suspend fun updateSubscription( subscription: Subscription diff --git a/sphinx/application/data/features/feature-repository-android/src/main/java/chat/sphinx/feature_repository_android/SphinxRepositoryAndroid.kt b/sphinx/application/data/features/feature-repository-android/src/main/java/chat/sphinx/feature_repository_android/SphinxRepositoryAndroid.kt index bc052ca2d1..5b798a3bd6 100644 --- a/sphinx/application/data/features/feature-repository-android/src/main/java/chat/sphinx/feature_repository_android/SphinxRepositoryAndroid.kt +++ b/sphinx/application/data/features/feature-repository-android/src/main/java/chat/sphinx/feature_repository_android/SphinxRepositoryAndroid.kt @@ -5,15 +5,15 @@ import androidx.paging.PagingSource import chat.sphinx.concept_coredb.CoreDB import chat.sphinx.concept_crypto_rsa.RSA import chat.sphinx.concept_meme_server.MemeServerTokenHandler -import chat.sphinx.concept_network_query_meme_server.NetworkQueryMemeServer import chat.sphinx.concept_network_query_chat.NetworkQueryChat import chat.sphinx.concept_network_query_contact.NetworkQueryContact import chat.sphinx.concept_network_query_invite.NetworkQueryInvite import chat.sphinx.concept_network_query_lightning.NetworkQueryLightning +import chat.sphinx.concept_network_query_meme_server.NetworkQueryMemeServer import chat.sphinx.concept_network_query_message.NetworkQueryMessage +import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription import chat.sphinx.concept_network_query_verify_external.NetworkQueryAuthorizeExternal import chat.sphinx.concept_paging.PageSourceWrapper -import chat.sphinx.concept_relay.RelayDataHandler import chat.sphinx.concept_repository_dashboard.DashboardItem import chat.sphinx.concept_repository_dashboard_android.RepositoryDashboardAndroid import chat.sphinx.concept_socket_io.SocketIOManager @@ -49,6 +49,7 @@ class SphinxRepositoryAndroid( networkQueryMessage: NetworkQueryMessage, networkQueryInvite: NetworkQueryInvite, networkQueryAuthorizeExternal: NetworkQueryAuthorizeExternal, + networkQuerySubscription: NetworkQuerySubscription, rsa: RSA, socketIOManager: SocketIOManager, LOG: SphinxLogger, @@ -68,6 +69,7 @@ class SphinxRepositoryAndroid( networkQueryMessage, networkQueryInvite, networkQueryAuthorizeExternal, + networkQuerySubscription, rsa, socketIOManager, LOG, diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index b7bc323c1a..54b2e00fd1 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -20,6 +20,9 @@ import chat.sphinx.concept_network_query_message.NetworkQueryMessage import chat.sphinx.concept_network_query_message.model.MessageDto import chat.sphinx.concept_network_query_message.model.PostMessageDto import chat.sphinx.concept_network_query_message.model.PostPaymentDto +import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription +import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto +import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_query_verify_external.NetworkQueryAuthorizeExternal import chat.sphinx.concept_repository_chat.ChatRepository import chat.sphinx.concept_repository_chat.model.CreateTribe @@ -114,6 +117,7 @@ abstract class SphinxRepository( private val networkQueryMessage: NetworkQueryMessage, private val networkQueryInvite: NetworkQueryInvite, private val networkQueryAuthorizeExternal: NetworkQueryAuthorizeExternal, + private val networkQuerySubscription: NetworkQuerySubscription, private val rsa: RSA, private val socketIOManager: SocketIOManager, protected val LOG: SphinxLogger, @@ -3583,9 +3587,44 @@ abstract class SphinxRepository( chatId: ChatId?, endDate: String?, endNumber: EndNumber? - ): Response { - var response: Response = Response.Error(ResponseError(("Failed to create subscription"))) + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to create subscription"))) + + applicationScope.launch(mainImmediate) { + + networkQuerySubscription.postSubscription( + PostSubscriptionDto( + amount = amount.value, + contact_id = contactId.value, + chat_id = chatId?.value, + interval = interval, + end_number = endNumber?.value, + end_date = endDate + ) + ).collect { loadResponse -> + when (loadResponse) { + LoadResponse.Loading -> { } + is Response.Error -> { + response = loadResponse + } + is Response.Success -> { + response = loadResponse + val queries = coreDB.getSphinxDatabaseQueries() + subscriptionLock.withLock { + withContext(io) { + queries.transaction { + upsertSubscription( + loadResponse.value, + queries + ) + } + } + } + } + } + } + }.join() return response } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt index bd0ab178d2..9b3fb8557c 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt @@ -6,11 +6,12 @@ import chat.sphinx.concept_network_query_contact.model.ContactDto import chat.sphinx.concept_network_query_invite.model.InviteDto import chat.sphinx.concept_network_query_lightning.model.balance.BalanceDto import chat.sphinx.concept_network_query_message.model.MessageDto +import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.conceptcoredb.SphinxDatabaseQueries import chat.sphinx.wrapper_chat.* import chat.sphinx.wrapper_common.* -import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.chat.ChatUUID +import chat.sphinx.wrapper_common.dashboard.ChatId import chat.sphinx.wrapper_common.dashboard.ContactId import chat.sphinx.wrapper_common.dashboard.InviteId import chat.sphinx.wrapper_common.invite.InviteStatus @@ -20,6 +21,10 @@ import chat.sphinx.wrapper_common.invite.toInviteStatus import chat.sphinx.wrapper_common.lightning.* import chat.sphinx.wrapper_common.message.MessageId import chat.sphinx.wrapper_common.message.toMessageUUID +import chat.sphinx.wrapper_common.subscription.Cron +import chat.sphinx.wrapper_common.subscription.EndNumber +import chat.sphinx.wrapper_common.subscription.SubscriptionCount +import chat.sphinx.wrapper_common.subscription.SubscriptionId import chat.sphinx.wrapper_contact.* import chat.sphinx.wrapper_invite.InviteString import chat.sphinx.wrapper_lightning.NodeBalance @@ -421,3 +426,21 @@ inline fun TransactionCallbacks.deleteMessageById( queries.messageDeleteById(messageId) queries.messageMediaDeleteById(messageId) } + +@Suppress("NOTHING_TO_INLINE") +inline fun TransactionCallbacks.upsertSubscription(subscriptionDto: SubscriptionDto, queries: SphinxDatabaseQueries) { + queries.subscriptionUpsert( + id = SubscriptionId(subscriptionDto.id), + amount = Sat(subscriptionDto.amount), + contact_id = ContactId(subscriptionDto.contact_id), + chat_id = ChatId(subscriptionDto.chat_id), + count = SubscriptionCount(subscriptionDto.count.toLong()), + cron = Cron(subscriptionDto.cron), + end_date = subscriptionDto.end_date?.toDateTime(), + end_number = subscriptionDto.end_number?.let { EndNumber(it.toLong()) }, + created_at = subscriptionDto.created_at.toDateTime(), + updated_at = subscriptionDto.updated_at.toDateTime(), + ended = subscriptionDto.ended == 1, + paused = subscriptionDto.paused == 1, + ) +} diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt index 2296b893d7..5b5923351d 100644 --- a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt @@ -1,5 +1,6 @@ package chat.sphinx.concept_network_query_subscription +import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.kotlin_response.LoadResponse import chat.sphinx.kotlin_response.ResponseError @@ -54,7 +55,7 @@ abstract class NetworkQuerySubscription { //////////// // app.post('/subscriptions', subcriptions.createSubscription) abstract fun postSubscription( - subscriptionDto: SubscriptionDto, + postSubscriptionDto: PostSubscriptionDto, relayData: Pair? = null ): Flow> diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PostSubscriptionDto.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PostSubscriptionDto.kt new file mode 100644 index 0000000000..8f4c2c1fde --- /dev/null +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PostSubscriptionDto.kt @@ -0,0 +1,13 @@ +package chat.sphinx.concept_network_query_subscription.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class PostSubscriptionDto( + val amount: Long, + val contact_id: Long, + val chat_id: Long?, + val end_number: Long?, + val end_date: String?, + val interval: String +) diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt index 45d4e1cf77..ac3427b77c 100644 --- a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt @@ -1,6 +1,7 @@ package chat.sphinx.feature_network_query_subscription import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription +import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_relay_call.NetworkRelayCall import chat.sphinx.feature_network_query_subscription.model.GetSubscriptionsRelayResponse @@ -113,14 +114,14 @@ class NetworkQuerySubscriptionImpl( /// POST /// //////////// override fun postSubscription( - subscriptionDto: SubscriptionDto, + postSubscriptionDto: PostSubscriptionDto, relayData: Pair? ): Flow> = networkRelayCall.relayPost( responseJsonClass = SubscriptionRelayResponse::class.java, relayEndpoint = ENDPOINT_SUBSCRIPTIONS, - requestBodyJsonClass = SubscriptionDto::class.java, - requestBody = subscriptionDto, + requestBodyJsonClass = PostSubscriptionDto::class.java, + requestBody = postSubscriptionDto, relayData = relayData ) diff --git a/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt b/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt index 5ff5ff3a9f..2283d28417 100644 --- a/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt +++ b/sphinx/application/sphinx/src/main/java/chat/sphinx/di/RepositoryModule.kt @@ -9,6 +9,7 @@ import chat.sphinx.concept_network_query_invite.NetworkQueryInvite import chat.sphinx.concept_network_query_lightning.NetworkQueryLightning import chat.sphinx.concept_network_query_meme_server.NetworkQueryMemeServer import chat.sphinx.concept_network_query_message.NetworkQueryMessage +import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription import chat.sphinx.concept_network_query_verify_external.NetworkQueryAuthorizeExternal import chat.sphinx.concept_repository_chat.ChatRepository import chat.sphinx.concept_repository_contact.ContactRepository @@ -126,6 +127,7 @@ object RepositoryModule { networkQueryMessage: NetworkQueryMessage, networkQueryInvite: NetworkQueryInvite, networkQueryAuthorizeExternal: NetworkQueryAuthorizeExternal, + networkQuerySubscription: NetworkQuerySubscription, socketIOManager: SocketIOManager, rsa: RSA, sphinxLogger: SphinxLogger, @@ -146,6 +148,7 @@ object RepositoryModule { networkQueryMessage, networkQueryInvite, networkQueryAuthorizeExternal, + networkQuerySubscription, rsa, socketIOManager, sphinxLogger, From 14acacfeb8d08460cb886219c2f72317890785f1 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Tue, 31 Aug 2021 22:24:13 +0200 Subject: [PATCH 14/32] Add remaining SphinxRepository subscription functions --- .../SubscriptionRepository.kt | 6 +- .../feature_repository/SphinxRepository.kt | 90 ++++++++++++++++++- .../feature_repository/util/Extensions.kt | 8 ++ 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt index b3a35b4a4c..d980983237 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -27,15 +27,15 @@ interface SubscriptionRepository { suspend fun updateSubscription( subscription: Subscription - ): Response + ): Response suspend fun restartSubscription( subscriptionId: SubscriptionId - ): Response + ): Response suspend fun pauseSubscription( subscriptionId: SubscriptionId - ): Response + ): Response suspend fun deleteSubscription( subscriptionId: SubscriptionId diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 54b2e00fd1..2104907080 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -3638,16 +3638,73 @@ abstract class SphinxRepository( override suspend fun restartSubscription( subscriptionId: SubscriptionId - ): Response { - var response: Response = Response.Error(ResponseError(("Failed to restart subscription"))) + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to restart subscription"))) + + applicationScope.launch(mainImmediate) { + + networkQuerySubscription.putRestartSubscription( + subscriptionId + ).collect { loadResponse -> + when (loadResponse) { + LoadResponse.Loading -> { } + is Response.Error -> { + response = loadResponse + } + is Response.Success -> { + response = loadResponse + val queries = coreDB.getSphinxDatabaseQueries() + subscriptionLock.withLock { + withContext(io) { + queries.transaction { + upsertSubscription( + loadResponse.value, + queries + ) + } + } + } + } + } + } + }.join() return response } override suspend fun pauseSubscription( subscriptionId: SubscriptionId - ): Response { - var response: Response = Response.Error(ResponseError(("Failed to pause subscription"))) + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to pause subscription"))) + + applicationScope.launch(mainImmediate) { + + networkQuerySubscription.putPauseSubscription( + subscriptionId + ).collect { loadResponse -> + when (loadResponse) { + LoadResponse.Loading -> { } + is Response.Error -> { + response = loadResponse + } + is Response.Success -> { + response = loadResponse + val queries = coreDB.getSphinxDatabaseQueries() + + subscriptionLock.withLock { + withContext(io) { + queries.transaction { + upsertSubscription( + loadResponse.value, + queries + ) + } + } + } + } + } + } + }.join() return response } @@ -3657,6 +3714,31 @@ abstract class SphinxRepository( ): Response { var response: Response = Response.Error(ResponseError(("Failed to delete subscription"))) + applicationScope.launch(mainImmediate) { + networkQuerySubscription.deleteSubscription( + subscriptionId + ).collect { loadResponse -> + when (loadResponse) { + LoadResponse.Loading -> { } + is Response.Error -> { + response = loadResponse + } + is Response.Success -> { + response = loadResponse + val queries = coreDB.getSphinxDatabaseQueries() + + subscriptionLock.withLock { + withContext(io) { + queries.transaction { + deleteSubscriptionById(subscriptionId, queries) + } + } + } + } + } + } + }.join() + return response } } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt index 9b3fb8557c..70fb6a31d4 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt @@ -444,3 +444,11 @@ inline fun TransactionCallbacks.upsertSubscription(subscriptionDto: Subscription paused = subscriptionDto.paused == 1, ) } + +@Suppress("NOTHING_TO_INLINE") +inline fun TransactionCallbacks.deleteSubscriptionById( + subscriptionId: SubscriptionId, + queries: SphinxDatabaseQueries +) { + queries.subscriptionDeleteById(subscriptionId) +} \ No newline at end of file From 1831b9b1b9e71ae7e68ace107cfa03c8a0812749 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Wed, 1 Sep 2021 00:16:30 +0200 Subject: [PATCH 15/32] Functionality to update subscription --- .../chat/sphinx/wrapper_common/DateTime.kt | 16 +++ .../SubscriptionRepository.kt | 12 +- .../feature_repository/SphinxRepository.kt | 61 +++++++++- .../feature_repository/util/Extensions.kt | 4 +- .../NetworkQuerySubscription.kt | 2 + .../model/PutSubscriptionDto.kt | 13 +++ .../NetworkQuerySubscriptionImpl.kt | 10 +- .../subscription/ui/SubscriptionFragment.kt | 24 ++-- .../subscription/ui/SubscriptionViewModel.kt | 109 +++++++++++------- .../subscription/ui/SubscriptionViewState.kt | 2 + .../main/res/layout/fragment_subscription.xml | 1 - .../src/main/res/values/strings.xml | 2 + 12 files changed, 192 insertions(+), 64 deletions(-) create mode 100644 sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PutSubscriptionDto.kt diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt index 87f1e83951..b40eed94fd 100644 --- a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt @@ -95,6 +95,7 @@ value class DateTime(val value: Date) { private const val FORMAT_EEE_DD = "EEE dd" private const val FORMAT_EEE_MM_DD_H_MM_A = "EEE MMM dd, h:mm a" private const val FORMAT_DD_MMM_HH_MM = "dd MMM, HH:mm" + private const val FORMAT_MMM_DD_YYYY = "MMM dd, yyyy" private const val SIX_DAYS_IN_MILLISECONDS = 518_400_000L @@ -241,6 +242,21 @@ value class DateTime(val value: Date) { formatEEEdd = it } } + + @Volatile + private var formatMMMddyyyy: SimpleDateFormat? = null + fun getFormatMMMddyyyy(): SimpleDateFormat = + formatMMMddyyyy?.also { + it.timeZone = TimeZone.getDefault() + } ?: synchronized(this) { + formatMMMddyyyy?.also { + it.timeZone = TimeZone.getDefault() + } ?: SimpleDateFormat(FORMAT_MMM_DD_YYYY, Locale.getDefault()) + .also { + it.timeZone = TimeZone.getDefault() + formatMMMddyyyy = it + } + } } override fun toString(): String { diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt index d980983237..66be916095 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -16,6 +16,10 @@ interface SubscriptionRepository { contactId: ContactId ): Flow + suspend fun getActiveSubscriptionByContactId( + contactId: ContactId + ): Flow + suspend fun createSubscription( amount: Sat, interval: String, @@ -26,7 +30,13 @@ interface SubscriptionRepository { ): Response suspend fun updateSubscription( - subscription: Subscription + id: SubscriptionId, + amount: Sat, + interval: String, + contactId: ContactId, + chatId: ChatId?, + endDate: String?, + endNumber: EndNumber? ): Response suspend fun restartSubscription( diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 2104907080..46342c0d48 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -22,6 +22,7 @@ import chat.sphinx.concept_network_query_message.model.PostMessageDto import chat.sphinx.concept_network_query_message.model.PostPaymentDto import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto +import chat.sphinx.concept_network_query_subscription.model.PutSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_query_verify_external.NetworkQueryAuthorizeExternal import chat.sphinx.concept_repository_chat.ChatRepository @@ -3580,6 +3581,16 @@ abstract class SphinxRepository( ) } + override suspend fun getActiveSubscriptionByContactId(contactId: ContactId): Flow = flow { + emitAll( + coreDB.getSphinxDatabaseQueries().activeSubscriptionGetByContactId(contactId) + .asFlow() + .mapToOneOrNull(io) + .map { it?.let { subscriptionDboPresenterMapper.mapFrom(it) } } + .distinctUntilChanged() + ) + } + override suspend fun createSubscription( amount: Sat, interval: String, @@ -3591,7 +3602,6 @@ abstract class SphinxRepository( var response: Response = Response.Error(ResponseError(("Failed to create subscription"))) applicationScope.launch(mainImmediate) { - networkQuerySubscription.postSubscription( PostSubscriptionDto( amount = amount.value, @@ -3629,9 +3639,52 @@ abstract class SphinxRepository( } override suspend fun updateSubscription( - subscription: Subscription - ): Response { - var response: Response = Response.Error(ResponseError(("Failed to update subsccription"))) + id: SubscriptionId, + amount: Sat, + interval: String, + contactId: ContactId, + chatId: ChatId?, + endDate: String?, + endNumber: EndNumber? + ): Response { + var response: Response = Response.Error(ResponseError(("Failed to update subscription"))) + + applicationScope.launch(mainImmediate) { + + networkQuerySubscription.putSubscription( + id, + PutSubscriptionDto( + amount = amount.value, + contact_id = contactId.value, + chat_id = chatId?.value, + interval = interval, + end_number = endNumber?.value, + end_date = endDate + ) + ).collect { loadResponse -> + when (loadResponse) { + LoadResponse.Loading -> { } + is Response.Error -> { + response = loadResponse + } + is Response.Success -> { + response = loadResponse + val queries = coreDB.getSphinxDatabaseQueries() + + subscriptionLock.withLock { + withContext(io) { + queries.transaction { + upsertSubscription( + loadResponse.value, + queries + ) + } + } + } + } + } + } + }.join() return response } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt index 70fb6a31d4..a67eb4427f 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt @@ -440,8 +440,8 @@ inline fun TransactionCallbacks.upsertSubscription(subscriptionDto: Subscription end_number = subscriptionDto.end_number?.let { EndNumber(it.toLong()) }, created_at = subscriptionDto.created_at.toDateTime(), updated_at = subscriptionDto.updated_at.toDateTime(), - ended = subscriptionDto.ended == 1, - paused = subscriptionDto.paused == 1, + ended = subscriptionDto.endedActual, + paused = subscriptionDto.pausedActual, ) } diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt index 5b5923351d..1dca291a4d 100644 --- a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt @@ -1,6 +1,7 @@ package chat.sphinx.concept_network_query_subscription import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto +import chat.sphinx.concept_network_query_subscription.model.PutSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.kotlin_response.LoadResponse import chat.sphinx.kotlin_response.ResponseError @@ -37,6 +38,7 @@ abstract class NetworkQuerySubscription { // app.put('/subscription/:id/restart', subcriptions.restartSubscription) abstract fun putSubscription( subscriptionId: SubscriptionId, + putSubscriptionDto: PutSubscriptionDto, relayData: Pair? = null ): Flow> diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PutSubscriptionDto.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PutSubscriptionDto.kt new file mode 100644 index 0000000000..31091dcba5 --- /dev/null +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/model/PutSubscriptionDto.kt @@ -0,0 +1,13 @@ +package chat.sphinx.concept_network_query_subscription.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class PutSubscriptionDto( + val amount: Long, + val contact_id: Long, + val chat_id: Long?, + val end_number: Long?, + val end_date: String?, + val interval: String +) diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt index ac3427b77c..ea63431f90 100644 --- a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt @@ -2,6 +2,7 @@ package chat.sphinx.feature_network_query_subscription import chat.sphinx.concept_network_query_subscription.NetworkQuerySubscription import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto +import chat.sphinx.concept_network_query_subscription.model.PutSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_relay_call.NetworkRelayCall import chat.sphinx.feature_network_query_subscription.model.GetSubscriptionsRelayResponse @@ -70,19 +71,16 @@ class NetworkQuerySubscriptionImpl( /////////// /// PUT /// /////////// -// app.put('/subscription/:id', subcriptions.editSubscription) -// app.put('/subscription/:id/pause', subcriptions.pauseSubscription) -// app.put('/subscription/:id/restart', subcriptions.restartSubscription) - override fun putSubscription( subscriptionId: SubscriptionId, + putSubscriptionDto: PutSubscriptionDto, relayData: Pair? ): Flow> = networkRelayCall.relayPut( responseJsonClass = SubscriptionRelayResponse::class.java, relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}", - requestBodyJsonClass = Map::class.java, - requestBody = mapOf(Pair("", "")), + requestBodyJsonClass = PutSubscriptionDto::class.java, + requestBody = putSubscriptionDto, relayData = relayData ) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index a749676553..e0b38112b3 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -11,7 +11,9 @@ import chat.sphinx.insetter_activity.InsetterActivity import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.subscription.R import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding +import chat.sphinx.wrapper_common.DateTime import chat.sphinx.wrapper_common.lightning.Sat +import chat.sphinx.wrapper_common.toDateTime import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment import io.matthewnelson.android_feature_screens.util.gone @@ -32,6 +34,8 @@ internal class SubscriptionFragment: SideEffectFragment< override val viewModel: SubscriptionViewModel by viewModels() override val binding: FragmentSubscriptionBinding by viewBinding(FragmentSubscriptionBinding::bind) + private val calendar = Calendar.getInstance() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -62,8 +66,6 @@ internal class SubscriptionFragment: SideEffectFragment< } } - val calendar = Calendar.getInstance() - editTextPayUntil.setOnClickListener { this@SubscriptionFragment.context?.let { context -> val datePickerDialog = DatePickerDialog( @@ -133,7 +135,7 @@ internal class SubscriptionFragment: SideEffectFragment< } var endNumber: Long? = null - val endDate: Date? = when (radioGroupEndRule.checkedRadioButtonId) { + val endDate: DateTime? = when (radioGroupEndRule.checkedRadioButtonId) { R.id.radio_button_make -> { editTextMakeQuantity.text?.toString()?.let { endNumber = it.toLongOrNull() @@ -141,8 +143,7 @@ internal class SubscriptionFragment: SideEffectFragment< null } R.id.radio_button_until -> { - // TODO: Load editTextPayUntil.text into date - Calendar.getInstance().time + calendar.timeInMillis.toDateTime() } else -> null } @@ -162,12 +163,18 @@ internal class SubscriptionFragment: SideEffectFragment< override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { when(viewState) { - SubscriptionViewState.Idle -> { + is SubscriptionViewState.Idle -> { // Setup for new subscription binding.apply { progressBarSubscriptionSave.gone textViewDetailSubscriptionDelete.gone layoutConstraintSubscriptionEnablement.gone + buttonSave.text = getString(R.string.subscribe) + } + } + is SubscriptionViewState.CreatedSubscription -> { + lifecycleScope.launch { + viewModel.navigator.closeDetailScreen() } } is SubscriptionViewState.SubscriptionLoaded -> { @@ -175,6 +182,7 @@ internal class SubscriptionFragment: SideEffectFragment< progressBarSubscriptionSave.gone textViewDetailSubscriptionDelete.visible layoutConstraintSubscriptionEnablement.visible + buttonSave.text = getString(R.string.update_subscription) switchSubscriptionEnablement.isChecked = !viewState.subscription.ended && !viewState.subscription.paused @@ -197,11 +205,11 @@ internal class SubscriptionFragment: SideEffectFragment< // Populate Time Interval when { - viewState.subscription.cron.value.startsWith("* * *") -> { + viewState.subscription.cron.value.endsWith("* * *") -> { // Daily... radioButtonDaily.isChecked = true } - viewState.subscription.cron.value.startsWith("* *") -> { + viewState.subscription.cron.value.endsWith("* *") -> { // Monthly radioButtonMonthly.isChecked = true } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index b4f0f4e2e2..ac435b37bc 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -9,9 +9,9 @@ import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.kotlin_response.Response import chat.sphinx.subscription.R import chat.sphinx.subscription.navigation.SubscriptionNavigator +import chat.sphinx.wrapper_common.DateTime import chat.sphinx.wrapper_common.dashboard.ContactId import chat.sphinx.wrapper_common.lightning.Sat -import chat.sphinx.wrapper_common.subscription.Cron import chat.sphinx.wrapper_common.subscription.EndNumber import chat.sphinx.wrapper_subscription.Subscription import dagger.hilt.android.lifecycle.HiltViewModel @@ -42,15 +42,28 @@ internal class SubscriptionViewModel @Inject constructor( private val args: SubscriptionFragmentArgs by savedStateHandle.navArgs() fun initSubscription() { - updateViewState( - SubscriptionViewState.Idle - ) + viewModelScope.launch(mainImmediate) { + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + if (subscription == null) { + updateViewState( + SubscriptionViewState.Idle + ) + } else { + updateViewState( + SubscriptionViewState.SubscriptionLoaded( + subscription + ) + ) + } + } + } + } fun saveSubscription( amount: Sat?, interval: String?, - endDate: Date?, + endDate: DateTime?, endNumber: Long? ) { viewModelScope.launch(mainImmediate) { @@ -82,7 +95,7 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } - subscriptionRepository.getSubscriptionByContactId( + subscriptionRepository.getActiveSubscriptionByContactId( ContactId(args.argContactId) ).firstOrNull().let { subscription -> val loadResponse = if (subscription == null) { @@ -91,25 +104,18 @@ internal class SubscriptionViewModel @Inject constructor( interval = interval, contactId = ContactId(args.argContactId), chatId = null, - endDate = null, // TODO: Fix this + endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it) }, endNumber = endNumber?.let { EndNumber(it) } ) } else { subscriptionRepository.updateSubscription( - Subscription( - id = subscription.id, - cron = Cron(interval), - amount = amount, - end_number = subscription.end_number, - count = subscription.count, - end_date = subscription.end_date, - ended = subscription.ended, - paused = subscription.paused, - created_at = subscription.created_at, - updated_at = subscription.updated_at, - chat_id = subscription.chat_id, - contact_id = subscription.contact_id - ) + id = subscription.id, + amount = amount, + interval = interval, + contactId = ContactId(args.argContactId), + chatId = subscription.chat_id, + endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it.value) }, + endNumber = endNumber?.let { EndNumber(it) } ) } @@ -120,8 +126,8 @@ internal class SubscriptionViewModel @Inject constructor( ) } is Response.Success -> { - submitSideEffect( - SubscriptionSideEffect.Notify(app.getString(R.string.saved_subscription_successfully)) + updateViewState( + SubscriptionViewState.CreatedSubscription ) } } @@ -145,7 +151,7 @@ internal class SubscriptionViewModel @Inject constructor( fun pauseSubscription() { viewModelScope.launch(mainImmediate) { - subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> if (subscription == null) { submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_pause_subscription)) @@ -161,45 +167,64 @@ internal class SubscriptionViewModel @Inject constructor( submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.successfully_paused_subscription)) ) - // TODO: Set subscription to viewState... -// updateViewState( -// SubscriptionViewState.Subscription( -// -// ) -// ) + updateViewState( + SubscriptionViewState.SubscriptionLoaded( + Subscription( + id = subscription.id, + subscription.cron, + subscription.amount, + subscription.end_number, + subscription.count, + subscription.end_date, + subscription.ended, + paused = true, + subscription.created_at, + subscription.updated_at, + subscription.chat_id, + subscription.contact_id + ) + ) + ) } } } } - - } } fun restartSubscription() { viewModelScope.launch(mainImmediate) { - subscriptionRepository.getSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> if (subscription == null) { submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_restart_subscription)) ) } else { - when (subscriptionRepository.pauseSubscription(subscription.id)) { + when (subscriptionRepository.restartSubscription(subscription.id)) { is Response.Error -> { submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_restart_subscription)) ) } is Response.Success -> { - submitSideEffect( - SubscriptionSideEffect.Notify(app.getString(R.string.successfully_restarted_subscription)) + updateViewState( + SubscriptionViewState.SubscriptionLoaded( + Subscription( + id = subscription.id, + subscription.cron, + subscription.amount, + subscription.end_number, + subscription.count, + subscription.end_date, + subscription.ended, + paused = false, + subscription.created_at, + subscription.updated_at, + subscription.chat_id, + subscription.contact_id + ) + ) ) - // TODO: Set subscription to viewState -// updateViewState( -// SubscriptionViewState.Subscription( -// -// ) -// ) } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index 1a04d913d3..cb62c91a62 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -6,6 +6,8 @@ import io.matthewnelson.concept_views.viewstate.ViewState internal sealed class SubscriptionViewState: ViewState() { object Idle: SubscriptionViewState() + object CreatedSubscription: SubscriptionViewState() + class SubscriptionLoaded( val subscription: Subscription ) : SubscriptionViewState() diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index 8820aff6ea..f828d9d326 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -415,7 +415,6 @@ style="@style/RoundedBlueButton" android:layout_width="match_parent" android:layout_height="match_parent" - android:text="Subscribe" android:textSize="@dimen/default_header_text_size" android:textColor="@android:color/white" android:background="@drawable/button_background_white_rad_48" diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index 4fee3c2a61..b95c1de95d 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -26,4 +26,6 @@ Successfully paused subscription Failed to restart subscription Successfully restarted subscription + Subscribe + Update Subscription \ No newline at end of file From cc84e55ce861ad0088c79c6ddf5f542a140a1dd2 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Wed, 1 Sep 2021 00:29:57 +0200 Subject: [PATCH 16/32] Delete subscription And extract more strings --- .../NetworkQuerySubscription.kt | 3 --- .../NetworkQuerySubscriptionImpl.kt | 3 ++- .../model/DeleteSubscriptionRelayResponse.kt | 11 ++++++++ .../subscription/ui/SubscriptionFragment.kt | 2 +- .../subscription/ui/SubscriptionViewModel.kt | 25 ++++++++++++++++--- .../subscription/ui/SubscriptionViewState.kt | 2 +- .../src/main/res/values-es/strings.xml | 3 +++ .../src/main/res/values-ja/strings.xml | 3 +++ .../src/main/res/values-zh/strings.xml | 3 +++ .../src/main/res/values/strings.xml | 1 + 10 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/DeleteSubscriptionRelayResponse.kt diff --git a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt index 1dca291a4d..c1deb74ae1 100644 --- a/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt +++ b/sphinx/application/network/concepts/queries/concept-network-query-subscription/src/main/java/chat/sphinx/concept_network_query_subscription/NetworkQuerySubscription.kt @@ -33,9 +33,6 @@ abstract class NetworkQuerySubscription { /////////// /// PUT /// /////////// -// app.put('/subscription/:id', subcriptions.editSubscription) -// app.put('/subscription/:id/pause', subcriptions.pauseSubscription) -// app.put('/subscription/:id/restart', subcriptions.restartSubscription) abstract fun putSubscription( subscriptionId: SubscriptionId, putSubscriptionDto: PutSubscriptionDto, diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt index ea63431f90..485e3fff09 100644 --- a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/NetworkQuerySubscriptionImpl.kt @@ -5,6 +5,7 @@ import chat.sphinx.concept_network_query_subscription.model.PostSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.PutSubscriptionDto import chat.sphinx.concept_network_query_subscription.model.SubscriptionDto import chat.sphinx.concept_network_relay_call.NetworkRelayCall +import chat.sphinx.feature_network_query_subscription.model.DeleteSubscriptionRelayResponse import chat.sphinx.feature_network_query_subscription.model.GetSubscriptionsRelayResponse import chat.sphinx.feature_network_query_subscription.model.SubscriptionRelayResponse import chat.sphinx.kotlin_response.LoadResponse @@ -131,7 +132,7 @@ class NetworkQuerySubscriptionImpl( relayData: Pair? ): Flow> = networkRelayCall.relayDelete( - responseJsonClass = SubscriptionRelayResponse::class.java, + responseJsonClass = DeleteSubscriptionRelayResponse::class.java, relayEndpoint = "$ENDPOINT_SUBSCRIPTION/${subscriptionId.value}", requestBody = null, relayData = relayData diff --git a/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/DeleteSubscriptionRelayResponse.kt b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/DeleteSubscriptionRelayResponse.kt new file mode 100644 index 0000000000..ab812cf96e --- /dev/null +++ b/sphinx/application/network/features/queries/feature-network-query-subscription/src/main/java/chat/sphinx/feature_network_query_subscription/model/DeleteSubscriptionRelayResponse.kt @@ -0,0 +1,11 @@ +package chat.sphinx.feature_network_query_subscription.model + +import chat.sphinx.concept_network_relay_call.RelayResponse +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class DeleteSubscriptionRelayResponse( + override val success: Boolean, + override val response: Any?, + override val error: String? +): RelayResponse() diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index e0b38112b3..7e0000330e 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -172,7 +172,7 @@ internal class SubscriptionFragment: SideEffectFragment< buttonSave.text = getString(R.string.subscribe) } } - is SubscriptionViewState.CreatedSubscription -> { + is SubscriptionViewState.CloseSubscriptionDetail -> { lifecycleScope.launch { viewModel.navigator.closeDetailScreen() } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index ac435b37bc..b43b3cbb3b 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -127,7 +127,7 @@ internal class SubscriptionViewModel @Inject constructor( } is Response.Success -> { updateViewState( - SubscriptionViewState.CreatedSubscription + SubscriptionViewState.CloseSubscriptionDetail ) } } @@ -140,9 +140,26 @@ internal class SubscriptionViewModel @Inject constructor( submitSideEffect( SubscriptionSideEffect.AlertConfirmDeleteSubscription() { viewModelScope.launch(mainImmediate) { - submitSideEffect( - SubscriptionSideEffect.Notify(app.getString(R.string.deleting_subscription)) - ) + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + if (subscription == null) { + updateViewState( + SubscriptionViewState.CloseSubscriptionDetail + ) + } else { + when(subscriptionRepository.deleteSubscription(subscription.id)) { + is Response.Error -> { + submitSideEffect( + SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_delete_subscription)) + ) + } + is Response.Success -> { + updateViewState( + SubscriptionViewState.CloseSubscriptionDetail + ) + } + } + } + } } } ) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index cb62c91a62..b3b659af14 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -6,7 +6,7 @@ import io.matthewnelson.concept_views.viewstate.ViewState internal sealed class SubscriptionViewState: ViewState() { object Idle: SubscriptionViewState() - object CreatedSubscription: SubscriptionViewState() + object CloseSubscriptionDetail: SubscriptionViewState() class SubscriptionLoaded( val subscription: Subscription diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml index 2db901e283..4ced1f6c5f 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml @@ -26,4 +26,7 @@ Suscripción pausada con éxito No se pudo reiniciar la suscripción Suscripción reiniciada correctamente + Suscribir + Actualizar suscripción + No se pudo borrar la suscripción \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml index 807e91b285..c69f68a5ca 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-ja/strings.xml @@ -26,4 +26,7 @@ サブスクリプションを正常に一時停止しました サブスクリプションの再開に失敗しました サブスクリプションが正常に再開されました + 申し込む + サブスクリプションの更新 + サブスクリプションの削除に失敗しました \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml index 5b9855b400..e311a216ae 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-zh/strings.xml @@ -26,4 +26,7 @@ 已成功暫停訂閱 重新開始訂閱失敗 成功重啟訂閱 + 訂閱 + 更新訂閱 + 刪除訂閱失敗 \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index b95c1de95d..b4dcd79bf1 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -28,4 +28,5 @@ Successfully restarted subscription Subscribe Update Subscription + Failed to delete subscription \ No newline at end of file From a3de1868af300ea2012bdbc0fd216da6ecc59c71 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 13:30:14 -0300 Subject: [PATCH 17/32] - Layout adjustments on EditContact view and NewContact view - Change title and color of SUBSCRIBE button if active subscription exists --- .../contact/contact-common/build.gradle | 1 + .../chat/sphinx/contact/ui/ContactFragment.kt | 36 +++++++++++++----- .../sphinx/contact/ui/ContactSideEffect.kt | 3 +- .../sphinx/contact/ui/ContactViewModel.kt | 11 ++---- .../src/main/res/layout/layout_contact.xml | 3 +- .../layout_contact_detail_screen_header.xml | 38 ++++++++++--------- .../src/main/res/values-es/strings.xml | 2 + .../src/main/res/values-ja/strings.xml | 2 + .../src/main/res/values-zh/strings.xml | 2 + .../src/main/res/values/dimens.xml | 3 ++ .../src/main/res/values/strings.xml | 2 + .../edit_contact/ui/EditContactViewModel.kt | 11 +++++- .../new_contact/ui/NewContactViewModel.kt | 3 ++ 13 files changed, 78 insertions(+), 39 deletions(-) diff --git a/sphinx/screens-detail/contact/contact-common/build.gradle b/sphinx/screens-detail/contact/contact-common/build.gradle index 33995ba1bb..c601bf180d 100644 --- a/sphinx/screens-detail/contact/contact-common/build.gradle +++ b/sphinx/screens-detail/contact/contact-common/build.gradle @@ -35,6 +35,7 @@ dependencies { api project(path: ':sphinx:activity:insetter-activity') api project(path: ':sphinx:application:data:concepts:concept-image-loader') api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-contact') + api project(path: ':sphinx:application:data:concepts:repositories:concept-repository-subscription') api project(path: ':sphinx:screens-detail:common:detail-resources') api project(path: ':sphinx:application:common:resources') api project(path: ':sphinx:screens-detail:scanner:scanner-view-model-coordinator') diff --git a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt index 9e916a14ef..923715cd19 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt +++ b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactFragment.kt @@ -8,6 +8,7 @@ import android.text.Editable import android.text.TextWatcher import android.view.View import androidx.annotation.LayoutRes +import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.NavArgs import androidx.viewbinding.ViewBinding @@ -78,7 +79,6 @@ abstract class ContactFragment< textViewDetailScreenHeaderName.text = getHeaderText() textViewDetailScreenHeaderNavBack.apply { - goneIfFalse(viewModel.isFromAddFriend()) setOnClickListener { lifecycleScope.launch(viewModel.mainImmediate) { @@ -87,14 +87,12 @@ abstract class ContactFragment< } } - textViewDetailScreenClose.goneIfTrue(viewModel.isExistingContact()) textViewDetailScreenClose.setOnClickListener { lifecycleScope.launch(viewModel.mainImmediate) { viewModel.navigator.closeDetailScreen() } } - textViewDetailScreenSubscribe.goneIfFalse(viewModel.isExistingContact()) textViewDetailScreenSubscribe.setOnClickListener { lifecycleScope.launch(viewModel.mainImmediate) { viewModel.navigator.closeDetailScreen() @@ -110,17 +108,10 @@ abstract class ContactFragment< } } - editTextContactAddress.isEnabled = !viewModel.isExistingContact() - - scanAddressButton.goneIfTrue(viewModel.isExistingContact()) scanAddressButton.setOnClickListener { viewModel.requestScanner() } - buttonQrCode.goneIfFalse(viewModel.isExistingContact()) - - layoutConstraintExistingContactProfilePicture.goneIfFalse(viewModel.isExistingContact()) - editTextContactAddress.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } @@ -188,7 +179,30 @@ abstract class ContactFragment< } is ContactSideEffect.ExistingContact -> { + headerBinding.apply { + textViewDetailScreenHeaderNavBack.gone + + textViewDetailScreenSubscribe.visible + + textViewDetailScreenSubscribe.text = if (sideEffect.subscribed) { + getString(R.string.edit_contact_header_subscribed_button) + } else { + getString(R.string.edit_contact_header_subscribe_button) + } + + textViewDetailScreenSubscribe.backgroundTintList = if (sideEffect.subscribed) { + ContextCompat.getColorStateList(root.context, R.color.secondaryText) + } else { + ContextCompat.getColorStateList(root.context, R.color.primaryBlue) + } + } + contactBinding.apply { + editTextContactAddress.isEnabled = false + + scanAddressButton.gone + buttonQrCode.visible + editTextContactNickname.setText(sideEffect.nickname) editTextContactAddress.setText(sideEffect.pubKey.value) editTextContactRouteHint.setText(sideEffect.routeHint?.value ?: "") @@ -209,6 +223,8 @@ abstract class ContactFragment< } } + layoutConstraintExistingContactProfilePicture.visible + sideEffect.photoUrl?.let { viewModel.imageLoader.load( imageViewProfilePicture, diff --git a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactSideEffect.kt b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactSideEffect.kt index 0d2d3009f0..b536e3e5fd 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactSideEffect.kt +++ b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactSideEffect.kt @@ -72,7 +72,8 @@ sealed class ContactSideEffect: SideEffect() { val photoUrl: PhotoUrl?, val colorKey: String?, val pubKey: LightningNodePubKey, - val routeHint: LightningRouteHint? = null + val routeHint: LightningRouteHint? = null, + val subscribed: Boolean ): ContactSideEffect() { override suspend fun execute(value: Context) {} } diff --git a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactViewModel.kt b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactViewModel.kt index 95d988dc7e..32622d20b7 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactViewModel.kt +++ b/sphinx/screens-detail/contact/contact-common/src/main/java/chat/sphinx/contact/ui/ContactViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import androidx.navigation.NavArgs import chat.sphinx.concept_image_loader.ImageLoader import chat.sphinx.concept_repository_contact.ContactRepository +import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.concept_view_model_coordinator.ViewModelCoordinator import chat.sphinx.contact.R import chat.sphinx.contact.navigation.ContactNavigator @@ -20,6 +21,7 @@ import chat.sphinx.wrapper_contact.ContactAlias import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import io.matthewnelson.concept_views.viewstate.ViewStateContainer import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -28,6 +30,7 @@ abstract class ContactViewModel ( dispatchers: CoroutineDispatchers, private val app: Application, protected val contactRepository: ContactRepository, + protected val subscriptionRepository: SubscriptionRepository, protected val scannerCoordinator: ViewModelCoordinator, val imageLoader: ImageLoader ): SideEffectViewModel< @@ -120,14 +123,6 @@ abstract class ContactViewModel ( lightningRouteHint: LightningRouteHint? ) - fun isFromAddFriend(): Boolean { - return fromAddFriend - } - - fun isExistingContact(): Boolean { - return contactId != null - } - fun toQrCodeLightningNodePubKey(nodePubKey: String) { viewModelScope.launch(mainImmediate) { navigator.toQRCodeDetail( diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact.xml index ebe3c502bf..2a6f6942e7 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact.xml @@ -58,6 +58,7 @@ android:id="@+id/layout_constraint_existing_contact_profile_picture" android:layout_width="@dimen/default_form_scan_icon_container_width" android:layout_height="match_parent" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"> @@ -168,7 +169,7 @@ android:background="@drawable/ic_qr_code" android:backgroundTint="@color/secondaryText" android:layout_marginBottom="@dimen/default_form_scan_icon_bottom_margin" - android:visibility="visible" + android:visibility="gone" /> diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml index ee29226e49..c9c03f19d2 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/layout/layout_contact_detail_screen_header.xml @@ -19,22 +19,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - @@ -65,4 +49,22 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/values-es/strings.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/values-es/strings.xml index afafce2cee..7d27442309 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/values-es/strings.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/values-es/strings.xml @@ -8,4 +8,6 @@ Clave Pública inválida Pista de Ruta inválida No se pudo guardar el contacto + Suscribirse + Suscripto \ No newline at end of file diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/values-ja/strings.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/values-ja/strings.xml index 7e2a9bcdaf..dad1716ff1 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/values-ja/strings.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/values-ja/strings.xml @@ -8,4 +8,6 @@ 無効な公開鍵 無効なルートヒント 連絡先の保存に失敗しました + Subscribe + Subscribed \ No newline at end of file diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/values-zh/strings.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/values-zh/strings.xml index 620d4bd1f5..6029b663dc 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/values-zh/strings.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/values-zh/strings.xml @@ -8,4 +8,6 @@ 節點住址無效 秘密通路無效 無法保存聯繫人 + Subscribe + Subscribed \ No newline at end of file diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/values/dimens.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/values/dimens.xml index b9017095b6..874d5a976c 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/values/dimens.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/values/dimens.xml @@ -9,4 +9,7 @@ 230dp 25dp 10dp + + 30dp + 14sp \ No newline at end of file diff --git a/sphinx/screens-detail/contact/contact-common/src/main/res/values/strings.xml b/sphinx/screens-detail/contact/contact-common/src/main/res/values/strings.xml index 0d3b9afacd..f30249c71c 100644 --- a/sphinx/screens-detail/contact/contact-common/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/contact/contact-common/src/main/res/values/strings.xml @@ -8,4 +8,6 @@ Invalid Public Key Invalid Route Hint Failed to save contact + Subscribe + Subscribed \ No newline at end of file diff --git a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt index 682bf2de37..c9f4451b83 100644 --- a/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt +++ b/sphinx/screens-detail/contact/edit-contact/src/main/java/chat/sphinx/edit_contact/ui/EditContactViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import chat.sphinx.concept_image_loader.ImageLoader import chat.sphinx.concept_repository_contact.ContactRepository +import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.concept_view_model_coordinator.ViewModelCoordinator import chat.sphinx.contact.ui.ContactSideEffect import chat.sphinx.contact.ui.ContactViewModel @@ -35,12 +36,14 @@ internal class EditContactViewModel @Inject constructor( app: Application, scannerCoordinator: ViewModelCoordinator, contactRepository: ContactRepository, + subscriptionRepository: SubscriptionRepository, imageLoader: ImageLoader, ): ContactViewModel( editContactNavigator, dispatchers, app, contactRepository, + subscriptionRepository, scannerCoordinator, imageLoader ) @@ -57,13 +60,19 @@ internal class EditContactViewModel @Inject constructor( contactRepository.getContactById(contactId).firstOrNull().let { contact -> if (contact != null) { contact.nodePubKey?.let { lightningNodePubKey -> + + val subscription = subscriptionRepository.getActiveSubscriptionByContactId( + contactId + ).firstOrNull() + submitSideEffect( ContactSideEffect.ExistingContact( contact.alias?.value, contact.photoUrl, contact.getColorKey(), lightningNodePubKey, - contact.routeHint + contact.routeHint, + subscription != null ) ) } diff --git a/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactViewModel.kt b/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactViewModel.kt index cd4afdf87c..99b0f07793 100644 --- a/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactViewModel.kt +++ b/sphinx/screens-detail/contact/new-contact/src/main/java/chat/sphinx/new_contact/ui/NewContactViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import chat.sphinx.concept_image_loader.ImageLoader import chat.sphinx.concept_repository_contact.ContactRepository +import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.concept_view_model_coordinator.ViewModelCoordinator import chat.sphinx.contact.ui.ContactSideEffect import chat.sphinx.contact.ui.ContactViewModel @@ -38,12 +39,14 @@ internal class NewContactViewModel @Inject constructor( app: Application, scannerCoordinator: ViewModelCoordinator, contactRepository: ContactRepository, + subscriptionRepository: SubscriptionRepository, imageLoader: ImageLoader ): ContactViewModel( newContactNavigator, dispatchers, app, contactRepository, + subscriptionRepository, scannerCoordinator, imageLoader, ) { From a2d70310a9d40995c9966a06bf36875a5c263a81 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 14:39:22 -0300 Subject: [PATCH 18/32] Layout adjustments on Subscription form view --- .../subscription/ui/SubscriptionFragment.kt | 15 +- .../main/res/layout/fragment_subscription.xml | 606 ++++++++++-------- .../src/main/res/values/dimens.xml | 9 + 3 files changed, 348 insertions(+), 282 deletions(-) create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values/dimens.xml diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 7e0000330e..a57148a250 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -199,7 +199,10 @@ internal class SubscriptionFragment: SideEffectFragment< } else -> { radioButtonCustomAmount.isChecked = true - editTextCustomAmount.setText(viewState.subscription.amount.toString()) + + editTextCustomAmount.setText( + viewState.subscription.amount.value.toString() + ) } } @@ -223,11 +226,17 @@ internal class SubscriptionFragment: SideEffectFragment< when { viewState.subscription.end_number != null -> { radioButtonMake.isChecked = true - editTextMakeQuantity.setText(viewState.subscription.end_number?.value.toString()) + + editTextMakeQuantity.setText( + viewState.subscription.end_number?.value.toString() + ) } viewState.subscription.end_date != null -> { radioButtonUntil.isChecked = true - editTextPayUntil.setText(viewState.subscription.end_date.toString()) + + editTextPayUntil.setText( + viewState.subscription.end_date?.toString() + ) } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index f828d9d326..37a5ffcb9a 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -12,8 +12,8 @@ android:id="@+id/layout_constraint_subscription_header" android:layout_width="match_parent" android:layout_height="@dimen/default_header_height" - app:layout_constraintTop_toTopOf="parent" - > + app:layout_constraintTop_toTopOf="parent"> + + tools:visibility="visible" /> + tools:visibility="visible"> - - - - - + app:layout_constraintBottom_toTopOf="@+id/layout_constraint_subscribe"> - - - - - + android:layout_height="wrap_content"> - android:background="@drawable/edit_text_background" - app:layout_constraintTop_toTopOf="@+id/radio_button_custom_amount" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toEndOf="@+id/radio_button_custom_amount"> - - + + + app:layout_constraintTop_toTopOf="parent" /> - - - - - - - + - + - - - - - - + android:layout_marginHorizontal="@dimen/default_layout_margin" + android:paddingTop="@dimen/subscription_form_section_top_padding" + android:paddingBottom="@dimen/subscription_form_section_bottom_padding" + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_amount_header"> - - - + + + + + + + + + + + + + + + + + - - - - - - + + + + + app:layout_constraintTop_toTopOf="parent" /> + + - - + android:layout_marginHorizontal="@dimen/default_layout_margin" + android:paddingTop="@dimen/subscription_form_section_top_padding" + android:paddingBottom="@dimen/subscription_form_section_bottom_padding" + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_time_interval_header"> + + + + + + + + + + + + - - + + + + + app:layout_constraintTop_toTopOf="parent" /> + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:backgroundTint="@color/primaryBlue" /> + android:indeterminate="true" /> + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/dimens.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..45579eba67 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/dimens.xml @@ -0,0 +1,9 @@ + + + 36dp + 25dp + 25dp + 50dp + 60dp + + \ No newline at end of file From ecf780598a3ff2194f8fb96c036f0067f824597e Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 14:50:51 -0300 Subject: [PATCH 19/32] Fix for crash when setting end date --- .../java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index b43b3cbb3b..4928b14628 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import android.content.Context import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import chat.sphinx.concept_repository_contact.ContactRepository import chat.sphinx.concept_repository_subscription.SubscriptionRepository import chat.sphinx.kotlin_response.Response import chat.sphinx.subscription.R @@ -30,7 +29,6 @@ internal class SubscriptionViewModel @Inject constructor( val app: Application, dispatchers: CoroutineDispatchers, savedStateHandle: SavedStateHandle, - private val contactRepository: ContactRepository, private val subscriptionRepository: SubscriptionRepository, val navigator: SubscriptionNavigator ): SideEffectViewModel< @@ -104,7 +102,7 @@ internal class SubscriptionViewModel @Inject constructor( interval = interval, contactId = ContactId(args.argContactId), chatId = null, - endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it) }, + endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it.value) }, endNumber = endNumber?.let { EndNumber(it) } ) } else { From 77245f1938eb39499570d422a3af15395feb5bb7 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 14:51:20 -0300 Subject: [PATCH 20/32] ContactRepository unused dependency removed --- sphinx/screens-detail/subscription/subscription/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/screens-detail/subscription/subscription/build.gradle b/sphinx/screens-detail/subscription/subscription/build.gradle index b3d5703a8c..48b9b2d099 100644 --- a/sphinx/screens-detail/subscription/subscription/build.gradle +++ b/sphinx/screens-detail/subscription/subscription/build.gradle @@ -39,7 +39,6 @@ dependencies { implementation project(path: ':sphinx:application:common:wrappers:wrapper-common') implementation project(path: ':sphinx:screens-detail:common:detail-resources') implementation project(path: ':sphinx:application:data:concepts:repositories:concept-repository-subscription') - implementation project(path: ':sphinx:application:data:concepts:repositories:concept-repository-contact') implementation deps.androidx.lifecycle.hilt implementation deps.google.hilt From d43be5f0cda140d1cfa07d10b90bb3c4b281898d Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 15:28:05 -0300 Subject: [PATCH 21/32] Some translations fixed --- .../src/main/res/values-es/strings.xml | 18 +++++++++--------- .../src/main/res/values/strings.xml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml index 4ced1f6c5f..71f0d204ce 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml @@ -1,23 +1,23 @@ - PERIÓDICA + PAGOS RECURRENTES Monto - Custom amount: + Monto: 500 sats 1000 sats 2000 sats INTERVALO DE TIEMPO - Cotidiano + Diariament Semanalmente - Mensual - REGLA FINAL - Hacer + Mensualmente + FINALIZACIÓN + Realizar Pagos - Paga hasta + Pagar hasta Eliminar suscripción ¿Está seguro de que desea eliminar esta suscripción? - Se requiere la cantidad - Se requiere un intervalo de tiempo + Monto requerido + Intervalo de tiempo requerido Establezca la cantidad de pagos a realizar o la fecha de finalización No se pudo guardar la suscripción Suscripción guardada correctamente diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index b4dcd79bf1..b339e968f0 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -15,7 +15,7 @@ Payments Pay until Delete subscription - Are you sure you want to delete this subscription + Are you sure you want to delete this subscription? Amount is required Time Interval is required Please set either the number of payments to make or end date From c24c7b8bf2bc3b45211e7a1c75f73ca6e4243b39 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 15:28:58 -0300 Subject: [PATCH 22/32] Fixes related with end date calendar picker --- .../java/chat/sphinx/wrapper_common/DateTime.kt | 5 +++++ .../subscription/ui/SubscriptionFragment.kt | 13 +++++++++---- .../subscription/ui/widgets/SphinxRadioGroup.kt | 10 ++++++++-- .../main/res/layout/fragment_subscription.xml | 17 +++++++++++------ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt index b40eed94fd..3c079e2ce3 100644 --- a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt @@ -15,6 +15,11 @@ import java.util.* inline fun String.toDateTime(): DateTime = DateTime(DateTime.getFormatRelay().parse(this)) +@Suppress("NOTHING_TO_INLINE") +@Throws(ParseException::class) +inline fun String.toDateTimeWithFormat(format: SimpleDateFormat): DateTime = + DateTime(format.parse(this)) + @Suppress("NOTHING_TO_INLINE") inline fun Long.toDateTime(): DateTime = DateTime(Date(this)) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index a57148a250..9cdeb97ae2 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -14,6 +14,7 @@ import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding import chat.sphinx.wrapper_common.DateTime import chat.sphinx.wrapper_common.lightning.Sat import chat.sphinx.wrapper_common.toDateTime +import chat.sphinx.wrapper_common.toDateTimeWithFormat import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment import io.matthewnelson.android_feature_screens.util.gone @@ -76,14 +77,14 @@ internal class SubscriptionFragment: SideEffectFragment< calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) editTextPayUntil.setText( - SimpleDateFormat("dd/MM/yyyy", Locale.US).format(calendar.time) + DateTime.getFormatMMMddyyyy().format(calendar.time) ) }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), ) - datePickerDialog.datePicker.minDate = calendar.timeInMillis + datePickerDialog.datePicker.minDate = Date().time datePickerDialog.show() } @@ -228,15 +229,19 @@ internal class SubscriptionFragment: SideEffectFragment< radioButtonMake.isChecked = true editTextMakeQuantity.setText( - viewState.subscription.end_number?.value.toString() + viewState.subscription.end_number!!.value.toString() ) } viewState.subscription.end_date != null -> { radioButtonUntil.isChecked = true editTextPayUntil.setText( - viewState.subscription.end_date?.toString() + DateTime.getFormatMMMddyyyy().format( + viewState.subscription.end_date!!.value + ) ) + + calendar.time = viewState.subscription.end_date!!.value } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt index 0ff10b79fd..b7260c4afa 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/widgets/SphinxRadioGroup.kt @@ -210,12 +210,15 @@ class SphinxRadioGroup @JvmOverloads constructor( super.onCheckedChanged(buttonView, isChecked) editText.isEnabled = isChecked - if (editText.isEnabled) { + + if (editText.isEnabled && buttonView.isPressed) { editText.requestFocus() context?.let { val inputMethodManager = ContextCompat.getSystemService(it, InputMethodManager::class.java) inputMethodManager?.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) } + } else { + editText.setText("") } } } @@ -225,9 +228,12 @@ class SphinxRadioGroup @JvmOverloads constructor( super.onCheckedChanged(buttonView, isChecked) editText.isEnabled = isChecked - if (editText.isEnabled) { + + if (editText.isEnabled && buttonView.isPressed) { editText.requestFocus() editText.callOnClick() + } else { + editText.setText("") } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index 37a5ffcb9a..8934beccb8 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -27,16 +27,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - @@ -383,6 +386,7 @@ android:backgroundTint="@android:color/transparent" android:textCursorDrawable="@drawable/cursor" android:gravity="center" + android:enabled="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> @@ -424,7 +428,7 @@ android:id="@+id/edit_text_pay_until" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="120dp" + android:minWidth="170dp" android:inputType="date" android:singleLine="true" android:clickable="false" @@ -437,6 +441,7 @@ android:textCursorDrawable="@drawable/cursor" android:gravity="center" android:drawableStart="@drawable/ic_calendar" + android:enabled="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> From 10a626bd8e99ca068a4b576f786a29b9a31dc577 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 15:50:58 -0300 Subject: [PATCH 23/32] Fixes related with date calendar and timezones --- .../chat/sphinx/wrapper_common/DateTime.kt | 8 ++-- .../subscription/ui/SubscriptionFragment.kt | 45 ++++++++++--------- .../subscription/ui/SubscriptionViewModel.kt | 4 +- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt index 3c079e2ce3..68ecdda015 100644 --- a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/DateTime.kt @@ -250,15 +250,15 @@ value class DateTime(val value: Date) { @Volatile private var formatMMMddyyyy: SimpleDateFormat? = null - fun getFormatMMMddyyyy(): SimpleDateFormat = + fun getFormatMMMddyyyy(timeZone: TimeZone = TimeZone.getDefault()): SimpleDateFormat = formatMMMddyyyy?.also { - it.timeZone = TimeZone.getDefault() + it.timeZone = timeZone } ?: synchronized(this) { formatMMMddyyyy?.also { - it.timeZone = TimeZone.getDefault() + it.timeZone = timeZone } ?: SimpleDateFormat(FORMAT_MMM_DD_YYYY, Locale.getDefault()) .also { - it.timeZone = TimeZone.getDefault() + it.timeZone = timeZone formatMMMddyyyy = it } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 9cdeb97ae2..df65561ad6 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -40,6 +40,8 @@ internal class SubscriptionFragment: SideEffectFragment< override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + calendar.timeZone = TimeZone.getTimeZone(DateTime.UTC) + binding.apply { textViewDetailScreenHeaderName.text = getString(R.string.subscription_header_name) @@ -68,26 +70,25 @@ internal class SubscriptionFragment: SideEffectFragment< } editTextPayUntil.setOnClickListener { - this@SubscriptionFragment.context?.let { context -> - val datePickerDialog = DatePickerDialog( - context, - { _, year, month, dayOfMonth -> - calendar.set(Calendar.YEAR, year) - calendar.set(Calendar.MONTH, month) - calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) - - editTextPayUntil.setText( - DateTime.getFormatMMMddyyyy().format(calendar.time) - ) - }, - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH), - ) - datePickerDialog.datePicker.minDate = Date().time - datePickerDialog.show() - } + val datePickerDialog = DatePickerDialog( + root.context, + { _, year, month, dayOfMonth -> + calendar.set(Calendar.YEAR, year) + calendar.set(Calendar.MONTH, month) + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + editTextPayUntil.setText( + DateTime.getFormatMMMddyyyy( + TimeZone.getTimeZone(DateTime.UTC) + ).format(calendar.time) + ) + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), + ) + datePickerDialog.datePicker.minDate = Date().time + datePickerDialog.show() } radioGroupAmount.setOnCheckedChangeListenerWithInputInteraction( @@ -236,9 +237,9 @@ internal class SubscriptionFragment: SideEffectFragment< radioButtonUntil.isChecked = true editTextPayUntil.setText( - DateTime.getFormatMMMddyyyy().format( - viewState.subscription.end_date!!.value - ) + DateTime.getFormatMMMddyyyy( + TimeZone.getTimeZone(DateTime.UTC) + ).format(viewState.subscription.end_date!!.value) ) calendar.time = viewState.subscription.end_date!!.value diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 4928b14628..5d2b39a01f 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -102,7 +102,7 @@ internal class SubscriptionViewModel @Inject constructor( interval = interval, contactId = ContactId(args.argContactId), chatId = null, - endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it.value) }, + endDate = endDate?.let { DateTime.getFormatMMMddyyyy(TimeZone.getTimeZone("UTC")).format(it.value) }, endNumber = endNumber?.let { EndNumber(it) } ) } else { @@ -112,7 +112,7 @@ internal class SubscriptionViewModel @Inject constructor( interval = interval, contactId = ContactId(args.argContactId), chatId = subscription.chat_id, - endDate = endDate?.let { DateTime.getFormatMMMddyyyy().format(it.value) }, + endDate = endDate?.let { DateTime.getFormatMMMddyyyy(TimeZone.getTimeZone("UTC")).format(it.value) }, endNumber = endNumber?.let { EndNumber(it) } ) } From e44a76db26b0b376f8d4e97548f219158c32e50c Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 15:58:04 -0300 Subject: [PATCH 24/32] Changes related with loading wheel while saving subscription --- .../subscription/ui/SubscriptionFragment.kt | 11 ++++++----- .../subscription/ui/SubscriptionViewModel.kt | 15 ++++++--------- .../subscription/ui/SubscriptionViewState.kt | 4 +++- .../src/main/res/values-es/strings.xml | 2 +- .../subscription/src/main/res/values/strings.xml | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index df65561ad6..c88262bd32 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -174,11 +174,6 @@ internal class SubscriptionFragment: SideEffectFragment< buttonSave.text = getString(R.string.subscribe) } } - is SubscriptionViewState.CloseSubscriptionDetail -> { - lifecycleScope.launch { - viewModel.navigator.closeDetailScreen() - } - } is SubscriptionViewState.SubscriptionLoaded -> { binding.apply { progressBarSubscriptionSave.gone @@ -247,6 +242,12 @@ internal class SubscriptionFragment: SideEffectFragment< } } } + is SubscriptionViewState.SavingSubscription -> { + binding.progressBarSubscriptionSave.visible + } + is SubscriptionViewState.SavingSubscriptionFailed -> { + binding.progressBarSubscriptionSave.gone + } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 5d2b39a01f..eb6d0d6a27 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -93,6 +93,8 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } + updateViewState(SubscriptionViewState.SavingSubscription) + subscriptionRepository.getActiveSubscriptionByContactId( ContactId(args.argContactId) ).firstOrNull().let { subscription -> @@ -122,11 +124,10 @@ internal class SubscriptionViewModel @Inject constructor( submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_save_subscription)) ) + updateViewState(SubscriptionViewState.SavingSubscriptionFailed) } is Response.Success -> { - updateViewState( - SubscriptionViewState.CloseSubscriptionDetail - ) + navigator.popBackStack() } } } @@ -140,9 +141,7 @@ internal class SubscriptionViewModel @Inject constructor( viewModelScope.launch(mainImmediate) { subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> if (subscription == null) { - updateViewState( - SubscriptionViewState.CloseSubscriptionDetail - ) + navigator.popBackStack() } else { when(subscriptionRepository.deleteSubscription(subscription.id)) { is Response.Error -> { @@ -151,9 +150,7 @@ internal class SubscriptionViewModel @Inject constructor( ) } is Response.Success -> { - updateViewState( - SubscriptionViewState.CloseSubscriptionDetail - ) + navigator.popBackStack() } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index b3b659af14..885154e0f3 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -6,7 +6,9 @@ import io.matthewnelson.concept_views.viewstate.ViewState internal sealed class SubscriptionViewState: ViewState() { object Idle: SubscriptionViewState() - object CloseSubscriptionDetail: SubscriptionViewState() + object SavingSubscription: SubscriptionViewState() + + object SavingSubscriptionFailed: SubscriptionViewState() class SubscriptionLoaded( val subscription: Subscription diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml index 71f0d204ce..3f6b17dd92 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values-es/strings.xml @@ -27,6 +27,6 @@ No se pudo reiniciar la suscripción Suscripción reiniciada correctamente Suscribir - Actualizar suscripción + Actualizar No se pudo borrar la suscripción \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml index b339e968f0..091e0ba406 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/strings.xml @@ -27,6 +27,6 @@ Failed to restart subscription Successfully restarted subscription Subscribe - Update Subscription + Update Failed to delete subscription \ No newline at end of file From 264ebc53e4dd120623de2acd588496c3cbe5e9d5 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 16:15:03 -0300 Subject: [PATCH 25/32] Custom radio button style applied on subscription form --- .../main/res/drawable/custom_radio_button.xml | 22 +++++++++++++++++++ .../main/res/drawable/ic_check_box_off.xml | 9 ++++++++ .../src/main/res/drawable/ic_check_box_on.xml | 9 ++++++++ .../main/res/layout/fragment_subscription.xml | 18 +++++++++++++++ .../src/main/res/values/styles.xml | 6 +++++ 5 files changed, 64 insertions(+) create mode 100644 sphinx/application/common/resources/src/main/res/drawable/custom_radio_button.xml create mode 100644 sphinx/application/common/resources/src/main/res/drawable/ic_check_box_off.xml create mode 100644 sphinx/application/common/resources/src/main/res/drawable/ic_check_box_on.xml create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/res/values/styles.xml diff --git a/sphinx/application/common/resources/src/main/res/drawable/custom_radio_button.xml b/sphinx/application/common/resources/src/main/res/drawable/custom_radio_button.xml new file mode 100644 index 0000000000..80ee2f3251 --- /dev/null +++ b/sphinx/application/common/resources/src/main/res/drawable/custom_radio_button.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_off.xml b/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_off.xml new file mode 100644 index 0000000000..fc7725a72b --- /dev/null +++ b/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_on.xml b/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_on.xml new file mode 100644 index 0000000000..b7ec917b66 --- /dev/null +++ b/sphinx/application/common/resources/src/main/res/drawable/ic_check_box_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index 8934beccb8..c86b057197 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -169,6 +169,8 @@ android:textColor="@color/secondaryText" android:layout_width="match_parent" android:layout_height="@dimen/subscription_form_field_height" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" /> @@ -178,6 +180,8 @@ android:textColor="@color/secondaryText" android:layout_width="match_parent" android:layout_height="@dimen/subscription_form_field_height" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toBottomOf="@+id/radio_button_500_sats" app:layout_constraintStart_toStartOf="parent" /> @@ -187,6 +191,8 @@ android:textColor="@color/secondaryText" android:layout_width="match_parent" android:layout_height="@dimen/subscription_form_field_height" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toBottomOf="@+id/radio_button_1000_sats" app:layout_constraintStart_toStartOf="parent" /> @@ -196,6 +202,8 @@ android:textColor="@color/secondaryText" android:layout_width="wrap_content" android:layout_height="@dimen/subscription_form_field_height" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/radio_button_2000_sats" /> @@ -284,6 +292,8 @@ android:layout_height="@dimen/subscription_form_field_height" android:textColor="@color/secondaryText" android:text="@string/daily" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toTopOf="parent" /> @@ -361,6 +375,8 @@ android:layout_height="@dimen/subscription_form_big_field_height" android:textColor="@color/secondaryText" android:text="@string/make" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"/> @@ -409,6 +425,8 @@ android:layout_height="@dimen/subscription_form_big_field_height" android:textColor="@color/secondaryText" android:text="@string/pay_until" + android:paddingStart="@dimen/default_half_layout_margin" + style="@style/CustomRadioButtonStyle" app:layout_constraintTop_toBottomOf="@+id/radio_button_make" app:layout_constraintStart_toStartOf="parent" /> diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/values/styles.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/values/styles.xml new file mode 100644 index 0000000000..d2775e3ed0 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 357030a1a510f637efcea239ac265d6f10d02a37 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Wed, 1 Sep 2021 16:50:03 -0300 Subject: [PATCH 26/32] Some view ids changed to follow good practices --- .../subscription/ui/SubscriptionFragment.kt | 74 ++++++------- .../main/res/layout/fragment_subscription.xml | 102 +++++++++--------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index c88262bd32..4832ab47fd 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -69,7 +69,7 @@ internal class SubscriptionFragment: SideEffectFragment< } } - editTextPayUntil.setOnClickListener { + editTextSubscriptionPayUntil.setOnClickListener { val datePickerDialog = DatePickerDialog( root.context, { _, year, month, dayOfMonth -> @@ -77,7 +77,7 @@ internal class SubscriptionFragment: SideEffectFragment< calendar.set(Calendar.MONTH, month) calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) - editTextPayUntil.setText( + editTextSubscriptionPayUntil.setText( DateTime.getFormatMMMddyyyy( TimeZone.getTimeZone(DateTime.UTC) ).format(calendar.time) @@ -91,60 +91,60 @@ internal class SubscriptionFragment: SideEffectFragment< datePickerDialog.show() } - radioGroupAmount.setOnCheckedChangeListenerWithInputInteraction( - radioButtonCustomAmount, editTextCustomAmount + radioGroupSubscriptionAmount.setOnCheckedChangeListenerWithInputInteraction( + radioButtonSubscriptionAmountCustom, editTextSubscriptionCustomAmount ) - radioGroupEndRule.setOnCheckedChangeListenerWithInputInteraction( - radioButtonMake, editTextMakeQuantity + radioGroupSubscriptionEndRule.setOnCheckedChangeListenerWithInputInteraction( + radioButtonSubscriptionMakeQuantity, editTextSubscriptionMakeQuantity ) - radioGroupEndRule.setOnCheckedChangeListenerWithDatePickerInputInteraction( - radioButtonUntil, editTextPayUntil + radioGroupSubscriptionEndRule.setOnCheckedChangeListenerWithDatePickerInputInteraction( + radioButtonSubscriptionPayUntil, editTextSubscriptionPayUntil ) - buttonSave.setOnClickListener { + buttonSubscriptionSave.setOnClickListener { - val amount: Sat? = when (radioGroupAmount.checkedRadioButtonId) { - R.id.radio_button_500_sats -> { + val amount: Sat? = when (radioGroupSubscriptionAmount.checkedRadioButtonId) { + R.id.radio_button_subscription_amount_500_sats -> { Sat(500) } - R.id.radio_button_1000_sats -> { + R.id.radio_button_subscription_amount_1000_sats -> { Sat(1000) } - R.id.radio_button_2000_sats -> { + R.id.radio_button_subscription_amount_2000_sats -> { Sat(2000) } - R.id.radio_button_custom_amount -> { - editTextCustomAmount.text?.toString()?.toLongOrNull()?.let { + R.id.radio_button_subscription_amount_custom -> { + editTextSubscriptionCustomAmount.text?.toString()?.toLongOrNull()?.let { Sat(it) } } else -> null } - val cron: String? = when (radioGroupTimeInterval.checkedRadioButtonId) { - R.id.radio_button_daily -> { + val cron: String? = when (radioGroupSubscriptionTimeInterval.checkedRadioButtonId) { + R.id.radio_button_subscription_daily -> { "daily" } - R.id.radio_button_weekly -> { + R.id.radio_button_subscription_weekly -> { "weekly" } - R.id.radio_button_monthly -> { + R.id.radio_button_subscription_monthly -> { "monthly" } else -> null } var endNumber: Long? = null - val endDate: DateTime? = when (radioGroupEndRule.checkedRadioButtonId) { - R.id.radio_button_make -> { - editTextMakeQuantity.text?.toString()?.let { + val endDate: DateTime? = when (radioGroupSubscriptionEndRule.checkedRadioButtonId) { + R.id.radio_button_subscription_make_quantity -> { + editTextSubscriptionMakeQuantity.text?.toString()?.let { endNumber = it.toLongOrNull() } null } - R.id.radio_button_until -> { + R.id.radio_button_subscription_pay_until -> { calendar.timeInMillis.toDateTime() } else -> null @@ -171,7 +171,7 @@ internal class SubscriptionFragment: SideEffectFragment< progressBarSubscriptionSave.gone textViewDetailSubscriptionDelete.gone layoutConstraintSubscriptionEnablement.gone - buttonSave.text = getString(R.string.subscribe) + buttonSubscriptionSave.text = getString(R.string.subscribe) } } is SubscriptionViewState.SubscriptionLoaded -> { @@ -179,25 +179,25 @@ internal class SubscriptionFragment: SideEffectFragment< progressBarSubscriptionSave.gone textViewDetailSubscriptionDelete.visible layoutConstraintSubscriptionEnablement.visible - buttonSave.text = getString(R.string.update_subscription) + buttonSubscriptionSave.text = getString(R.string.update_subscription) switchSubscriptionEnablement.isChecked = !viewState.subscription.ended && !viewState.subscription.paused // Populate Amount when (viewState.subscription.amount.value) { 500L -> { - radioButton500Sats.isChecked = true + radioButtonSubscriptionAmount500Sats.isChecked = true } 1000L -> { - radioButton1000Sats.isChecked = true + radioButtonSubscriptionAmount1000Sats.isChecked = true } 2000L -> { - radioButton2000Sats.isChecked = true + radioButtonSubscriptionAmount2000Sats.isChecked = true } else -> { - radioButtonCustomAmount.isChecked = true + radioButtonSubscriptionAmountCustom.isChecked = true - editTextCustomAmount.setText( + editTextSubscriptionCustomAmount.setText( viewState.subscription.amount.value.toString() ) } @@ -207,31 +207,31 @@ internal class SubscriptionFragment: SideEffectFragment< when { viewState.subscription.cron.value.endsWith("* * *") -> { // Daily... - radioButtonDaily.isChecked = true + radioButtonSubscriptionDaily.isChecked = true } viewState.subscription.cron.value.endsWith("* *") -> { // Monthly - radioButtonMonthly.isChecked = true + radioButtonSubscriptionMonthly.isChecked = true } else -> { // Weekly - radioButtonWeekly.isChecked = true + radioButtonSubscriptionWeekly.isChecked = true } } // Populate End Rule when { viewState.subscription.end_number != null -> { - radioButtonMake.isChecked = true + radioButtonSubscriptionMakeQuantity.isChecked = true - editTextMakeQuantity.setText( + editTextSubscriptionMakeQuantity.setText( viewState.subscription.end_number!!.value.toString() ) } viewState.subscription.end_date != null -> { - radioButtonUntil.isChecked = true + radioButtonSubscriptionPayUntil.isChecked = true - editTextPayUntil.setText( + editTextSubscriptionPayUntil.setText( DateTime.getFormatMMMddyyyy( TimeZone.getTimeZone(DateTime.UTC) ).format(viewState.subscription.end_date!!.value) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml index c86b057197..aa7e3b1c30 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml +++ b/sphinx/screens-detail/subscription/subscription/src/main/res/layout/fragment_subscription.xml @@ -105,14 +105,14 @@ android:layout_height="0dp" android:layout_marginBottom="@dimen/default_layout_margin" app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_header" - app:layout_constraintBottom_toTopOf="@+id/layout_constraint_subscribe"> + app:layout_constraintBottom_toTopOf="@+id/layout_constraint_subscription_save"> + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_amount_section_header"> + app:layout_constraintTop_toBottomOf="@+id/radio_button_subscription_amount_2000_sats" /> + app:layout_constraintTop_toTopOf="@+id/radio_button_subscription_amount_custom" + app:layout_constraintBottom_toBottomOf="@+id/radio_button_subscription_amount_custom" + app:layout_constraintStart_toEndOf="@+id/radio_button_subscription_amount_custom"> + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_amount_container"> + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_time_interval_section_header"> + app:layout_constraintTop_toBottomOf="@+id/radio_button_subscription_daily" /> + app:layout_constraintTop_toBottomOf="@+id/radio_button_subscription_weekly" /> + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_time_interval_container"> + app:layout_constraintTop_toBottomOf="@+id/layout_constraint_subscription_end_rule_section_header"> + app:layout_constraintStart_toEndOf="@+id/layout_constraint_subscription_make_quantity" + app:layout_constraintBottom_toBottomOf="@+id/layout_constraint_subscription_make_quantity" + app:layout_constraintTop_toTopOf="@+id/layout_constraint_subscription_make_quantity" /> + app:layout_constraintStart_toEndOf="@+id/radio_button_subscription_pay_until" + app:layout_constraintBottom_toBottomOf="@+id/radio_button_subscription_pay_until" + app:layout_constraintTop_toTopOf="@+id/radio_button_subscription_pay_until"> Date: Thu, 2 Sep 2021 13:46:10 -0300 Subject: [PATCH 27/32] Changes requested on PR (Removing RadioGroup still pending) --- .../detail/SubscriptionNavigatorImpl.kt | 4 +- .../wrapper_common/subscription/Cron.kt | 2 +- .../wrapper_subscription/Subscription.kt | 12 ++-- .../sphinx/concept_coredb/SphinxDatabase.sq | 20 +++--- .../SubscriptionRepository.kt | 4 -- .../feature_repository/SphinxRepository.kt | 33 ++++------ .../SubscriptionDboPresenterMapper.kt | 12 ++-- .../feature_repository/util/Extensions.kt | 1 + .../subscription/ui/SubscriptionFragment.kt | 42 ++++++------ .../subscription/ui/SubscriptionViewModel.kt | 66 +++++++------------ .../subscription/ui/SubscriptionViewState.kt | 7 +- 11 files changed, 88 insertions(+), 115 deletions(-) diff --git a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt index 235b2019a1..c8ee70b5cc 100644 --- a/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt +++ b/sphinx/activity/main/activitymain/src/main/java/chat/sphinx/activitymain/navigation/navigators/detail/SubscriptionNavigatorImpl.kt @@ -5,10 +5,10 @@ import chat.sphinx.subscription.navigation.SubscriptionNavigator import javax.inject.Inject internal class SubscriptionNavigatorImpl @Inject constructor( - private val detailDriver: DetailNavigationDriver, + detailDriver: DetailNavigationDriver, ): SubscriptionNavigator(detailDriver) { override suspend fun closeDetailScreen() { - detailDriver.closeDetailScreen() + (navigationDriver as DetailNavigationDriver).closeDetailScreen() } } diff --git a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt index f3e9d8a29b..cd2206c6f5 100644 --- a/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt +++ b/sphinx/application/common/wrappers/wrapper-common/src/main/java/chat/sphinx/wrapper_common/subscription/Cron.kt @@ -4,7 +4,7 @@ package chat.sphinx.wrapper_common.subscription value class Cron(val value: String) { init { require(value.isNotEmpty()) { - "SubscriptionCount cannot be empty" + "Subscription Cron cannot be empty" } } } \ No newline at end of file diff --git a/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt b/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt index ba42690a65..a6b3950d8d 100644 --- a/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt +++ b/sphinx/application/common/wrappers/wrapper-subscription/src/main/java/chat/sphinx/wrapper_subscription/Subscription.kt @@ -13,13 +13,13 @@ data class Subscription( val id: SubscriptionId, val cron: Cron, val amount: Sat, - val end_number: EndNumber?, + val endNumber: EndNumber?, val count: SubscriptionCount, - val end_date: DateTime?, + val endDate: DateTime?, val ended: Boolean, val paused: Boolean, - val created_at: DateTime, - val updated_at: DateTime, - val chat_id: ChatId, - val contact_id: ContactId + val createdAt: DateTime, + val updatedAt: DateTime, + val chatId: ChatId, + val contactId: ContactId ) \ No newline at end of file diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq index 91d474f614..3f25e59683 100644 --- a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq @@ -756,15 +756,12 @@ SELECT * FROM subscriptionDbo WHERE id = ?; -subscriptionGetByContactId: +subscriptionGetLastActiveByContactId: SELECT * FROM subscriptionDbo -WHERE ended = 0 AND contact_id = ?; - -activeSubscriptionGetByContactId: -SELECT * -FROM subscriptionDbo -WHERE ended = 0 AND contact_id = ?; +WHERE ended = 0 AND contact_id = ? +ORDER BY id DESC +LIMIT 1; subscriptionGetByChatId: SELECT * @@ -777,8 +774,7 @@ FROM subscriptionDbo; subscriptionUpsert { UPDATE subscriptionDbo - SET id = :id, - cron = :cron, + SET cron = :cron, amount = :amount, end_number = :end_number, count = :count, @@ -833,4 +829,8 @@ WHERE id = ?; subscriptionUpdateEnded: UPDATE subscriptionDbo SET ended = :ended -WHERE id = ?; \ No newline at end of file +WHERE id = ?; + +subscriptionDeleteByContactId: +DELETE FROM subscriptionDbo +WHERE contact_id = ?; \ No newline at end of file diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt index 66be916095..23e2cc9d9e 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -12,10 +12,6 @@ import kotlinx.coroutines.flow.Flow interface SubscriptionRepository { - suspend fun getSubscriptionByContactId( - contactId: ContactId - ): Flow - suspend fun getActiveSubscriptionByContactId( contactId: ContactId ): Flow diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 46342c0d48..489ac7cf19 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -3571,19 +3571,9 @@ abstract class SphinxRepository( SubscriptionDboPresenterMapper(dispatchers) } - override suspend fun getSubscriptionByContactId(contactId: ContactId): Flow = flow { - emitAll( - coreDB.getSphinxDatabaseQueries().subscriptionGetByContactId(contactId) - .asFlow() - .mapToOneOrNull(io) - .map { it?.let { subscriptionDboPresenterMapper.mapFrom(it) } } - .distinctUntilChanged() - ) - } - override suspend fun getActiveSubscriptionByContactId(contactId: ContactId): Flow = flow { emitAll( - coreDB.getSphinxDatabaseQueries().activeSubscriptionGetByContactId(contactId) + coreDB.getSphinxDatabaseQueries().subscriptionGetLastActiveByContactId(contactId) .asFlow() .mapToOneOrNull(io) .map { it?.let { subscriptionDboPresenterMapper.mapFrom(it) } } @@ -3612,8 +3602,9 @@ abstract class SphinxRepository( end_date = endDate ) ).collect { loadResponse -> + @Exhaustive when (loadResponse) { - LoadResponse.Loading -> { } + is LoadResponse.Loading -> {} is Response.Error -> { response = loadResponse } @@ -3647,7 +3638,7 @@ abstract class SphinxRepository( endDate: String?, endNumber: EndNumber? ): Response { - var response: Response = Response.Error(ResponseError(("Failed to update subscription"))) + var response: Response? = null applicationScope.launch(mainImmediate) { @@ -3662,6 +3653,7 @@ abstract class SphinxRepository( end_date = endDate ) ).collect { loadResponse -> + @Exhaustive when (loadResponse) { LoadResponse.Loading -> { } is Response.Error -> { @@ -3686,13 +3678,13 @@ abstract class SphinxRepository( } }.join() - return response + return response ?: Response.Error(ResponseError(("Failed to update subscription"))) } override suspend fun restartSubscription( subscriptionId: SubscriptionId ): Response { - var response: Response = Response.Error(ResponseError(("Failed to restart subscription"))) + var response: Response? = null applicationScope.launch(mainImmediate) { @@ -3722,13 +3714,14 @@ abstract class SphinxRepository( } } }.join() - return response + + return response ?: Response.Error(ResponseError(("Failed to restart subscription"))) } override suspend fun pauseSubscription( subscriptionId: SubscriptionId ): Response { - var response: Response = Response.Error(ResponseError(("Failed to pause subscription"))) + var response: Response? = null applicationScope.launch(mainImmediate) { @@ -3759,13 +3752,13 @@ abstract class SphinxRepository( } }.join() - return response + return response ?: Response.Error(ResponseError(("Failed to pause subscription"))) } override suspend fun deleteSubscription( subscriptionId: SubscriptionId ): Response { - var response: Response = Response.Error(ResponseError(("Failed to delete subscription"))) + var response: Response? = null applicationScope.launch(mainImmediate) { networkQuerySubscription.deleteSubscription( @@ -3792,6 +3785,6 @@ abstract class SphinxRepository( } }.join() - return response + return response ?: Response.Error(ResponseError(("Failed to delete subscription"))) } } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt index a66b442ae4..5f051a41d6 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/mappers/subscription/SubscriptionDboPresenterMapper.kt @@ -35,15 +35,15 @@ internal class SubscriptionDboPresenterMapper( id, cron, amount, - end_number, + endNumber, count, - end_date, + endDate, ended, paused, - created_at, - updated_at, - chat_id, - contact_id + createdAt, + updatedAt, + chatId, + contactId ) } } diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt index a67eb4427f..b59ac01f8e 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/util/Extensions.kt @@ -416,6 +416,7 @@ inline fun TransactionCallbacks.deleteContactById( queries.contactDeleteById(contactId) queries.inviteDeleteByContactId(contactId) queries.dashboardDeleteById(contactId) + queries.subscriptionDeleteByContactId(contactId) } @Suppress("NOTHING_TO_INLINE") diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 4832ab47fd..8d5622794d 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -43,11 +43,12 @@ internal class SubscriptionFragment: SideEffectFragment< calendar.timeZone = TimeZone.getTimeZone(DateTime.UTC) binding.apply { + textViewDetailScreenHeaderName.text = getString(R.string.subscription_header_name) - textViewDetailScreenHeaderNavBack.apply { - visible - setOnClickListener { + textViewDetailScreenHeaderNavBack.apply navBack@ { + this@navBack.visible + this@navBack.setOnClickListener { lifecycleScope.launch { viewModel.navigator.popBackStack() } } } @@ -125,13 +126,13 @@ internal class SubscriptionFragment: SideEffectFragment< val cron: String? = when (radioGroupSubscriptionTimeInterval.checkedRadioButtonId) { R.id.radio_button_subscription_daily -> { - "daily" + SubscriptionViewModel.DAILY_INTERVAL } R.id.radio_button_subscription_weekly -> { - "weekly" + SubscriptionViewModel.WEEKLY_INTERVAL } R.id.radio_button_subscription_monthly -> { - "monthly" + SubscriptionViewModel.MONTHLY_INTERVAL } else -> null } @@ -181,10 +182,9 @@ internal class SubscriptionFragment: SideEffectFragment< layoutConstraintSubscriptionEnablement.visible buttonSubscriptionSave.text = getString(R.string.update_subscription) - switchSubscriptionEnablement.isChecked = !viewState.subscription.ended && !viewState.subscription.paused + switchSubscriptionEnablement.isChecked = viewState.isActive - // Populate Amount - when (viewState.subscription.amount.value) { + when (viewState.amount) { 500L -> { radioButtonSubscriptionAmount500Sats.isChecked = true } @@ -198,46 +198,42 @@ internal class SubscriptionFragment: SideEffectFragment< radioButtonSubscriptionAmountCustom.isChecked = true editTextSubscriptionCustomAmount.setText( - viewState.subscription.amount.value.toString() + viewState.amount.toString() ) } } - // Populate Time Interval - when { - viewState.subscription.cron.value.endsWith("* * *") -> { - // Daily... + when (viewState.timeInterval) { + SubscriptionViewModel.DAILY_INTERVAL -> { radioButtonSubscriptionDaily.isChecked = true } - viewState.subscription.cron.value.endsWith("* *") -> { - // Monthly + SubscriptionViewModel.MONTHLY_INTERVAL -> { radioButtonSubscriptionMonthly.isChecked = true } - else -> { - // Weekly + SubscriptionViewModel.WEEKLY_INTERVAL -> { radioButtonSubscriptionWeekly.isChecked = true } } // Populate End Rule when { - viewState.subscription.end_number != null -> { + viewState.endNumber != null -> { radioButtonSubscriptionMakeQuantity.isChecked = true editTextSubscriptionMakeQuantity.setText( - viewState.subscription.end_number!!.value.toString() + viewState.endNumber!!.toString() ) } - viewState.subscription.end_date != null -> { + viewState.endDate != null -> { radioButtonSubscriptionPayUntil.isChecked = true editTextSubscriptionPayUntil.setText( DateTime.getFormatMMMddyyyy( TimeZone.getTimeZone(DateTime.UTC) - ).format(viewState.subscription.end_date!!.value) + ).format(viewState.endDate!!.value) ) - calendar.time = viewState.subscription.end_date!!.value + calendar.time = viewState.endDate!!.value } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index eb6d0d6a27..7122a29f27 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -19,6 +19,8 @@ import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect import io.matthewnelson.android_feature_viewmodel.updateViewState import io.matthewnelson.concept_coroutines.CoroutineDispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import java.util.* @@ -39,23 +41,40 @@ internal class SubscriptionViewModel @Inject constructor( { private val args: SubscriptionFragmentArgs by savedStateHandle.navArgs() + companion object { + const val DAILY_INTERVAL: String = "daily" + const val WEEKLY_INTERVAL: String = "weekly" + const val MONTHLY_INTERVAL: String = "MONTHLY" + } + fun initSubscription() { viewModelScope.launch(mainImmediate) { - subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).firstOrNull().let { subscription -> + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).distinctUntilChanged().collect { subscription -> if (subscription == null) { updateViewState( SubscriptionViewState.Idle ) } else { + val timeInterval = if (subscription.cron.value.endsWith("* * *")) { + DAILY_INTERVAL + } else if (subscription.cron.value.endsWith("* *")) { + MONTHLY_INTERVAL + } else { + WEEKLY_INTERVAL + } + updateViewState( SubscriptionViewState.SubscriptionLoaded( - subscription + isActive = !subscription.paused, + amount = subscription.amount.value, + timeInterval = timeInterval, + endNumber = subscription.endNumber?.value, + endDate = subscription.endDate ) ) } } } - } fun saveSubscription( @@ -113,7 +132,7 @@ internal class SubscriptionViewModel @Inject constructor( amount = amount, interval = interval, contactId = ContactId(args.argContactId), - chatId = subscription.chat_id, + chatId = subscription.chatId, endDate = endDate?.let { DateTime.getFormatMMMddyyyy(TimeZone.getTimeZone("UTC")).format(it.value) }, endNumber = endNumber?.let { EndNumber(it) } ) @@ -179,24 +198,6 @@ internal class SubscriptionViewModel @Inject constructor( submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.successfully_paused_subscription)) ) - updateViewState( - SubscriptionViewState.SubscriptionLoaded( - Subscription( - id = subscription.id, - subscription.cron, - subscription.amount, - subscription.end_number, - subscription.count, - subscription.end_date, - subscription.ended, - paused = true, - subscription.created_at, - subscription.updated_at, - subscription.chat_id, - subscription.contact_id - ) - ) - ) } } } @@ -218,26 +219,7 @@ internal class SubscriptionViewModel @Inject constructor( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_restart_subscription)) ) } - is Response.Success -> { - updateViewState( - SubscriptionViewState.SubscriptionLoaded( - Subscription( - id = subscription.id, - subscription.cron, - subscription.amount, - subscription.end_number, - subscription.count, - subscription.end_date, - subscription.ended, - paused = false, - subscription.created_at, - subscription.updated_at, - subscription.chat_id, - subscription.contact_id - ) - ) - ) - } + is Response.Success -> {} } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index 885154e0f3..28a07d3981 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -1,5 +1,6 @@ package chat.sphinx.subscription.ui +import chat.sphinx.wrapper_common.DateTime import chat.sphinx.wrapper_subscription.Subscription import io.matthewnelson.concept_views.viewstate.ViewState @@ -11,6 +12,10 @@ internal sealed class SubscriptionViewState: ViewState() object SavingSubscriptionFailed: SubscriptionViewState() class SubscriptionLoaded( - val subscription: Subscription + val isActive: Boolean, + val amount: Long, + val timeInterval: String, + val endNumber: Long?, + val endDate: DateTime?, ) : SubscriptionViewState() } From cfa08f0ec4398d173d84f9a4b451281a21d22b11 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Thu, 2 Sep 2021 13:49:50 -0300 Subject: [PATCH 28/32] More changes requested on PR --- .../chat/sphinx/concept_coredb/SphinxDatabase.sq | 2 +- .../sphinx/feature_repository/SphinxRepository.kt | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq index 3f25e59683..f6d26b6e33 100644 --- a/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq +++ b/sphinx/application/data/concepts/concept-coredb/src/main/sqldelight/chat/sphinx/concept_coredb/SphinxDatabase.sq @@ -763,7 +763,7 @@ WHERE ended = 0 AND contact_id = ? ORDER BY id DESC LIMIT 1; -subscriptionGetByChatId: +subscriptionGetAllByChatId: SELECT * FROM subscriptionDbo WHERE chat_id = ?; diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index 489ac7cf19..a166da9336 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -3655,7 +3655,7 @@ abstract class SphinxRepository( ).collect { loadResponse -> @Exhaustive when (loadResponse) { - LoadResponse.Loading -> { } + is LoadResponse.Loading -> {} is Response.Error -> { response = loadResponse } @@ -3691,8 +3691,9 @@ abstract class SphinxRepository( networkQuerySubscription.putRestartSubscription( subscriptionId ).collect { loadResponse -> + @Exhaustive when (loadResponse) { - LoadResponse.Loading -> { } + is LoadResponse.Loading -> {} is Response.Error -> { response = loadResponse } @@ -3728,8 +3729,9 @@ abstract class SphinxRepository( networkQuerySubscription.putPauseSubscription( subscriptionId ).collect { loadResponse -> + @Exhaustive when (loadResponse) { - LoadResponse.Loading -> { } + is LoadResponse.Loading -> {} is Response.Error -> { response = loadResponse } @@ -3764,8 +3766,9 @@ abstract class SphinxRepository( networkQuerySubscription.deleteSubscription( subscriptionId ).collect { loadResponse -> + @Exhaustive when (loadResponse) { - LoadResponse.Loading -> { } + is LoadResponse.Loading -> {} is Response.Error -> { response = loadResponse } From 58733afa8d3f250f5b59ad18a060709e098d5e95 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Thu, 2 Sep 2021 13:24:01 -0400 Subject: [PATCH 29/32] remove unnecessary suspend modifier (flow is being used) --- .../concept_repository_subscription/SubscriptionRepository.kt | 2 +- .../java/chat/sphinx/feature_repository/SphinxRepository.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt index 23e2cc9d9e..6969f73035 100644 --- a/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt +++ b/sphinx/application/data/concepts/repositories/concept-repository-subscription/src/main/java/chat/sphinx/concept_repository_subscription/SubscriptionRepository.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.Flow interface SubscriptionRepository { - suspend fun getActiveSubscriptionByContactId( + fun getActiveSubscriptionByContactId( contactId: ContactId ): Flow diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index a166da9336..d4644b2418 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -3571,7 +3571,7 @@ abstract class SphinxRepository( SubscriptionDboPresenterMapper(dispatchers) } - override suspend fun getActiveSubscriptionByContactId(contactId: ContactId): Flow = flow { + override fun getActiveSubscriptionByContactId(contactId: ContactId): Flow = flow { emitAll( coreDB.getSphinxDatabaseQueries().subscriptionGetLastActiveByContactId(contactId) .asFlow() From 409473e5752b5d46b9cd10d704cbbab2d76ea0e1 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Thu, 2 Sep 2021 13:25:12 -0400 Subject: [PATCH 30/32] fix response variable usage by switching initizliation to null to inhibit unnecessary instantiation of objects --- .../java/chat/sphinx/feature_repository/SphinxRepository.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt index d4644b2418..ed6eff4687 100644 --- a/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt +++ b/sphinx/application/data/features/feature-repository/src/main/java/chat/sphinx/feature_repository/SphinxRepository.kt @@ -3589,7 +3589,7 @@ abstract class SphinxRepository( endDate: String?, endNumber: EndNumber? ): Response { - var response: Response = Response.Error(ResponseError(("Failed to create subscription"))) + var response: Response? = null applicationScope.launch(mainImmediate) { networkQuerySubscription.postSubscription( @@ -3626,7 +3626,8 @@ abstract class SphinxRepository( } } }.join() - return response + + return response ?: Response.Error(ResponseError(("Failed to create subscription"))) } override suspend fun updateSubscription( From d557b8a6ed757ceb42277599634323b92878f659 Mon Sep 17 00:00:00 2001 From: Tomas Timinskas Date: Thu, 2 Sep 2021 14:28:01 -0300 Subject: [PATCH 31/32] Changes requested on PR --- .../ui/SavingSubscriptionViewState.kt | 11 +++ .../subscription/ui/SubscriptionFragment.kt | 31 +++++-- .../subscription/ui/SubscriptionViewModel.kt | 82 ++++++++++++------- .../subscription/ui/SubscriptionViewState.kt | 4 - 4 files changed, 88 insertions(+), 40 deletions(-) create mode 100644 sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SavingSubscriptionViewState.kt diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SavingSubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SavingSubscriptionViewState.kt new file mode 100644 index 0000000000..41ab7cd879 --- /dev/null +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SavingSubscriptionViewState.kt @@ -0,0 +1,11 @@ +package chat.sphinx.subscription.ui + +import io.matthewnelson.concept_views.viewstate.ViewState + +internal sealed class SavingSubscriptionViewState: ViewState() { + object Idle: SavingSubscriptionViewState() + + object SavingSubscription: SavingSubscriptionViewState() + + object SavingSubscriptionFailed: SavingSubscriptionViewState() +} \ No newline at end of file diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt index 8d5622794d..1d5c881c2d 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionFragment.kt @@ -4,6 +4,7 @@ import android.app.DatePickerDialog import android.content.Context import android.os.Bundle import android.view.View +import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import by.kirich1409.viewbindingdelegate.viewBinding @@ -12,16 +13,21 @@ import chat.sphinx.insetter_activity.addNavigationBarPadding import chat.sphinx.subscription.R import chat.sphinx.subscription.databinding.FragmentSubscriptionBinding import chat.sphinx.wrapper_common.DateTime +import chat.sphinx.wrapper_common.eeemmddhmma import chat.sphinx.wrapper_common.lightning.Sat +import chat.sphinx.wrapper_common.lightning.asFormattedString +import chat.sphinx.wrapper_common.lightning.toSat import chat.sphinx.wrapper_common.toDateTime import chat.sphinx.wrapper_common.toDateTimeWithFormat import dagger.hilt.android.AndroidEntryPoint import io.matthewnelson.android_feature_screens.ui.sideeffect.SideEffectFragment import io.matthewnelson.android_feature_screens.util.gone import io.matthewnelson.android_feature_screens.util.visible +import io.matthewnelson.concept_views.viewstate.collect import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.* +import javax.annotation.meta.Exhaustive @AndroidEntryPoint internal class SubscriptionFragment: SideEffectFragment< @@ -160,8 +166,6 @@ internal class SubscriptionFragment: SideEffectFragment< } (requireActivity() as InsetterActivity).addNavigationBarPadding(layoutConstraintSubscription) } - - viewModel.initSubscription() } override suspend fun onViewStateFlowCollect(viewState: SubscriptionViewState) { @@ -238,11 +242,24 @@ internal class SubscriptionFragment: SideEffectFragment< } } } - is SubscriptionViewState.SavingSubscription -> { - binding.progressBarSubscriptionSave.visible - } - is SubscriptionViewState.SavingSubscriptionFailed -> { - binding.progressBarSubscriptionSave.gone + } + } + + override fun subscribeToViewStateFlow() { + super.subscribeToViewStateFlow() + + onStopSupervisor.scope.launch(viewModel.mainImmediate) { + viewModel.savingSubscriptionViewStateContainer.collect { viewState -> + @Exhaustive + when (viewState) { + is SavingSubscriptionViewState.Idle -> {} + is SavingSubscriptionViewState.SavingSubscription -> { + binding.progressBarSubscriptionSave.visible + } + is SavingSubscriptionViewState.SavingSubscriptionFailed -> { + binding.progressBarSubscriptionSave.gone + } + } } } } diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 7122a29f27..38b63d6def 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -19,9 +19,9 @@ import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect import io.matthewnelson.android_feature_viewmodel.updateViewState import io.matthewnelson.concept_coroutines.CoroutineDispatchers -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.firstOrNull +import io.matthewnelson.concept_views.viewstate.ViewStateContainer +import io.matthewnelson.concept_views.viewstate.value +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import java.util.* import javax.inject.Inject @@ -47,36 +47,56 @@ internal class SubscriptionViewModel @Inject constructor( const val MONTHLY_INTERVAL: String = "MONTHLY" } - fun initSubscription() { - viewModelScope.launch(mainImmediate) { - subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).distinctUntilChanged().collect { subscription -> - if (subscription == null) { - updateViewState( - SubscriptionViewState.Idle - ) - } else { - val timeInterval = if (subscription.cron.value.endsWith("* * *")) { - DAILY_INTERVAL - } else if (subscription.cron.value.endsWith("* *")) { - MONTHLY_INTERVAL - } else { - WEEKLY_INTERVAL - } + private val subscriptionSharedFlow: SharedFlow = flow { + emitAll(subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId))) + }.shareIn( + viewModelScope, + SharingStarted.WhileSubscribed(2_000), + replay = 1, + ) - updateViewState( - SubscriptionViewState.SubscriptionLoaded( - isActive = !subscription.paused, - amount = subscription.amount.value, - timeInterval = timeInterval, - endNumber = subscription.endNumber?.value, - endDate = subscription.endDate - ) + private inner class SubscriptionViewStateContainer: ViewStateContainer(SubscriptionViewState.Idle) { + override val viewStateFlow: StateFlow by lazy { + flow { + subscriptionSharedFlow.collect { subscription -> + emit( + if (subscription != null) { + val timeInterval = if (subscription.cron.value.endsWith("* * *")) { + DAILY_INTERVAL + } else if (subscription.cron.value.endsWith("* *")) { + MONTHLY_INTERVAL + } else { + WEEKLY_INTERVAL + } + + SubscriptionViewState.SubscriptionLoaded( + isActive = !subscription.paused, + amount = subscription.amount.value, + timeInterval = timeInterval, + endNumber = subscription.endNumber?.value, + endDate = subscription.endDate + ) + } else { + SubscriptionViewState.Idle + } ) } - } + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + SubscriptionViewState.Idle, + ) } } + override val viewStateContainer: ViewStateContainer by lazy { + SubscriptionViewStateContainer() + } + + val savingSubscriptionViewStateContainer: ViewStateContainer by lazy { + ViewStateContainer(SavingSubscriptionViewState.Idle) + } + fun saveSubscription( amount: Sat?, interval: String?, @@ -112,7 +132,9 @@ internal class SubscriptionViewModel @Inject constructor( return@launch } - updateViewState(SubscriptionViewState.SavingSubscription) + savingSubscriptionViewStateContainer.updateViewState( + SavingSubscriptionViewState.SavingSubscription + ) subscriptionRepository.getActiveSubscriptionByContactId( ContactId(args.argContactId) @@ -143,7 +165,9 @@ internal class SubscriptionViewModel @Inject constructor( submitSideEffect( SubscriptionSideEffect.Notify(app.getString(R.string.failed_to_save_subscription)) ) - updateViewState(SubscriptionViewState.SavingSubscriptionFailed) + savingSubscriptionViewStateContainer.updateViewState( + SavingSubscriptionViewState.SavingSubscriptionFailed + ) } is Response.Success -> { navigator.popBackStack() diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt index 28a07d3981..2ee8c7607c 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewState.kt @@ -7,10 +7,6 @@ import io.matthewnelson.concept_views.viewstate.ViewState internal sealed class SubscriptionViewState: ViewState() { object Idle: SubscriptionViewState() - object SavingSubscription: SubscriptionViewState() - - object SavingSubscriptionFailed: SubscriptionViewState() - class SubscriptionLoaded( val isActive: Boolean, val amount: Long, From 2e3a2fa405c37eb0bb64645df1bbbadcf887450e Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Thu, 2 Sep 2021 13:33:39 -0400 Subject: [PATCH 32/32] remove unnecessary use of SharedFlow --- .../sphinx/subscription/ui/SubscriptionViewModel.kt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt index 38b63d6def..044a1e99ea 100644 --- a/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt +++ b/sphinx/screens-detail/subscription/subscription/src/main/java/chat/sphinx/subscription/ui/SubscriptionViewModel.kt @@ -12,15 +12,12 @@ import chat.sphinx.wrapper_common.DateTime import chat.sphinx.wrapper_common.dashboard.ContactId import chat.sphinx.wrapper_common.lightning.Sat import chat.sphinx.wrapper_common.subscription.EndNumber -import chat.sphinx.wrapper_subscription.Subscription import dagger.hilt.android.lifecycle.HiltViewModel import io.matthewnelson.android_feature_navigation.util.navArgs import io.matthewnelson.android_feature_viewmodel.SideEffectViewModel import io.matthewnelson.android_feature_viewmodel.submitSideEffect -import io.matthewnelson.android_feature_viewmodel.updateViewState import io.matthewnelson.concept_coroutines.CoroutineDispatchers import io.matthewnelson.concept_views.viewstate.ViewStateContainer -import io.matthewnelson.concept_views.viewstate.value import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import java.util.* @@ -47,18 +44,10 @@ internal class SubscriptionViewModel @Inject constructor( const val MONTHLY_INTERVAL: String = "MONTHLY" } - private val subscriptionSharedFlow: SharedFlow = flow { - emitAll(subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId))) - }.shareIn( - viewModelScope, - SharingStarted.WhileSubscribed(2_000), - replay = 1, - ) - private inner class SubscriptionViewStateContainer: ViewStateContainer(SubscriptionViewState.Idle) { override val viewStateFlow: StateFlow by lazy { flow { - subscriptionSharedFlow.collect { subscription -> + subscriptionRepository.getActiveSubscriptionByContactId(ContactId(args.argContactId)).collect { subscription -> emit( if (subscription != null) { val timeInterval = if (subscription.cron.value.endsWith("* * *")) {