Skip to content
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

LinkActivity, ViewModel and Navigation #9334

Merged
merged 8 commits into from
Oct 1, 2024
Merged

Conversation

toluo-stripe
Copy link
Contributor

@toluo-stripe toluo-stripe commented Sep 25, 2024

Summary

Link Activity, Navigator and ViewModel

  • LinkViewModel - a base viewmodel that will be used for all link screen viewmodels to maintain a consistent architecture. It holds no business logic and should never.
  • LinkActivity - handles navigation and presenting link screens
  • LinkActivityViewModel
  • Activity & ViewModel tests

I mentioned that we will use typed-navigation here, but that requires upgrading to compose 1.6 . Since we can't upgrade, I decided to add string routes to LinkScreen

Motivation

JIRA

Testing

  • Added tests
  • Modified tests
  • Manually verified

@toluo-stripe toluo-stripe force-pushed the tolu/link-navigator branch 2 times, most recently from 27fc249 to 04bd804 Compare September 25, 2024 17:12
Copy link
Contributor

github-actions bot commented Sep 25, 2024

Diffuse output:

OLD: paymentsheet-example-release-master.apk (signature: V1, V2)
NEW: paymentsheet-example-release-pr.apk (signature: V1, V2)

          │            compressed            │           uncompressed            
          ├───────────┬───────────┬──────────┼───────────┬───────────┬───────────
 APK      │ old       │ new       │ diff     │ old       │ new       │ diff      
──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼───────────
      dex │   3.9 MiB │   3.9 MiB │ +5.2 KiB │   8.6 MiB │   8.6 MiB │ +10.9 KiB 
     arsc │   2.3 MiB │   2.3 MiB │      0 B │   2.3 MiB │   2.3 MiB │       0 B 
 manifest │   5.1 KiB │   5.1 KiB │    +25 B │  25.6 KiB │  25.9 KiB │    +240 B 
      res │ 933.6 KiB │ 933.6 KiB │      0 B │   1.5 MiB │   1.5 MiB │       0 B 
   native │   2.6 MiB │   2.6 MiB │      0 B │     6 MiB │     6 MiB │       0 B 
    asset │   2.9 MiB │   2.9 MiB │    -76 B │   2.9 MiB │   2.9 MiB │     -76 B 
    other │   196 KiB │   196 KiB │     -4 B │ 430.6 KiB │ 430.6 KiB │       0 B 
──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼───────────
    total │  12.8 MiB │  12.8 MiB │ +5.1 KiB │  21.7 MiB │  21.7 MiB │   +11 KiB 

 DEX     │ old   │ new   │ diff                
─────────┼───────┼───────┼─────────────────────
   files │     1 │     1 │   0                 
 strings │ 42635 │ 42686 │ +51 (+3078 -3027)   
   types │ 14134 │ 14162 │ +28 (+3053 -3025)   
 classes │ 11770 │ 11798 │ +28 (+2447 -2419)   
 methods │ 60378 │ 60470 │ +92 (+25138 -25046) 
  fields │ 40043 │ 40089 │ +46 (+17138 -17092) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  242 │  242 │  0   
 entries │ 6247 │ 6247 │  0
APK
     compressed      │     uncompressed      │                                
──────────┬──────────┼───────────┬───────────┤                                
 size     │ diff     │ size      │ diff      │ path                           
──────────┼──────────┼───────────┼───────────┼────────────────────────────────
  3.9 MiB │ +5.2 KiB │   8.6 MiB │ +10.9 KiB │ ∆ classes.dex                  
    127 B │   +127 B │       5 B │      +5 B │ + META-INF/services/E9.A       
    127 B │   +127 B │       5 B │      +5 B │ + META-INF/services/F9.a       
          │   -127 B │           │      -5 B │ - META-INF/services/A9.A       
          │   -127 B │           │      -5 B │ - META-INF/services/B9.a       
  7.8 KiB │    -77 B │   7.7 KiB │     -77 B │ ∆ assets/dexopt/baseline.prof  
  5.1 KiB │    +25 B │  25.9 KiB │    +240 B │ ∆ AndroidManifest.xml          
 53.4 KiB │     -7 B │ 118.3 KiB │       0 B │ ∆ META-INF/CERT.SF             
 50.1 KiB │     +3 B │ 118.2 KiB │       0 B │ ∆ META-INF/MANIFEST.MF         
    971 B │     +1 B │     839 B │      +1 B │ ∆ assets/dexopt/baseline.profm 
──────────┼──────────┼───────────┼───────────┼────────────────────────────────
    4 MiB │ +5.1 KiB │   8.9 MiB │   +11 KiB │ (total)
MANIFEST
@@ -215,2 +215,9 @@
         android:launchMode=1
+        android:name=com.stripe.android.link.LinkActivity
+        android:theme=@style/StripeTransparentTheme
+        />
+    <activity
+        android:autoRemoveFromRecents=true
+        android:configChanges=0x3504
+        android:launchMode=1
         android:name=com.stripe.android.link.LinkForegroundActivity
DEX
STRINGS:

   old   │ new   │ diff              
  ───────┼───────┼───────────────────
   42635 │ 42686 │ +51 (+3078 -3027) 
  
  + CardEditScreen
  + D1
  + E1
  + F1
  + G1
  + GoBack
  + LA7/s;
  + LA7/t;
  + LA7/u;
  + LA7/v;
  + LA7/w;
  + LA7/x;
  + LA7/y;
  + LA7/z;
  + LA8/b;
  + LA8/c;
  + LA8/d;
  + LA8/e;
  + LA8/f;
  + LA8/g;
  + LA8/h;
  + LA8/i;
  + LA8/j;
  + LA8/k;
  + LA8/l;
  + LA8/m;
  + LA8/n;
  + LA8/o;
  + LB7/v;
  + LB7/w;
  + LB7/x;
  + LB9/g;
  + LB9/h;
  + LB9/i;
  + LB9/j;
  + LB9/k;
  + LB9/l;
  + LB9/m;
  + LB9/n;
  + LB9/o;
  + LBa/A;
  + LBa/c;
  + LBa/d;
  + LBa/e;
  + LBa/f;
  + LBa/g;
  + LBa/h;
  + LBa/i;
  + LBa/j;
  + LBa/k;
  + LBa/l;
  + LBa/m;
  + LBa/n;
  + LBa/o;
  + LBa/p;
  + LBa/q;
  + LBa/r;
  + LBa/s;
  + LBa/t;
  + LBa/u;
  + LBa/v;
  + LBa/w;
  + LBa/x;
  + LBa/y;
  + LBa/z;
  + LC0/q;
  + LC5/l;
  + LC5/m;
  + LCa/c;
  + LD0/k;
  + LD6/A;
  + LD6/B;
  + LD6/C;
  + LD6/D;
  + LD6/E;
  + LD6/F;
  + LD6/G;
  + LD6/H;
  + LD6/I;
  + LD6/J;
  + LD6/K;
  + LD6/L;
  + LD6/M;
  + LD6/N;
  + LD6/O;
  + LD6/P;
  + LD6/Q;
  + LD6/S;
  + LD6/T;
  + LD6/b;
  + LD6/c;
  + LD6/d;
  + LD6/e;
  + LD6/f;
  + LD6/g;
  + LD6/h;
  + LD6/i;
  + LD6/j;
  + LD6/k;
  + LD6/l;
  + LD6/m;
  + LD6/n;
  + LD6/o;
  + LD6/p;
  + LD6/q;
  + LD6/r;
  + LD6/s;
  + LD6/t;
  + LD6/u;
  + LD6/v;
  + LD6/w;
  + LD6/x;
  + LD6/y;
  + LD6/z;
  + LD7/A;
  + LD7/B;
  + LD7/C;
  + LD7/D;
  + LD7/E;
  + LD7/y;
  + LD7/z;
  + LDa/c;
  + LE/r0;
  + LE2/A;
  + LE7/b;
  + LE7/c;
  + LE7/d;
  + LE7/e;
  + LE7/f;
  + LE7/g;
  + LE7/h;
  + LE7/i;
  + LE7/j;
  + LE7/k;
  + LE7/l;
  + LE7/m;
  + LE9/A0;
  + LE9/B0;
  + LE9/C0;
  + LE9/D0;
  + LE9/E0;
  + LE9/E;
  + LE9/F0;
  + LE9/F;
  + LE9/G0;
  + LE9/G;
  + LE9/H;
  + LE9/I;
  + LE9/J;
  + LE9/K;
  + LE9/L;
  + LE9/M;
  + LE9/N;
  + LE9/O;
  + LE9/P;
  + LE9/Q;
  + LE9/S;
  + LE9/T;
  + LE9/U;
  + LE9/V;
  + LE9/W;
  + LE9/X;
  + LE9/Y;
  + LE9/Z;
  + LE9/a0;
  + LE9/b0;
  + LE9/c0;
  + LE9/d0;
  + LE9/e0;
  + LE9/f0;
  + LE9/g0;
  + LE9/h0;
  + LE9/i0;
  + LE9/j0;
  + LE9/k0;
  + LE9/l0;
  + LE9/m0;
  + LE9/n0;
  + LE9/o0;
  + LE9/p0;
  + LE9/q0;
  + LE9/r0;
  + LE9/s0;
  + LE9/t0;
  + LE9/u0;
  + LE9/v0;
  + LE9/w0;
  + LE9/x0;
  + LE9/y0;
  + LE9/z0;
  + LEa/c;
  + LEa/d;
  + LF6/b;
  + LF6/c;
  + LF6/d;
  + LF6/e;
  + LF6/f;
  + LF6/g;
  + LF6/h;
  + LF6/i;
  + LF6/j;
  + LF6/k;
  + LF6/l;
  + LG7/A;
  + LG7/B;
  + LG7/C;
  + LG7/D;
  + LG7/E;
  + LG7/F;
  + LG7/G;
  + LG7/H;
  + LG7/I;
  + LG7/J;
  + LG7/K;
  + LG7/L;
  + LG7/c;
  + LG7/d;
  + LG7/e;
  + LG7/f;
  + LG7/g;
  + LG7/h;
  + LG7/i;
  + LG7/j;
  + LG7/k;
  + LG7/l;
  + LG7/m;
  + LG7/n;
  + LG7/o;
  + LG7/p;
  + LG7/q;
  + LG7/r;
  + LG7/s;
  + LG7/t;
  + LG7/u;
  + LG7/v;
  + LG7/w;
  + LG7/x;
  + LG7/y;
  + LG7/z;
  + LG9/a;
  + LG9/b;
  + LG9/c;
  + LG9/d;
  + LG9/e;
  + LG9/f;
  + LG9/g;
  + LG9/h;
  + LG9/i;
  + LG9/j;
  + LG9/k;
  + LG9/l;
  + LG9/m;
  + LG9/n;
  + LG9/o;
  + LG9/p;
  + LG9/q;
  + LG9/r;
  + LG9/s;
  + LG9/t;
  + LG9/u;
  + LG9/v;
  + LG9/w;
  + LH0/U;
  + LH8/A0;
  + LH8/A1;
  + LH8/A;
  + LH8/B0;
  + LH8/B1;
  + LH8/B;
  + LH8/C0;
  + LH8/C1;
  + LH8/C;
  + LH8/D0;
  + LH8/D1;
  + LH8/D;
  + LH8/E0;
  + LH8/E1;
  + LH8/E;
  + LH8/F0;
  + LH8/F1;
  + LH8/F;
  + LH8/G0;
  + LH8/G1;
  + LH8/G;
  + LH8/H0;
  + LH8/H1;
  + LH8/H;
  + LH8/I0;
  + LH8/I1;
  + LH8/I;
  + LH8/J0;
  + LH8/J1;
  + LH8/J;
  + LH8/K0;
  + LH8/K1;
  + LH8/K;
  + LH8/L0;
  + LH8/L1;
  + LH8/L;
  + LH8/M0;
  + LH8/M1;
  + LH8/M;
  + LH8/N0;
  + LH8/N1;
  + LH8/N;
  + LH8/O0;
  + LH8/O1;
  + LH8/O;
  + LH8/P0;
  + LH8/P1;
  + LH8/P;
  + LH8/Q0;
  + LH8/Q1;
  + LH8/Q;
  + LH8/R0;
  + LH8/R1;
  + LH8/S0;
  + LH8/S1;
  + LH8/S;
  + LH8/T0;
  + LH8/T1;
  + LH8/T;
  + LH8/U0;
  + LH8/U1;
  + LH8/U;
  + LH8/V0;
  + LH8/V1;
  + LH8/V;
  + LH8/W0;
  + LH8/W1;
  + LH8/W;
  + LH8/X0;
  + LH8/X1;
  + LH8/X;
  + LH8/Y0;
  + LH8/Y1;
  + LH8/Y;
  + LH8/Z0;
  + LH8/Z1;
  + LH8/Z;
  + LH8/a0;
  + LH8/a1;
  + LH8/a2;
  + LH8/a;
  + LH8/b0;
  + LH8/b1;
  + LH8/b2;
  + LH8/b;
  + LH8/c0;
  + LH8/c1;
  + LH8/c;
  + LH8/d0;
  + LH8/d1;
  + LH8/d;
  + LH8/e0;
  + LH8/e1;
  + LH8/e;
  + LH8/f0;
  + LH8/f1;
  + LH8/f;
  + LH8/g0;
  + LH8/g1;
  + LH8/g;
  + LH8/h0;
  + LH8/h1;
  + LH8/h;
  + LH8/i0;
  + LH8/i1;
  + LH8/i;
  + LH8/j0;
  + LH8/j1;
  + LH8/j;
  + LH8/k0;
  + LH8/k1;
  + LH8/k;
  + LH8/l0;
  + LH8/l1;
  + LH8/l;
  + LH8/m0;
  + LH8/m1;
  + LH8/m;
  + LH8/n0;
  + LH8/n1;
  + LH8/n;
  + LH8/o0;
  + LH8/o1;
  + LH8/o;
  + LH8
...✂

@toluo-stripe toluo-stripe force-pushed the tolu/link-navigator branch 2 times, most recently from f4375bd to fdb91ec Compare September 25, 2024 17:58
@toluo-stripe toluo-stripe changed the title Tolu/link navigator LinkActivity, ViewModel and Navigator Sep 25, 2024
@toluo-stripe toluo-stripe changed the title LinkActivity, ViewModel and Navigator LinkActivity, ViewModel and Navigation Sep 25, 2024
@toluo-stripe toluo-stripe marked this pull request as ready for review September 25, 2024 18:41
@toluo-stripe toluo-stripe requested review from a team as code owners September 25, 2024 18:41
import com.stripe.android.link.ui.verification.VerificationScreen
import com.stripe.android.link.ui.wallet.WalletScreen

internal class LinkActivity : AppCompatActivity() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will navigation between PaymentSheet and LinkActivity look like? Since this is its own activity with its own navigation, I'm assuming this will be totally separate but I'm curious what the transition will look like

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yh, it'll be separate from PaymentSheet. PaymentSheet will launch this with an activityResultCaller.

initialState = LinkState,
) {
override fun actionToResult(action: LinkAction): Flow<LinkResult> {
// These are placeholder actions, they may be removed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this comment mean? If we don't need these actions, I don't think we should merge them in


// This will be used as a base for all Link screen viewmodels to create a consistent architecture
// There should be no more logic here besides updating state and sending effects
internal abstract class LinkViewModel<State, Action, Result, Effect>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the advantage of having this as a superclass to the view models, rather than having fully independent interactors, like we have for PaymentSheetScreen?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on this: I think we should prefer [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance#:~:text=Composition%20over%20inheritance%20(or%20composite,from%20a%20base%20or%20parent) if you'd like to re-use the function to covert actions -> results -> state/effects

import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

// This will be used as a base for all Link screen viewmodels to create a consistent architecture
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these comments are helpful for review! I think these work better as github comments for review only, and shouldn't be checked in. If you want to check in comments like this, you should add proper javadoc.


// This will be used as a base for all Link screen viewmodels to create a consistent architecture
// There should be no more logic here besides updating state and sending effects
internal abstract class LinkViewModel<State, Action, Result, Effect>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following up on this: I think we should prefer [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance#:~:text=Composition%20over%20inheritance%20(or%20composite,from%20a%20base%20or%20parent) if you'd like to re-use the function to covert actions -> results -> state/effects


sealed interface SignUpResult {
data object ShowLoader : SignUpResult
data class SendEffect(val effect: SignUpEffect) : SignUpResult
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having a result called SendEffect is conflating results with effects. It makes more sense to me for this to be something like SignUpSucceeded or SignUpFailed. Then the resultToEffect function would be responsible for determining that e.g. if sign up succeeds, then we should navigate to the wallet screen

Comment on lines 17 to 23
LaunchedEffect("SignUpEffects") {
viewModel.effect.collect { effect ->
when (effect) {
SignUpEffect.NavigateToWallet -> navController.navigate(LinkScreen.Wallet.route)
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to navigate to the wallet screen should be in the view model, not in the UI

}

private fun handleBackPressed() {
dismissWithResult?.invoke(LinkActivityResult.Canceled())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be called something else. handleBackPressed sounds like it's for navigating back between screens, and then maybe exiting the whole activity if the back stack is empty. But the implementation looks like it just cancels.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to leave this for when I start implementing screens, but it makes sense to just add the back navigation now

@toluo-stripe toluo-stripe merged commit 190cffc into master Oct 1, 2024
12 of 13 checks passed
@toluo-stripe toluo-stripe deleted the tolu/link-navigator branch October 1, 2024 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants