From 852dae9231a577bd606e29309fc44b2a9a045f8b Mon Sep 17 00:00:00 2001 From: mtgriego Date: Wed, 9 Oct 2024 09:49:20 -0700 Subject: [PATCH] [NoTicket] various RXjava cleanup (#2149) * various RXjava cleanup * lint --- .../ui/adapters/MessagesAdapter.java | 79 ------------- .../ui/adapters/MessagesAdapter.kt | 77 +++++++++++++ .../ui/intentmappers/ProjectIntentMapper.kt | 57 ---------- .../DiscoveryFragmentViewModelOutputs.java | 46 -------- .../ui/intents/ProjectIntentMapperTest.kt | 104 +----------------- 5 files changed, 80 insertions(+), 283 deletions(-) delete mode 100644 app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.java create mode 100644 app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.kt delete mode 100644 app/src/main/java/com/kickstarter/viewmodels/outputs/DiscoveryFragmentViewModelOutputs.java diff --git a/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.java b/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.java deleted file mode 100644 index 21658495b6..0000000000 --- a/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.kickstarter.ui.adapters; - -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; - -import com.kickstarter.R; -import com.kickstarter.databinding.MessageCenterTimestampLayoutBinding; -import com.kickstarter.databinding.MessageViewBinding; -import com.kickstarter.models.Message; -import com.kickstarter.ui.viewholders.KSViewHolder; -import com.kickstarter.ui.viewholders.MessageCenterTimestampViewHolder; -import com.kickstarter.ui.viewholders.MessageViewHolder; - -import org.joda.time.DateTime; - -import java.util.Collections; -import java.util.List; - -import rx.Observable; - -public final class MessagesAdapter extends KSAdapter { - public MessagesAdapter() {} - - private int getLayoutId(final @NonNull SectionRow sectionRow) { - if (objectFromSectionRow(sectionRow) instanceof DateTime) { - return R.layout.message_center_timestamp_layout; - } else if (objectFromSectionRow(sectionRow) instanceof Message) { - return R.layout.message_view; - } - return R.layout.empty_view; - } - - public void messages(final @NonNull List messages) { - clearSections(); - - // Group messages by start of day. - Observable.from(messages) - .groupBy(message -> message.createdAt().withTimeAtStartOfDay()) - .forEach(dateAndGroupedMessages -> { - addSection(Collections.singletonList(dateAndGroupedMessages.getKey())); - dateAndGroupedMessages - .forEach(message -> addSection(Collections.singletonList(message))); - }); - - notifyDataSetChanged(); - } - - @Override - protected int layout(final @NonNull SectionRow sectionRow) { - return getLayoutId(sectionRow); - } - - @Override - public void onBindViewHolder(final @NonNull KSViewHolder holder, final int position, - final @NonNull List payloads) { - super.onBindViewHolder(holder, position, payloads); - - if (holder instanceof MessageViewHolder) { - // Let the MessageViewHolder know if it is the last position in the RecyclerView. - ((MessageViewHolder) holder).isLastPosition(position == getItemCount() - 1); - } - } - - @Override - protected @NonNull KSViewHolder viewHolder(final @LayoutRes int layout, final @NonNull ViewGroup viewGroup) { - switch (layout) { - case R.layout.message_center_timestamp_layout: - return new MessageCenterTimestampViewHolder(MessageCenterTimestampLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false)); - case R.layout.message_view: - return new MessageViewHolder(MessageViewBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false)); - - default: - throw new IllegalStateException("Invalid layout."); - } - } -} diff --git a/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.kt b/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.kt new file mode 100644 index 0000000000..01d74432f6 --- /dev/null +++ b/app/src/main/java/com/kickstarter/ui/adapters/MessagesAdapter.kt @@ -0,0 +1,77 @@ +package com.kickstarter.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import com.kickstarter.R +import com.kickstarter.databinding.MessageCenterTimestampLayoutBinding +import com.kickstarter.databinding.MessageViewBinding +import com.kickstarter.models.Message +import com.kickstarter.ui.viewholders.KSViewHolder +import com.kickstarter.ui.viewholders.MessageCenterTimestampViewHolder +import com.kickstarter.ui.viewholders.MessageViewHolder +import org.joda.time.DateTime + +class MessagesAdapter : KSAdapter() { + private fun getLayoutId(sectionRow: SectionRow): Int { + if (objectFromSectionRow(sectionRow) is DateTime) { + return R.layout.message_center_timestamp_layout + } else if (objectFromSectionRow(sectionRow) is Message) { + return R.layout.message_view + } + return R.layout.empty_view + } + + fun messages(messages: List) { + clearSections() + + // Group messages by start of day. + messages + .groupBy { it.createdAt().withTimeAtStartOfDay() } + .forEach { dateAndMessages -> + addSection(listOf(dateAndMessages.key)) + dateAndMessages.value.forEach { message -> addSection(listOf(message)) } + } + + notifyDataSetChanged() + } + + override fun layout(sectionRow: SectionRow): Int { + return getLayoutId(sectionRow) + } + + override fun onBindViewHolder( + holder: KSViewHolder, + position: Int, + payloads: List + ) { + super.onBindViewHolder(holder, position, payloads) + + if (holder is MessageViewHolder) { + // Let the MessageViewHolder know if it is the last position in the RecyclerView. + holder.isLastPosition(position == itemCount - 1) + } + } + + override fun viewHolder(@LayoutRes layout: Int, viewGroup: ViewGroup): KSViewHolder { + return when (layout) { + R.layout.message_center_timestamp_layout -> MessageCenterTimestampViewHolder( + MessageCenterTimestampLayoutBinding.inflate( + LayoutInflater.from(viewGroup.context), + viewGroup, + false + ) + ) + + R.layout.message_view -> MessageViewHolder( + MessageViewBinding.inflate( + LayoutInflater.from(viewGroup.context), + viewGroup, + false + ) + ) + + else -> throw IllegalStateException("Invalid layout.") + } + } +} diff --git a/app/src/main/java/com/kickstarter/ui/intentmappers/ProjectIntentMapper.kt b/app/src/main/java/com/kickstarter/ui/intentmappers/ProjectIntentMapper.kt index 3e2a899b27..1b8922e8d4 100644 --- a/app/src/main/java/com/kickstarter/ui/intentmappers/ProjectIntentMapper.kt +++ b/app/src/main/java/com/kickstarter/ui/intentmappers/ProjectIntentMapper.kt @@ -7,13 +7,9 @@ import com.kickstarter.libs.RefTag import com.kickstarter.libs.utils.KsOptional import com.kickstarter.libs.utils.extensions.query import com.kickstarter.models.Project -import com.kickstarter.services.ApiClientType import com.kickstarter.services.ApiClientTypeV2 -import com.kickstarter.services.ApolloClientType import com.kickstarter.services.ApolloClientTypeV2 -import com.kickstarter.services.apiresponses.PushNotificationEnvelope import com.kickstarter.ui.IntentKey -import rx.Observable import java.util.regex.Pattern object ProjectIntentMapper { @@ -48,50 +44,6 @@ object ProjectIntentMapper { .mergeWith(projectFromParceledParam) } - fun project(intent: Intent, apolloClient: ApolloClientType): Observable { - val intentProject = projectFromIntent(intent) - val projectFromParceledProject = - if (intentProject == null) Observable.empty() else Observable.just(intentProject) - .switchMap { project: Project? -> - project?.let { apolloClient.getProject(it) } - } - .startWith(intentProject) - .retry(3) - - val projectFromParceledParam = Observable.just(paramFromIntent(intent) ?: "") - .filter { it.isNotEmpty() } - .switchMap { slug: String? -> - slug?.let { apolloClient.getProject(it) } - } - .retry(3) - return projectFromParceledProject - .mergeWith(projectFromParceledParam).last() - } - - /** - * Returns an observable of projects retrieved from intent data. May hit the API if the intent only contains a project - * param rather than a parceled project. - */ - fun project(intent: Intent, client: ApiClientType): Observable { - val intentProject = projectFromIntent(intent) - val projectFromParceledProject = - if (intentProject == null) Observable.empty() else Observable.just(intentProject) - .flatMap { project: Project? -> - project?.let { client.fetchProject(it) } - } - .startWith(intentProject) - .retry(3) - - val projectFromParceledParam = Observable.just(paramFromIntent(intent) ?: "") - .filter { it.isNotEmpty() } - .flatMap { param: String? -> - param?.let { client.fetchProject(it) } - } - .retry(3) - return projectFromParceledProject - .mergeWith(projectFromParceledParam) - } - /** * Returns an observable of projects retrieved from intent data. May hit the API if the intent only contains a project * param rather than a parceled project. @@ -130,15 +82,6 @@ object ProjectIntentMapper { return io.reactivex.Observable.just(intent.getBooleanExtra(IntentKey.SAVE_FLAG_VALUE, false)) } - /** - * Returns an observable of push notification envelopes from the intent data. This will emit only when the project - * is launched from a push notification. - */ - fun pushNotificationEnvelope(intent: Intent): Observable { - return Observable.just(intent.getParcelableExtra(IntentKey.PUSH_NOTIFICATION_ENVELOPE)) - .ofType(PushNotificationEnvelope::class.java) - } - /** * Gets a parceled project from the intent data, may return `null`. */ diff --git a/app/src/main/java/com/kickstarter/viewmodels/outputs/DiscoveryFragmentViewModelOutputs.java b/app/src/main/java/com/kickstarter/viewmodels/outputs/DiscoveryFragmentViewModelOutputs.java deleted file mode 100644 index 11b496a9aa..0000000000 --- a/app/src/main/java/com/kickstarter/viewmodels/outputs/DiscoveryFragmentViewModelOutputs.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.kickstarter.viewmodels.outputs; - -import android.util.Pair; - -import com.kickstarter.libs.RefTag; -import com.kickstarter.models.Activity; -import com.kickstarter.models.Project; - -import java.util.List; - -import rx.Observable; - -public interface DiscoveryFragmentViewModelOutputs { - /** Emits a list of projects to display.*/ - Observable> projectList(); - - /** - * Emits when the activity feed should be shown - */ - Observable showActivityFeed(); - - /** - * Emits an activity for the activity sample view - */ - Observable activity(); - - /** - * Emits when the login tout activity should be shown - */ - Observable showLoginTout(); - - /** - * Emits a boolean that determines if the onboarding view should be shown - */ - Observable shouldShowOnboardingView(); - - /** - * Emits a Project and RefTag pair when we should start the {@link com.kickstarter.ui.activities.ProjectActivity}. - */ - Observable> startProjectActivity(); - - /** - * Emits an activity when we should start the {@link com.kickstarter.ui.activities.UpdateActivity}. - */ - Observable startUpdateActivity(); -} diff --git a/app/src/test/java/com/kickstarter/ui/intents/ProjectIntentMapperTest.kt b/app/src/test/java/com/kickstarter/ui/intents/ProjectIntentMapperTest.kt index 4fec45e91d..5b2f27efcb 100644 --- a/app/src/test/java/com/kickstarter/ui/intents/ProjectIntentMapperTest.kt +++ b/app/src/test/java/com/kickstarter/ui/intents/ProjectIntentMapperTest.kt @@ -8,73 +8,38 @@ import com.kickstarter.libs.RefTag import com.kickstarter.libs.utils.KsOptional import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.mock.factories.ProjectFactory -import com.kickstarter.mock.factories.PushNotificationEnvelopeFactory -import com.kickstarter.mock.factories.UserFactory -import com.kickstarter.mock.services.MockApiClient import com.kickstarter.mock.services.MockApiClientV2 -import com.kickstarter.mock.services.MockApolloClient import com.kickstarter.mock.services.MockApolloClientV2 import com.kickstarter.models.Project -import com.kickstarter.services.apiresponses.PushNotificationEnvelope import com.kickstarter.ui.IntentKey import com.kickstarter.ui.intentmappers.ProjectIntentMapper import io.reactivex.disposables.CompositeDisposable import org.junit.After import org.junit.Test -import rx.observers.TestSubscriber class ProjectIntentMapperTest : KSRobolectricTestCase() { private val disposables = CompositeDisposable() - @Test - fun testProject_creatorProjectIntent() { - val resultTest = TestSubscriber.create() - val creator = UserFactory.creator() - val creatorProject = ProjectFactory.project() - .toBuilder() - .creator(creator) - .build() - - val intent = Intent().putExtra(IntentKey.PROJECT, creatorProject) - ProjectIntentMapper.project(intent, MockApolloClient()).subscribe(resultTest) - - resultTest.assertValueCount(1) - } - @Test fun testProject_emitsFromProjectParamExtra() { val intent = Intent().putExtra(IntentKey.PROJECT_PARAM, "skull-graphic-tee") - val resultTest = TestSubscriber.create() val resultTestV2 = io.reactivex.subscribers.TestSubscriber.create() - attacheTestResultSubscriber(intent, resultTest, resultTestV2) + attacheTestResultSubscriber(intent, resultTestV2) - resultTest.assertValueCount(1) resultTestV2.assertValueCount(1) } @Test fun testProject_emitsFromProjectParamExtraApollo() { val intent = Intent().putExtra(IntentKey.PROJECT_PARAM, "skull-graphic-tee") - val resultTest = TestSubscriber.create() val resultTestV2 = io.reactivex.subscribers.TestSubscriber.create() - attacheTestResultSubscriber(intent, resultTest, resultTestV2) + attacheTestResultSubscriber(intent, resultTestV2) - resultTest.assertValueCount(1) resultTestV2.assertValueCount(1) } - @Test - fun testProject_emitsTwiceFromProjectExtra() { - val project = ProjectFactory.project() - val intent = Intent().putExtra(IntentKey.PROJECT, project) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.project(intent, MockApiClient()) - .subscribe(resultTest) - resultTest.assertValueCount(2) - } - @Test fun testProject_emitsTwiceFromProjectExtra_V2() { val project = ProjectFactory.project() @@ -85,49 +50,17 @@ class ProjectIntentMapperTest : KSRobolectricTestCase() { resultTest.assertValueCount(2) } - @Test - fun testProject_emitsTwiceFromProjectExtraApollo() { - val project = ProjectFactory.project() - val intent = Intent().putExtra(IntentKey.PROJECT, project) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.project(intent, MockApolloClient()) - .subscribe(resultTest) - resultTest.assertValueCount(1) - } - @Test fun testProject_emitsFromKsrProjectUri() { val uri = Uri.parse("ksr://www.kickstarter.com/projects/1186238668/skull-graphic-tee") val intent = Intent(Intent.ACTION_VIEW, uri) - val resultTest = TestSubscriber.create() val resultTestV2 = io.reactivex.subscribers.TestSubscriber.create() - attacheTestResultSubscriber(intent, resultTest, resultTestV2) + attacheTestResultSubscriber(intent, resultTestV2) - resultTest.assertValueCount(1) resultTestV2.assertValueCount(1) } - @Test - fun testProject_emitsFromKsrProjectUriApollo() { - val uri = Uri.parse("ksr://www.kickstarter.com/projects/1186238668/skull-graphic-tee") - val intent = Intent(Intent.ACTION_VIEW, uri) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.project(intent, MockApolloClient()) - .subscribe(resultTest) - resultTest.assertValueCount(1) - } - - @Test - fun testProject_emitsFromHttpsProjectUri() { - val uri = Uri.parse("https://www.kickstarter.com/projects/1186238668/skull-graphic-tee") - val intent = Intent(Intent.ACTION_VIEW, uri) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.project(intent, MockApiClient()) - .subscribe(resultTest) - resultTest.assertValueCount(1) - } - @Test fun testProject_emitsFromHttpsProjectUri_V2() { val uri = Uri.parse("https://www.kickstarter.com/projects/1186238668/skull-graphic-tee") @@ -138,16 +71,6 @@ class ProjectIntentMapperTest : KSRobolectricTestCase() { resultTest.assertValueCount(1) } - @Test - fun testProject_emitsFromHttpsProjectUriApollo() { - val uri = Uri.parse("https://www.kickstarter.com/projects/1186238668/skull-graphic-tee") - val intent = Intent(Intent.ACTION_VIEW, uri) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.project(intent, MockApolloClient()) - .subscribe(resultTest) - resultTest.assertValueCount(1) - } - @Test fun testRefTag_emitsFromRefTag() { val refTag = RefTag.from("test") @@ -166,24 +89,6 @@ class ProjectIntentMapperTest : KSRobolectricTestCase() { assertNull(resultTest.values().get(0).getValue()) } - @Test - fun testPushNotificationEnvelope_emitsFromEnvelope() { - val envelope = PushNotificationEnvelopeFactory.envelope() - val intent = Intent().putExtra(IntentKey.PUSH_NOTIFICATION_ENVELOPE, envelope) - val resultTest = TestSubscriber.create() - ProjectIntentMapper.pushNotificationEnvelope(intent).subscribe(resultTest) - resultTest.assertValue(envelope) - } - - @Test - fun testPushNotificationEnvelope_doesNotEmitWithoutEnvelope() { - val envelope = PushNotificationEnvelopeFactory.envelope() - val intent = Intent() - val resultTest = TestSubscriber.create() - ProjectIntentMapper.pushNotificationEnvelope(intent).subscribe(resultTest) - resultTest.assertNoValues() - } - @Test fun testProjectHasSaveQueryFromUri() { assertTrue(ProjectIntentMapper.hasSaveQueryFromUri("ksr://www.kickstarter.com/projects/1186238668/skull-graphic-tee?save=true".toUri())) @@ -196,11 +101,8 @@ class ProjectIntentMapperTest : KSRobolectricTestCase() { } private fun attacheTestResultSubscriber( intent: Intent, - resultTest: TestSubscriber?, resultTestV2: io.reactivex.subscribers.TestSubscriber ) { - ProjectIntentMapper.project(intent, MockApolloClient()) - .subscribe(resultTest) ProjectIntentMapper.project(intent, MockApolloClientV2()).subscribe { resultTestV2.onNext(it)