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

[MBL-854] Gate Creator Dashboard Behind Feature Flag #1828

Merged
merged 14 commits into from
Jun 29, 2023
Merged

Conversation

scottkicks
Copy link
Contributor

@scottkicks scottkicks commented Jun 15, 2023

📲 What

Gates displaying creator tools (dashboard) behind the new feature flag.

🤔 Why

Since the creator tools (dashboard) is going to be depreciated, and this is a pretty big feature in the app currently, doing so behind a feature flag is safer and gives us more control over the deprecation.

🛠 How

There are a couple of entry points to the creator dashboard.

  1. The Dashboard tab in the main tab bar that only shows when a creator is logged in.
  2. Deep links handled in RootTabBarViewController & ProjectPageViewController

We can use the featureCreatorDashboardEnabled() helper method to gate the following methods responsible for displaying the dashboard tab.

  • RootViewModel.generatePersonalizedViewControllers(): which view controllers to add to the root tab view.
    • Creates an array with .dashboard(isMember: Bool) and .profile(isLoggedIn: Bool)
    • The setViewControllers signal output is then used in the RootTabBarViewController to determine which VC to instantiate for each tab.
  • RootViewModel.tabData(forUser: User): what tab data to use for each tab.
    • Signal is called when there is a currentUser and viewDidLoad, or userLocalePreferencesChangedProperty (logged in/logged out), is emitted.
    • Based on whether the user is a member (determined by whether the user.stats.memberProjectsCount > 0)

The deep link cases can be found:

  • access to switch to the dashboard: line 170 RootTabBarViewController
  • access to feature of the creator dashboard project activities: line 231 RootTabBarViewController
  • access to feature of the creator dashboard creator messages: line 217 RootTabBarViewController
  • creator dashboard from the project page: line 264 ProjectPageViewController

👀 See

Dashboard Enabled Dashboard Disabled
Simulator Screen Recording - iPhone 14 Pro - 2023-06-15 at 09 28 09 Simulator Screen Recording - iPhone 14 Pro - 2023-06-15 at 09 28 58

✅ Acceptance criteria

You can log in as a creator using the therealnativesquad@gmail.com account in 1Password under Kickstarter Native Squad

When Creator Dashboard is not enabled

  • The creator dashboard tab should not be available regardless of whether you’re logged in as a backer or creator, or logged out.

When Creator Dashboard is enabled

  • The creator dashboard tab should be shown as normal to logged in creators only.

⏰ TODO

  • Discuss the fact that testing cases where RemoteConfig flags are enabled isn't possible right now because the RemoteConfig SDK's obj-c initializer isn't available.

the cases where the flag is true is currently not testable due to RemoteConfig's OBJ-C initializer not being available. Still looking into this.
@scottkicks scottkicks self-assigned this Jun 15, 2023
@scottkicks scottkicks added this to the release-5.9.0 milestone Jun 15, 2023
@codecov
Copy link

codecov bot commented Jun 15, 2023

Codecov Report

Merging #1828 (18a1b49) into main (e629b80) will decrease coverage by 0.01%.
The diff coverage is 73.12%.

@@            Coverage Diff             @@
##             main    #1828      +/-   ##
==========================================
- Coverage   84.37%   84.37%   -0.01%     
==========================================
  Files        1276     1276              
  Lines      115679   115816     +137     
  Branches    30764    30793      +29     
==========================================
+ Hits        97604    97714     +110     
- Misses      17009    17036      +27     
  Partials     1066     1066              
Impacted Files Coverage Δ
...ectPage/Controller/ProjectPageViewController.swift 50.23% <0.00%> (-0.17%) ⬇️
...Features/RootTabBar/RootTabBarViewController.swift 4.77% <0.00%> (-0.38%) ⬇️
Library/ViewModels/RootViewModel.swift 0.00% <0.00%> (ø)
...emoteConfig/RemoteConfigFeature+HelpersTests.swift 100.00% <100.00%> (ø)
Library/ViewModels/RootViewModelTests.swift 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@scottkicks
Copy link
Contributor Author

@msadoon re: our sync conversation around testing cases where our Remote Config flags are true...

See the FIXME comments I've left in RootViewModelTests.
You can step through func tabData(forUser...)on line: 498 in RootViewModel to see that even though the remoteConfigClient in AppEnvironment is being set to the Mock with the flag set to true via the tests, the featureCreatorDashboardEnabled() call in the view model is returning false.

My current theory is that its related to the commented out tests in RemoteConfigFeature+HelpersTests that deal with the RemoteConfigValue not initializing.

Lmk if you have any thoughts on what might be happening here. Thanks!

@msadoon
Copy link
Contributor

msadoon commented Jun 15, 2023

@msadoon re: our sync conversation around testing cases where our Remote Config flags are true...

See the FIXME comments I've left in RootViewModelTests. You can step through func tabData(forUser...)on line: 498 in RootViewModel to see that even though the remoteConfigClient in AppEnvironment is being set to the Mock with the flag set to true via the tests, the featureCreatorDashboardEnabled() call in the view model is returning false.

My current theory is that its related to the commented out tests in RemoteConfigFeature+HelpersTests that deal with the RemoteConfigValue not initializing.

Lmk if you have any thoughts on what might be happening here. Thanks!

yep thanks for flagging this in a comment, I will take a look once the pr is ready to review.

@scottkicks scottkicks removed the WIP label Jun 15, 2023
@scottkicks scottkicks marked this pull request as ready for review June 15, 2023 18:29
@scottkicks scottkicks requested a review from msadoon June 15, 2023 18:29
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Overall good job hiding the DashboardViewController on first and subsequent launches.

I found that there are scenarios where the app just expects DashboardViewController is already shown to the user. Deeplinks for example are routes we support to open the dashboard view controller that can be used even if we've hidden it. So with that in mind let's also gate any events that change tabs when the DashboardViewController isn't shown.

The cases found were:

  • access to switch to the dashboard
    • line 170 RootTabBarViewController
  • access to feature of the creator dashboard project activities
    • line 231 RootTabBarViewController
  • access to feature of the creator dashboard creator messages
    • line 217 RootTabBarViewController
  • creator dashboard from the project page
    • line 264 ProjectPageViewController

So my suggestion is to gate these with the featureCreatorDashboardEnabled function. Make that guard check the first thing each function does before letting control flow through. Some tests might need corrections, no need to write both true and false cases, just make the exist ones to pass using MockRemoteConfigClient.

Which leads to the correction for MockRemoteConfigClient

Here is the correction, basically it overrides the value we check boolValue from RemoteConfig with a value we can change bool. We have to do this because RemoteConfigValue has no exposed initializer in the public API from Firebase.

private class MockRemoteConfigValue: RemoteConfigValue {
  var bool = false
  
  override var boolValue: Bool { bool }
}

All your tests should pass then. I ran them myself and they did. Good job on the tests.

I'll do actual in-app testing on the next review if there's no more code changes. I didn't do it on this one because I'd like to test all the final code changes in app. Let's aim to make the next review about testing the functionality and approved.

Library/ViewModels/RootViewModel.swift Outdated Show resolved Hide resolved
Library/ViewModels/RootViewModelTests.swift Outdated Show resolved Hide resolved
Library/ViewModels/RootViewModelTests.swift Show resolved Hide resolved
@scottkicks
Copy link
Contributor Author

@msadoon each of your suggestions have been addressed. Thanks for catching those other cases where the dashboard is shown. I think that should be everything. If anyone manages to get to the dashboard in an unforeseen case, the banner i'll be adding next will at least let them know it'll be going away soon.

also...

it looks like gating the deep link handlers doesn't impact any tests. All existing view controller tests still pass after the changes. And i've already covered the switchToDashboard flow, that these view controller methods end up calling, with unit tests in my previous commits. I think we should be good to go.

@scottkicks scottkicks requested a review from msadoon June 20, 2023 20:48
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Ok, code changes look good and in-app functionality works as expected for the most part, but I found an inconsistency for the tab items:

RPReplay_Final1687362266.MP4

Here's a recording and you'll notice that being logged in as a creator, if I turn off the beta flag, and switch to the profile tab, the user is shown two profile tabs, but the second profile tab is the Dashboard (it should be hidden). Relaunches fixes the issue.

If logged in as a backer then turning on the beta flag causes my profile tab to change to Dashboard but I don't see an additional tab - for Dashboard. Relaunch fixes the issue.

This bug occurs on production as well, without using the Beta Tools at all. Switching the dashboard values on/off there is a transition period before the tab bar fixes itself (takes subsequent relaunches).

So a small UI bug, but it does effect the production build and will end up confusing users, so we should probably fix it.

Can we investigate this issue and fix it? I don't know exactly what might be the cause, but if you want to pair on it, let me know.

Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Ok I uploaded fix for the creator dashboard - but the tests are failing because the GraphSchema needs to be updated - will take a look at that in my Add New Card Removal PR.

@@ -500,13 +500,13 @@ private func tabData(forUser user: User?) -> TabBarItemsData {
(user?.stats.memberProjectsCount ?? 0) > 0
let items: [TabBarItem]

switch (featureCreatorDashboardEnabled(), isMember) {
case (false, _), (true, false):
switch isMember {
Copy link
Contributor

Choose a reason for hiding this comment

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

So the idea is that the tabs are based on the user. And they don't need gating because this tab data is only used for styling - the actual setting of the tabs in the tab bar is done in setViewControllers.

}
}

fileprivate func isDashboardViewControllerDisplayable() -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function just checks if the DashboardTabViewController is available to display.

}
}

fileprivate func setProfileImage(with data: TabBarItemsData, avatarUrl: URL?, index: Int) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Move all the profile downloading to a separate function.

Comment on lines +263 to +266
let featureFlaggedTabBarItemStyle = self
.isDashboardViewControllerDisplayable() ? dashboardTabBarItemStyle :
profileTabBarItemStyle(isLoggedIn: data.isLoggedIn, isMember: data.isMember)
_ = tabBarItem(atIndex: index) ?|> featureFlaggedTabBarItemStyle
Copy link
Contributor

@msadoon msadoon Jun 22, 2023

Choose a reason for hiding this comment

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

This function is called as a result of tabData(forUser and it checks if that tab is available before styling it. So if we sent "Explore, Activity, Search, Dashboard and Profile" to this function - the view controllers may only be "Explore, Activity, Search and Profile". Which means that Dashboard case updates its styling to reflect "Profile" and the Profile case doesn't update anything - the tab doesn't exist. self.tabBar.items is called under the hood for tabBarItem(atIndex: and reflects the view controllers - which are independently set by setViewControllers (whenever that happens).

Copy link
Contributor

Choose a reason for hiding this comment

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

The only trade-off is that we lose the Profile user avatar, if they were a creator logged in with the dashboard hidden. I think its' worth it because this only affects logged in creators and not backers and we're removing creator login functionality anyway.

@scottkicks
Copy link
Contributor Author

@msadoon this makes sense because directly gating tabBarItemsData from the ViewModel can easily cause the setting of the view controllers and the associated tab data to be out of sync. Plus updating from the dashboard can have some lag so this gets around that as well.

Why does the graphschema need to be updated? Since you'll be taking care of that in a different PR this one should wait to be merged in until that is resolved.

@scottkicks scottkicks added the blocked a PR that is blocked for external reasons label Jun 23, 2023
Comment on lines 57 to 64
let (feature, _) = self.features[switchControl.tag]
if feature == RemoteConfigFeature.creatorDashboardEnabled {
NotificationCenter.default.post(
name: Notification.Name.ksr_userLocalePreferencesChanged,
object: nil,
userInfo: nil
)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This still might be worth having for testing. With your solution here, nothing will happen when toggling the feature from the local ff menu

@scottkicks scottkicks removed the blocked a PR that is blocked for external reasons label Jun 29, 2023
@scottkicks scottkicks requested a review from msadoon June 29, 2023 19:49
Copy link
Contributor

@msadoon msadoon left a comment

Choose a reason for hiding this comment

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

Good to go!

@scottkicks scottkicks merged commit ea00943 into main Jun 29, 2023
@scottkicks scottkicks deleted the scott/mbl-854 branch June 29, 2023 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants