-
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
[MBL-854] Gate Creator Dashboard Behind Feature Flag #1828
Conversation
…feature flag pre-planning
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.
Codecov Report
@@ 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
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
@msadoon re: our sync conversation around testing cases where our Remote Config flags are true... See the FIXME comments I've left in My current theory is that its related to the commented out tests in 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. |
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.
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.
RemoteConfigValue has no exposed initializer in the public API from Firebase so this is how can change boolValue for testing.
@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. |
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.
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.
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.
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 { |
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 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 { |
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.
This function just checks if the DashboardTabViewController
is available to display.
} | ||
} | ||
|
||
fileprivate func setProfileImage(with data: TabBarItemsData, avatarUrl: URL?, index: Int) { |
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.
Move all the profile downloading to a separate function.
let featureFlaggedTabBarItemStyle = self | ||
.isDashboardViewControllerDisplayable() ? dashboardTabBarItemStyle : | ||
profileTabBarItemStyle(isLoggedIn: data.isLoggedIn, isMember: data.isMember) | ||
_ = tabBarItem(atIndex: index) ?|> featureFlaggedTabBarItemStyle |
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.
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).
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 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.
@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. |
let (feature, _) = self.features[switchControl.tag] | ||
if feature == RemoteConfigFeature.creatorDashboardEnabled { | ||
NotificationCenter.default.post( | ||
name: Notification.Name.ksr_userLocalePreferencesChanged, | ||
object: nil, | ||
userInfo: 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.
This still might be worth having for testing. With your solution here, nothing will happen when toggling the feature from the local ff menu
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 to go!
📲 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.
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.RootTabBarViewController
to determine which VC to instantiate for each tab.RootViewModel.tabData(forUser: User)
: what tab data to use for each tab.The deep link cases can be found:
line 170
RootTabBarViewController
line 231
RootTabBarViewController
line 21
7RootTabBarViewController
line 264
ProjectPageViewController
👀 See
✅ 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
When Creator Dashboard is enabled
⏰ TODO