Skip to content

Commit

Permalink
MBL-1474: PPO Message creator navigation (#2059)
Browse files Browse the repository at this point in the history
  • Loading branch information
leighdouglas authored Jun 21, 2024
1 parent 2342c96 commit 207fb92
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.SnackbarHostState
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.paging.compose.collectAsLazyPagingItems
import com.kickstarter.features.pledgedprojectsoverview.viewmodel.PledgedProjectsOverviewViewModel
import com.kickstarter.libs.MessagePreviousScreenType
import com.kickstarter.libs.utils.TransitionUtils
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.isDarkModeEnabled
import com.kickstarter.ui.SharedPreferenceKey
import com.kickstarter.ui.activities.AppThemes
import com.kickstarter.ui.compose.designsystem.KickstarterApp
import com.kickstarter.ui.extensions.startCreatorMessageActivity
import com.kickstarter.ui.extensions.transition
import kotlinx.coroutines.launch

class PledgedProjectsOverviewActivity : AppCompatActivity() {

Expand All @@ -39,6 +46,7 @@ class PledgedProjectsOverviewActivity : AppCompatActivity() {

val darkModeEnabled = this.isDarkModeEnabled(env = env)
val lazyListState = rememberLazyListState()
val snackbarHostState = remember { SnackbarHostState() }

val ppoCardPagingSource = viewModel.ppoCardsState.collectAsLazyPagingItems()
val totalAlerts = viewModel.totalAlertsState.collectAsStateWithLifecycle()
Expand All @@ -60,11 +68,26 @@ class PledgedProjectsOverviewActivity : AppCompatActivity() {
modifier = Modifier,
onBackPressed = { onBackPressedDispatcher.onBackPressed() },
lazyColumnListState = lazyListState,
errorSnackBarHostState = snackbarHostState,
ppoCards = ppoCardPagingSource,
totalAlerts = totalAlerts.value
totalAlerts = totalAlerts.value,
onSendMessageClick = { projectName -> viewModel.onMessageCreatorClicked(projectName) }
)
}

LaunchedEffect(Unit) {
viewModel.projectFlow
.collect {
startCreatorMessageActivity(it, previousScreen = MessagePreviousScreenType.PLEDGED_PROJECTS_OVERVIEW)
}
}

viewModel.provideSnackbarMessage {
lifecycleScope.launch {
snackbarHostState.showSnackbar(getString(it))
}
}

onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarHost
import androidx.compose.material.SnackbarHostState
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
Expand Down Expand Up @@ -47,7 +49,9 @@ private fun PledgedProjectsOverviewScreenPreview() {
lazyColumnListState = rememberLazyListState(),
ppoCards = ppoCardList,
totalAlerts = 10,
onBackPressed = {}
onBackPressed = {},
onSendMessageClick = {},
errorSnackBarHostState = SnackbarHostState()
)
}
}
Expand All @@ -58,14 +62,21 @@ fun PledgedProjectsOverviewScreen(
modifier: Modifier,
onBackPressed: () -> Unit,
lazyColumnListState: LazyListState,
errorSnackBarHostState: SnackbarHostState,
ppoCards: LazyPagingItems<PPOCardDataMock>,
totalAlerts: Int = 0
totalAlerts: Int = 0,
onSendMessageClick: (projectName: String) -> Unit
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Scaffold(
snackbarHost = {
SnackbarHost(
hostState = errorSnackBarHostState
)
},
modifier = modifier,
topBar = {
TopToolBar(
Expand Down Expand Up @@ -113,7 +124,7 @@ fun PledgedProjectsOverviewScreen(
imageUrl = it.imageUrl,
imageContentDescription = it.imageContentDescription,
creatorName = it.creatorName,
sendAMessageClickAction = { },
sendAMessageClickAction = { onSendMessageClick(it.projectSlug) },
shippingAddress = it.shippingAddress,
showBadge = it.showBadge,
onActionButtonClicked = { },
Expand All @@ -140,6 +151,7 @@ data class PPOCardDataMock(
val viewType: PPOCardViewType = PPOCardViewType.FIX_PAYMENT,
val onCardClick: () -> Unit = { },
val projectName: String = "This is a project name",
val projectSlug: String = "",
val pledgeAmount: String = "$14.00",
val imageUrl: String = "",
val imageContentDescription: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,70 @@ package com.kickstarter.features.pledgedprojectsoverview.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.kickstarter.R
import com.kickstarter.features.pledgedprojectsoverview.ui.PPOCardDataMock
import com.kickstarter.libs.Environment
import com.kickstarter.models.Project
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx2.asFlow

class PledgedProjectsOverviewViewModel(environment: Environment) : ViewModel() {

private val ppoCards = MutableStateFlow<PagingData<PPOCardDataMock>>(PagingData.empty())
private val totalAlerts = MutableStateFlow<Int>(0)
private var mutableProjectFlow = MutableSharedFlow<Project>()
private var snackbarMessage: (stringID: Int) -> Unit = {}

private val apolloClient = requireNotNull(environment.apolloClientV2())
val ppoCardsState: StateFlow<PagingData<PPOCardDataMock>> = ppoCards.asStateFlow()
val totalAlertsState: StateFlow<Int> = totalAlerts.asStateFlow()

val projectFlow: SharedFlow<Project>
get() = mutableProjectFlow
.asSharedFlow()
.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
)

class Factory(private val environment: Environment) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PledgedProjectsOverviewViewModel(environment) as T
}
}

fun provideSnackbarMessage(snackBarMessage: (Int) -> Unit) {
this.snackbarMessage = snackBarMessage
}

fun onMessageCreatorClicked(projectName: String) {
viewModelScope.launch {
apolloClient.getProject(
slug = projectName,
)
.asFlow()
.onStart {
// TODO emit loading ui state
}.map { project ->
mutableProjectFlow.emit(project)
}.catch {
snackbarMessage.invoke(R.string.Something_went_wrong_please_try_again)
}.collect()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ enum class MessagePreviousScreenType(val trackingString: String) {
CREATOR_BIO_MODAL("creator_bio_modal"),
MESSAGES("messages"),
PROJECT_PAGE("project_page"),
PUSH("push")
PUSH("push"),
PLEDGED_PROJECTS_OVERVIEW("pledged_projects_overview")
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.google.android.play.core.review.ReviewInfo
import com.google.android.play.core.review.ReviewManagerFactory
import com.kickstarter.R
import com.kickstarter.libs.Environment
import com.kickstarter.libs.MessagePreviousScreenType
import com.kickstarter.libs.RefTag
import com.kickstarter.libs.utils.Secrets
import com.kickstarter.libs.utils.TransitionUtils
Expand All @@ -36,6 +37,7 @@ import com.kickstarter.ui.IntentKey
import com.kickstarter.ui.activities.DisclaimerItems
import com.kickstarter.ui.activities.HelpActivity
import com.kickstarter.ui.activities.LoginToutActivity
import com.kickstarter.ui.activities.MessagesActivity
import com.kickstarter.ui.data.PledgeData
import com.kickstarter.ui.data.PledgeReason
import com.kickstarter.ui.data.ProjectData
Expand Down Expand Up @@ -270,3 +272,12 @@ fun Activity.startDisclaimerChromeTab(disclaimerItem: DisclaimerItems, environme

ChromeTabsHelperActivity.openCustomTab(this, UrlUtils.baseCustomTabsIntent(this), uri, fallback)
}

fun Activity.startCreatorMessageActivity(project: Project, previousScreen: MessagePreviousScreenType) {
startActivity(
Intent(this, MessagesActivity::class.java)
.putExtra(IntentKey.MESSAGE_SCREEN_SOURCE_CONTEXT, previousScreen)
.putExtra(IntentKey.PROJECT, project)
.putExtra(IntentKey.BACKING, project.backing())
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kickstarter.features.pledgedprojectsoverview.ui

import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.SnackbarHostState
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
Expand Down Expand Up @@ -29,7 +30,9 @@ class PledgedProjectsOverviewScreenTest : KSRobolectricTestCase() {
modifier = Modifier,
onBackPressed = { backClickedCount++ },
lazyColumnListState = rememberLazyListState(),
ppoCards = ppoCardList
ppoCards = ppoCardList,
errorSnackBarHostState = SnackbarHostState(),
onSendMessageClick = {}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.kickstarter.features.pledgedprojectsoverview.viewmodel

import com.kickstarter.KSRobolectricTestCase
import com.kickstarter.R
import com.kickstarter.mock.factories.ProjectFactory
import com.kickstarter.mock.services.MockApolloClientV2
import com.kickstarter.models.Project
import io.reactivex.Observable
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test

class PledgedProjectsOverviewViewModelTest : KSRobolectricTestCase() {

private lateinit var viewModel: PledgedProjectsOverviewViewModel

@Before
fun setUpEnvrionment() {
viewModel = PledgedProjectsOverviewViewModel.Factory(environment = environment())
.create(PledgedProjectsOverviewViewModel::class.java)
}

@Test
fun `emits_project_when_message_creator_called`() =
runTest {
val projectState = mutableListOf<Project>()

val project = ProjectFactory.successfulProject()
viewModel = PledgedProjectsOverviewViewModel.Factory(
environment = environment().toBuilder()
.apolloClientV2(object : MockApolloClientV2() {
override fun getProject(slug: String): Observable<Project> {
return Observable.just(project)
}
}).build()
).create(PledgedProjectsOverviewViewModel::class.java)

backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.projectFlow.toList(projectState)
}
viewModel.onMessageCreatorClicked("test_project_slug")

assertEquals(
projectState.last(),
project
)
}

@Test
fun `emits_error_when_message_creator_called`() =
runTest {
var snackbarAction = 0
viewModel = PledgedProjectsOverviewViewModel.Factory(
environment = environment().toBuilder()
.apolloClientV2(object : MockApolloClientV2() {
override fun getProject(slug: String): Observable<Project> {
return Observable.error(Throwable("error"))
}
}).build()
).create(PledgedProjectsOverviewViewModel::class.java)

viewModel.provideSnackbarMessage { snackbarAction = it }
viewModel.onMessageCreatorClicked("test_project_slug")

// Should equal error string id
assertEquals(
snackbarAction,
R.string.Something_went_wrong_please_try_again
)
}
}

0 comments on commit 207fb92

Please sign in to comment.