-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NT-280] Manage pledge screen from CTA container #831
Conversation
SwiftFormat found issues:
Generated by 🚫 Danger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly questions
@@ -135,6 +135,26 @@ public final class ProjectPamphletViewController: UIViewController { | |||
self?.goToRewards(project: project, refTag: refTag) | |||
} | |||
|
|||
self.viewModel.outputs.goToManageViewPledge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing a lot of inconsistencies between the naming of VMs output
signal and the private
action on the controller...do you think it would be worth it cleaning this up..
So for example:
self.viewModel.outputs.goToManageViewPledge
would trigger self?.goToManageViewPledge
, etc...
For the deprecated
ones I'd suggest we simply add it as a suffix:
self.viewModel.outputs.goToViewBackingDeprecated
links to self?.goToViewBacking
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to change the private
actions to match the name of the output. Will change that.
Regarding the deprecated
naming, I'm following the naming convention that we adopted for deprecated classes (DeprecatedCheckoutViewModel
, DeprecatedRewardPledgeViewController
, DeprecatedRewardCell
etc)
.configuredWith( | ||
project: project, reward: reward | ||
) | ||
pledgeViewController.navigationItem.leftBarButtonItem = UIBarButtonItem( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we've decided to set this externally on DeprecatedRewardPledgeViewController
?
If we do this inside of the controller and set self.navigationItem
(like we do everywhere else we will be able to remove this code as well the identical code in ProjectPamphletContentViewController
.
But maybe I'm missing the reason here? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a rush to unblock the other tickets I had copied the code from somewhere else. Good catch.
if AppEnvironment.current.device.userInterfaceIdiom == .pad { | ||
let nav = UINavigationController(rootViewController: backingViewController) | ||
|> \.modalPresentationStyle .~ .formSheet | ||
self.present(nav, animated: true, completion: nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we omit completion
handler?
|> \.modalPresentationStyle .~ .formSheet | ||
self.present(nav, animated: true, completion: nil) | ||
} else { | ||
self.navigationController?.pushViewController(backingViewController, animated: true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I know we've previously discussed the fact that we're presenting manage screen over the previous context using .formSheet
...but do we really want to push
on non-iPad devices? Just trying to understand the decision behind this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The from BackingViewController
, we can access Messages (the vc's navigation controller pushes the MessagesViewController). If we present the BackingViewController
modally instead, we are no longer able to see the messages, since it requires a navigation controller to work
@@ -4,7 +4,7 @@ import Prelude | |||
import UIKit | |||
|
|||
protocol PledgeCTAContainerViewDelegate: AnyObject { | |||
func pledgeCTAButtonTapped() | |||
func pledgeCTAButtonTapped(_ state: PledgeStateCTAType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe with
state?
func pledgeCTAButtonTapped(with state: PledgeStateCTAType)
then we call it with more context like so
self?.delegate?.pledgeCTAButtonTapped(with: state)
Dunno which one is better ¯_(ツ)_/¯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me gusta :)
@@ -34,6 +34,12 @@ public protocol ProjectPamphletViewModelOutputs { | |||
/// Emits a (project, isLoading) tuple used to configure the pledge CTA view | |||
var configurePledgeCTAView: Signal<(Either<Project, ErrorEnvelope>, Bool), Never> { get } | |||
|
|||
var goToDeprecatedManagePledge: Signal<PledgeData, Never> { get } | |||
|
|||
var goToDeprecatedViewBacking: Signal<(Project, User?), Never> { get } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we're deprecating stuff but looking at these parameters I wonder if we can improve this by declaring BackingData
typealias, etc. for (Project, User?)
tuple
Just a suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea
.map(unpack) | ||
|
||
let goToManagePledge = ctaButtonTapped | ||
.filter(canShowManageViewPledgeScreen(_:_:state:)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like canShowManageViewPledgeScreen
does not need refTag
? Could omit and have more readable call site canShowManageViewPledgeScreen(_:state:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with that is ctaButtonTapped
emits (project, refTag, state). If we omit the refTag, the compiler will complain because the types emitted don't match the function arguments, thus the point free syntax will no longer be possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see!
Thanks for clarifying this...I don't know whether a stylistic thing like being able to call something with point-free
expression would overweight the code debt of passing argument that's not necessary.
In this case, I'd consider whether non-point-free expression would be more suitable (less code + less debt).
.filter { (project, _, state) in canShowManageViewPledgeScreen(project, state:state) }
or
.filter { canShowManageViewPledgeScreen($0.0, state:$0.2) }
let goToManagePledge = ctaButtonTapped | ||
.filter(canShowManageViewPledgeScreen(_:_:state:)) | ||
.map { (project, refTag, _) -> PledgeData in | ||
let r = reward(from: project.personalization.backing, inProject: project) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we name this reward
(it should fit on the screen and be more descriptive)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I did first, but kept getting the error Variable used within its own initial value
. Any suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, good point!
How about something like this:
.map { (project, refTag, _) -> PledgeData in
PledgeData(
project: project,
reward: reward(from: project.personalization.backing, inProject: project),
refTag: refTag
)
}
or
.map {
PledgeData(
project: $0.0, reward: reward(from: $0.0.personalization.backing, inProject: $0.0), refTag: $0.1
)
}
(but that might be more than confusing due to the repetition of those variables)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a language limitation I'd be OK with leaving it as is .. your decision! 🥂
.filter { _ in featureNativeCheckoutPledgeViewIsEnabled() } | ||
|
||
self.goToDeprecatedManagePledge = ctaButtonTapped | ||
.filter(shouldGoToDeprecatedManagePledge(_:_:state:)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing with refTag
...is this because we'll need it in the future but don't need it just now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah... we will only need the refTag for the manage pledge context. Here it's possibleto change because we don't need the refTag, so we can map the value creating a new tuple without it before filtering:
self.goToDeprecatedViewBacking = ctaButtonTapped
.map { (project, _, state) in (project, state) }
.filter(shouldGoToDeprecatedViewBacking(_:state:))
.map { project, _ in
return BackingData(project, AppEnvironment.current.currentUser)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd probably try to carry over as little unused things as possible..this makes it easier to reason about code and will be less confusing in the future (read in a week when we forget WTF was this) :D
self.goToDeprecatedManagePledge = ctaButtonTapped | ||
.filter(shouldGoToDeprecatedManagePledge(_:_:state:)) | ||
.map { (project, refTag, _) -> PledgeData in | ||
let r = reward(from: project.personalization.backing, inProject: project) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for the naming
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple additional comments..starting to look good!!!
func pledgeCTAButtonTapped() { | ||
self.viewModel.inputs.backThisProjectTapped() | ||
func pledgeCTAButtonTapped(with state: PledgeStateCTAType) { | ||
self.viewModel.inputs.pledgeCTAButtonTapped(state) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're keeping the input this way? I thought we would also make it more descriptive (i.e. input.pledgeCTAButtonTapped(with: state)
?
.map(unpack) | ||
|
||
let goToManagePledge = ctaButtonTapped | ||
.filter(canShowManageViewPledgeScreen(_:_:state:)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see!
Thanks for clarifying this...I don't know whether a stylistic thing like being able to call something with point-free
expression would overweight the code debt of passing argument that's not necessary.
In this case, I'd consider whether non-point-free expression would be more suitable (less code + less debt).
.filter { (project, _, state) in canShowManageViewPledgeScreen(project, state:state) }
or
.filter { canShowManageViewPledgeScreen($0.0, state:$0.2) }
let goToManagePledge = ctaButtonTapped | ||
.filter(canShowManageViewPledgeScreen(_:_:state:)) | ||
.map { (project, refTag, _) -> PledgeData in | ||
let r = reward(from: project.personalization.backing, inProject: project) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, good point!
How about something like this:
.map { (project, refTag, _) -> PledgeData in
PledgeData(
project: project,
reward: reward(from: project.personalization.backing, inProject: project),
refTag: refTag
)
}
or
.map {
PledgeData(
project: $0.0, reward: reward(from: $0.0.personalization.backing, inProject: $0.0), refTag: $0.1
)
}
(but that might be more than confusing due to the repetition of those variables)
let goToManagePledge = ctaButtonTapped | ||
.filter(canShowManageViewPledgeScreen(_:_:state:)) | ||
.map { (project, refTag, _) -> PledgeData in | ||
let r = reward(from: project.personalization.backing, inProject: project) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a language limitation I'd be OK with leaving it as is .. your decision! 🥂
.filter { _ in featureNativeCheckoutPledgeViewIsEnabled() } | ||
|
||
self.goToDeprecatedManagePledge = ctaButtonTapped | ||
.filter(shouldGoToDeprecatedManagePledge(_:_:state:)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd probably try to carry over as little unused things as possible..this makes it easier to reason about code and will be less confusing in the future (read in a week when we forget WTF was this) :D
return userCanSeeNativeCheckout() && (state == .pledge || state == .viewRewards) | ||
} | ||
|
||
private func canShowManageViewPledgeScreen( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After we remove the unused params, these function names should fit on one line making it a bit more readable IMO.
private func canShowRewardsScreen(state: PledgeStateCTAType?) -> Bool { .. }
private func canShowManageViewPledgeScreen(_ project: Project, state: PledgeStateCTAType?) -> Bool { .. }
private func shouldGoToDeprecatedViewBacking(_ project: Project, state: PledgeStateCTAType?) -> Bool { .. }
private func shouldGoToDeprecatedManagePledge(_ project: Project, state: PledgeStateCTAType?) -> Bool { .. }
@@ -686,7 +862,7 @@ final class ProjectPamphletViewModelTests: TestCase { | |||
self.goToRewardsProject.assertDidNotEmitValue() | |||
self.goToRewardsRefTag.assertDidNotEmitValue() | |||
|
|||
self.vm.inputs.backThisProjectTapped() | |||
self.vm.inputs.pledgeCTAButtonTapped(.pledge) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See, I think this is where a better naming convention would help us a little Tapped(with: .pledge)
..What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh right. I changed in one place but missed that one. Will change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good nino!
I had couple suggestions but they shouldn't hold this review up...they're up to your judgment.
Cheers~
|
||
public typealias BackingData = (Project, User?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like
// BackingViewModel.swift
fileprivate let projectAndBackerProperty = MutableProperty<(Project, User?)?>(nil)
could use this data type...should we update it?
// BackingViewModel.swift
fileprivate let projectAndBackerProperty = MutableProperty<BackingData?>(nil)
.map(unpack) | ||
|
||
let goToManagePledge = ctaButtonTapped | ||
.filter { canShowManageViewPledgeScreen($0.0, state:$0.2) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this in action, it might actually be more readable to name those params. I'll leave this out to you, I'm OK with both - but wanted to raise it in case you were in favour of the other option.
.filter { project, _, state in canShowManageViewPledgeScreen(project, state: state) }
(this of course would apply to other places in this PR.
📲 What
🤔 Why
🛠 How
RewardsCollectionViewController
toProjectPamphletViewController
👀 See
✅ Acceptance criteria
With
featureNativeCheckoutPledgeView
enabled.Back this project
button. The Rewards Carousel should be presented.Manage
button. TheManageViewPledgeViewController
(blank) should be presented.View your pledge
button. TheManageViewPledgeViewController
(blank) should be presented.Note:
By tapping to view your pledge, note that the title shown is
Manage your pledge
. The logic to change the title based of the pledge and project states will be implemented in a different PR.With
featureNativeCheckoutPledgeView
disabled.On the Rewards Carousel screen:
Back this project
button. The Rewards Carousel should be presented.Manage
button. TheDeprecatedRewardPledgeViewController
should be presented.View your pledge
button. TheBackingViewController
should be presented.