Skip to content

Commit

Permalink
NTV-14 :Add deep link to project comments (#1379)
Browse files Browse the repository at this point in the history
* Add the comment uri check

* Handle deep link for comment activity

* Start the comment activity for deepLink

* Fix format

* Handle KSR
  • Loading branch information
hadia committed Aug 30, 2021
1 parent 4058182 commit e5bcc1f
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 6 deletions.
7 changes: 6 additions & 1 deletion app/src/main/AndroidManifest.xml
Expand Up @@ -264,9 +264,14 @@
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="www.kickstarter.com"
android:host="*.kickstarter.com"
android:pathPrefix="/projects"
android:scheme="https" />
<data
android:host="*.kickstarter.com"
android:pathPrefix="/projects"
android:scheme="ksr" />

<data
android:host="*.kickstarter.com"
android:pathPrefix="/settings/"
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/extensions/UriExt.kt
Expand Up @@ -117,6 +117,11 @@ fun Uri.isProjectSurveyUri(webEndpoint: String): Boolean {
return isKickstarterUri(webEndpoint) && PROJECT_SURVEY.matcher(path()).matches()
}

fun Uri.isProjectCommentUri(webEndpoint: String): Boolean {
return isKickstarterUri(webEndpoint) && PROJECT_COMMENTS_PATTERN.matcher(path())
.matches()
}

fun Uri.isProjectUpdateCommentsUri(webEndpoint: String): Boolean {
return isKickstarterUri(webEndpoint) && PROJECT_UPDATE_COMMENTS_PATTERN.matcher(path()).matches()
}
Expand Down Expand Up @@ -176,6 +181,11 @@ private val PROJECT_SURVEY = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/surveys\\/[a-zA-Z0-9-_]+\\z"
)

// /projects/:creator_param/:project_param/comments
private val PROJECT_COMMENTS_PATTERN = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/comments\\z"
)

// /projects/:creator_param/:project_param/posts/:update_param/comments
private val PROJECT_UPDATE_COMMENTS_PATTERN = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/posts\\/[a-zA-Z0-9-_]+\\/comments\\z"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/kickstarter/ui/IntentKey.java
Expand Up @@ -40,4 +40,5 @@ private IntentKey() {}
public static final String COMMENT = "com.kickstarter.kickstarter.intent_comment";
public static final String COMMENT_CARD_DATA = "com.kickstarter.kickstarter.intent_comment_card_data";
public static final String REPLY_EXPAND = "com.kickstarter.kickstarter.intent_reply_expand";
public static final String DEEP_LINK_SCREEN_PROJECT_COMMENT = "com.kickstarter.kickstarter.deep_link_screen_project_comment";
}
Expand Up @@ -35,6 +35,11 @@ class DeepLinkActivity : BaseActivity<DeepLinkViewModel.ViewModel?>() {
.compose(Transformers.observeForUI())
.subscribe { uri: Uri -> startProjectActivity(uri) }

viewModel.outputs.startProjectActivityForComment()
.compose(bindToLifecycle())
.compose(Transformers.observeForUI())
.subscribe { startProjectActivityForComment(it) }

viewModel.outputs.startProjectActivityForCheckout()
.compose(bindToLifecycle())
.compose(Transformers.observeForUI())
Expand Down Expand Up @@ -70,6 +75,13 @@ class DeepLinkActivity : BaseActivity<DeepLinkViewModel.ViewModel?>() {
finish()
}

private fun startProjectActivityForComment(uri: Uri) {
val projectIntent = projectIntent(uri)
.putExtra(IntentKey.DEEP_LINK_SCREEN_PROJECT_COMMENT, true)
startActivity(projectIntent)
finish()
}

private fun startProjectActivityForCheckout(uri: Uri) {
val projectIntent = projectIntent(uri)
.putExtra(IntentKey.EXPAND_PLEDGE_SHEET, true)
Expand Down
Expand Up @@ -242,7 +242,9 @@ class ProjectActivity :
this.viewModel.outputs.startRootCommentsActivity()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { this.startRootCommentsActivity(it) }
.subscribe {
this.startRootCommentsActivity(it)
}

this.viewModel.outputs.startCreatorBioWebViewActivity()
.compose(bindToLifecycle())
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/kickstarter/viewmodels/DeepLinkViewModel.kt
Expand Up @@ -14,6 +14,7 @@ import com.kickstarter.libs.utils.Secrets
import com.kickstarter.libs.utils.UrlUtils.appendRefTag
import com.kickstarter.libs.utils.UrlUtils.refTag
import com.kickstarter.libs.utils.extensions.isCheckoutUri
import com.kickstarter.libs.utils.extensions.isProjectCommentUri
import com.kickstarter.libs.utils.extensions.isProjectPreviewUri
import com.kickstarter.libs.utils.extensions.isProjectUri
import com.kickstarter.libs.utils.extensions.isSettingsUrl
Expand All @@ -35,6 +36,9 @@ interface DeepLinkViewModel {
/** Emits when we should start the [com.kickstarter.ui.activities.ProjectActivity]. */
fun startProjectActivity(): Observable<Uri>

/** Emits when we should start the [com.kickstarter.ui.activities.ProjectActivity]. */
fun startProjectActivityForComment(): Observable<Uri>

/** Emits when we should start the [com.kickstarter.ui.activities.ProjectActivity] with pledge sheet expanded. */
fun startProjectActivityForCheckout(): Observable<Uri>

Expand All @@ -48,6 +52,7 @@ interface DeepLinkViewModel {
private val startBrowser = BehaviorSubject.create<String>()
private val startDiscoveryActivity = BehaviorSubject.create<Void>()
private val startProjectActivity = BehaviorSubject.create<Uri>()
private val startProjectActivityForComment = BehaviorSubject.create<Uri>()
private val startProjectActivityWithCheckout = BehaviorSubject.create<Uri>()
private val updateUserPreferences = BehaviorSubject.create<Boolean>()
private val finishDeeplinkActivity = BehaviorSubject.create<Void?>()
Expand Down Expand Up @@ -79,12 +84,26 @@ interface DeepLinkViewModel {
.filter {
!it.isProjectPreviewUri(Secrets.WebEndpoint.PRODUCTION)
}
.filter {
!it.isProjectCommentUri(Secrets.WebEndpoint.PRODUCTION)
}
.map { appendRefTagIfNone(it) }
.compose(bindToLifecycle())
.subscribe {
startProjectActivity.onNext(it)
}

uriFromIntent
.filter { ObjectUtils.isNotNull(it) }
.filter {
it.isProjectCommentUri(Secrets.WebEndpoint.PRODUCTION)
}
.map { appendRefTagIfNone(it) }
.compose(bindToLifecycle())
.subscribe {
startProjectActivityForComment.onNext(it)
}

uriFromIntent
.filter { ObjectUtils.isNotNull(it) }
.map { it.isSettingsUrl() }
Expand Down Expand Up @@ -126,6 +145,7 @@ interface DeepLinkViewModel {
.filter { !it.isSettingsUrl() }
.filter { !it.isCheckoutUri(Secrets.WebEndpoint.PRODUCTION) }
.filter { !it.isProjectUri(Secrets.WebEndpoint.PRODUCTION) }
.filter { !it.isProjectCommentUri(Secrets.WebEndpoint.PRODUCTION) }

Observable.merge(projectPreview, unsupportedDeepLink)
.map { obj: Uri -> obj.toString() }
Expand Down Expand Up @@ -167,6 +187,8 @@ interface DeepLinkViewModel {

override fun startDiscoveryActivity(): Observable<Void> = startDiscoveryActivity

override fun startProjectActivityForComment(): Observable<Uri> = startProjectActivityForComment

override fun startProjectActivity(): Observable<Uri> = startProjectActivity

override fun startProjectActivityForCheckout(): Observable<Uri> = startProjectActivityWithCheckout
Expand Down
24 changes: 21 additions & 3 deletions app/src/main/java/com/kickstarter/viewmodels/ProjectViewModel.kt
Expand Up @@ -51,6 +51,7 @@ import rx.subjects.BehaviorSubject
import rx.subjects.PublishSubject
import java.math.RoundingMode
import java.net.CookieManager
import java.util.concurrent.TimeUnit

interface ProjectViewModel {
interface Inputs {
Expand Down Expand Up @@ -307,7 +308,6 @@ interface ProjectViewModel {
private val showUpdatePledge = PublishSubject.create<Pair<PledgeData, PledgeReason>>()
private val showUpdatePledgeSuccess = PublishSubject.create<Void>()
private val startCampaignWebViewActivity = PublishSubject.create<ProjectData>()
private val startCommentsActivity = PublishSubject.create<Pair<Project, ProjectData>>()
private val startRootCommentsActivity = PublishSubject.create<Pair<Project, ProjectData>>()
private val startCreatorBioWebViewActivity = PublishSubject.create<Project>()
private val startCreatorDashboardActivity = PublishSubject.create<Project>()
Expand Down Expand Up @@ -492,9 +492,27 @@ interface ProjectViewModel {
val latestProjectAndProjectData = currentProject.compose<Pair<Project, ProjectData>>(combineLatestPair(projectData))

this.commentsTextViewClicked
.withLatestFrom(latestProjectAndProjectData) { _, project -> project }
.withLatestFrom(latestProjectAndProjectData) { _, project ->
project
}
.compose(bindToLifecycle())
.subscribe(this.startRootCommentsActivity)
.subscribe {
this.startRootCommentsActivity.onNext(it)
}

intent()
.take(1)
.delay(1, TimeUnit.SECONDS, environment.scheduler()) // add delay to wait until activity subscribed to viewmodel
.filter {
it.getBooleanExtra(IntentKey.DEEP_LINK_SCREEN_PROJECT_COMMENT, false)
}
.withLatestFrom(latestProjectAndProjectData) { _, project ->
project
}
.compose(bindToLifecycle())
.subscribe {
this.startRootCommentsActivity.onNext(it)
}

currentProject
.compose<Project>(takeWhen(this.creatorDashboardButtonClicked))
Expand Down
7 changes: 7 additions & 0 deletions app/src/test/java/com/kickstarter/services/UriExtTest.kt
Expand Up @@ -10,6 +10,7 @@ import com.kickstarter.libs.utils.extensions.isKSFavIcon
import com.kickstarter.libs.utils.extensions.isKickstarterUri
import com.kickstarter.libs.utils.extensions.isModalUri
import com.kickstarter.libs.utils.extensions.isNewGuestCheckoutUri
import com.kickstarter.libs.utils.extensions.isProjectCommentUri
import com.kickstarter.libs.utils.extensions.isProjectPreviewUri
import com.kickstarter.libs.utils.extensions.isProjectSurveyUri
import com.kickstarter.libs.utils.extensions.isProjectUpdateCommentsUri
Expand Down Expand Up @@ -134,6 +135,12 @@ class UriExtTest : KSRobolectricTestCase() {
assertFalse(updatesUri.isProjectUpdateCommentsUri(webEndpoint))
}

@Test
fun testUri_isProjectCommentsUri() {
val commentsUri = Uri.parse("https://www.ksr.com/projects/creator/project/comments")
assertTrue(commentsUri.isProjectCommentUri(webEndpoint))
}

@Test
fun testUri_isProjectUpdateUri() {
assertTrue(updateUri.isProjectUpdateUri(webEndpoint))
Expand Down
Expand Up @@ -15,6 +15,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
private val startDiscoveryActivity = TestSubscriber<Void>()
private val startProjectActivity = TestSubscriber<Uri>()
private val startProjectActivityForCheckout = TestSubscriber<Uri>()
private val startProjectActivityForComment = TestSubscriber<Uri>()
private val finishDeeplinkActivity = TestSubscriber<Void>()

fun setUpEnvironment() {
Expand All @@ -23,6 +24,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
vm.outputs.startDiscoveryActivity().subscribe(startDiscoveryActivity)
vm.outputs.startProjectActivity().subscribe(startProjectActivity)
vm.outputs.startProjectActivityForCheckout().subscribe(startProjectActivityForCheckout)
vm.outputs.startProjectActivityForComment().subscribe(startProjectActivityForComment)
vm.outputs.finishDeeplinkActivity().subscribe(finishDeeplinkActivity)
}

Expand All @@ -32,19 +34,21 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
vm.outputs.startDiscoveryActivity().subscribe(startDiscoveryActivity)
vm.outputs.startProjectActivity().subscribe(startProjectActivity)
vm.outputs.startProjectActivityForCheckout().subscribe(startProjectActivityForCheckout)
vm.outputs.startProjectActivityForComment().subscribe(startProjectActivityForComment)
vm.outputs.finishDeeplinkActivity().subscribe(finishDeeplinkActivity)
}

@Test
fun testNonDeepLink_startsBrowser() {
setUpEnvironment()
val url =
"https://www.kickstarter.com/projects/smithsonian/smithsonian-anthology-of-hip-hop-and-rap/comments"
"https://www.kickstarter.com/projects/smithsonian/smithsonian-anthology-of-hip-hop-and-rap/comment"
vm.intent(intentWithData(url))
startBrowser.assertValue(url)
startDiscoveryActivity.assertNoValues()
startProjectActivity.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
startProjectActivityForComment.assertNoValues()
}

@Test
Expand All @@ -57,6 +61,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startDiscoveryActivity.assertNoValues()
startProjectActivity.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
startProjectActivityForComment.assertNoValues()
}

@Test
Expand All @@ -68,9 +73,39 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startProjectActivity.assertNoValues()
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityForCheckout.assertValue(Uri.parse(url))
}

@Test
fun testCommentsDeepLinkWithRefTag_startsProjectActivityForComments() {
setUpEnvironment()
val url =
"https://www.kickstarter.com/projects/fjorden/fjorden-iphone-photography-reinvented/comments?ref=discovery"
vm.intent(intentWithData(url))
startProjectActivity.assertNoValues()
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
startProjectActivityForComment.assertValue(Uri.parse(url))
}

@Test
fun testCommentsDeepLinkWithRefTag_startsProjectActivityForComments_KSR_schema() {
val user =
UserFactory.allTraitsTrue().toBuilder().notifyMobileOfMarketingUpdate(false).build()
val mockUser = MockCurrentUser(user)
val environment = environment().toBuilder()
.currentUser(mockUser)
.build()
setUpEnvironment(environment)
val url = "ksr://www.kickstarter.com/projects/fjorden/fjorden-iphone-photography-reinvented/comments?ref=discovery"
vm.intent(intentWithData(url))
startBrowser.assertNoValues()
finishDeeplinkActivity.assertValueCount(1)
startProjectActivityForComment.assertValue(Uri.parse(url))
}

@Test
fun testCheckoutDeepLinkWithoutRefTag_startsProjectActivity() {
setUpEnvironment()
Expand All @@ -82,6 +117,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startProjectActivity.assertNoValues()
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityForCheckout.assertValue(Uri.parse(expectedUrl))
}

Expand All @@ -94,6 +130,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startProjectActivity.assertValue(Uri.parse(url))
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
}

Expand All @@ -108,6 +145,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startProjectActivity.assertValue(Uri.parse(expectedUrl))
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
}

Expand All @@ -119,6 +157,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startDiscoveryActivity.assertValueCount(1)
startBrowser.assertNoValues()
startProjectActivity.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
}

Expand Down
Expand Up @@ -33,7 +33,9 @@ import com.kickstarter.ui.data.ProjectData
import org.junit.Test
import rx.Observable
import rx.observers.TestSubscriber
import rx.schedulers.TestScheduler
import java.math.RoundingMode
import java.util.concurrent.TimeUnit

class ProjectViewModelTest : KSRobolectricTestCase() {
private lateinit var vm: ProjectViewModel.ViewModel
Expand Down Expand Up @@ -591,6 +593,31 @@ class ProjectViewModelTest : KSRobolectricTestCase() {
this.startRootCommentsActivityivity.assertValues(projectAndData)
}

@Test
fun testStartCommentsActivityFromDeepLink() {
val project = ProjectFactory.project()
val projectData = ProjectDataFactory.project(project)
val projectAndData = Pair.create(project, projectData)
val testScheduler = TestScheduler()

setUpEnvironment(
environment().toBuilder()
.scheduler(testScheduler).build()
)

// Start the view model with a project.
val intent = Intent().apply {
putExtra(IntentKey.DEEP_LINK_SCREEN_PROJECT_COMMENT, true)
putExtra(IntentKey.PROJECT, project)
}

this.vm.intent(intent)

testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)

this.startRootCommentsActivityivity.assertValues(projectAndData)
}

@Test
fun testCommentsActivity_whenCommentsOpenedAndThenCheckoutCompleted_shouldOnlyEmitOnce() {
val project = ProjectFactory.project()
Expand Down

0 comments on commit e5bcc1f

Please sign in to comment.