Hiding dashboard cards: generalize logic for storing preferences#20365
Conversation
guarani
left a comment
There was a problem hiding this comment.
Thank you for the PR!
It is the first MR in the series.
Just to double-check, is the "MR" here meant to be PR or does it stand for something else?
The keys for the cards are generated dynamically. The format of the keys and the data matches the format previously used for Prompts and Blaze cards, ensuring the existing data is migrated automatically.
I left a comment in the code about your approach which involves a new key for storing the Prompts key and thus migration. I'm eager to hear your thoughts.
Test 1
- Open the Dashboard and hide the Prompts card
In the case of the Prompts card, hiding corresponds to tapping "Turn off prompts", right? I noticed that the copy isn't consistent here because it's also called "Show Prompts" in site settings, but AFAIK they do the same thing. This would be an unrelated & separate UI issue though, so no need to address it.
Test 2
- Remove the Prompts card in the current version of the app
- Update the app to the version from this PR
- Verify that the Prompts card is still hidden
If we go with a different approach that doesn't involve changing the key names for prompts, I think this particular test would no longer be needed. For the current approach you've used, I think it's a valuable addition here to the regression tests 💯
Test 3
- Repeat tests for the Blaze card
I'm not sure the testing steps are very clear here. Only Test 1 seems applicable; however, the Blaze card (to my knowledge) doesn't have the option to turn it back on (although I'm not very familiar with the feature so might have missed something).
Test 2 doesn't seem applicable because the key for Blaze (blaze-card-enabled-site-settings) isn't changed by this PR. Could you make more explicit testing steps? This would help me understand how the change has been tested and provides me with more straightforward testing scenarios to follow myself.
PR submission checklist:
- I have completed the Regression Notes.
When filling out the release notes, please try to answer them as best you can and if they're not applicable, answer with a sentence or two explaining why. Thanks!
| func setEnabled(_ isEnabled: Bool, for card: DashboardCard) { | ||
| var settings = getSettings(for: card) | ||
| settings[siteID] = isEnabled | ||
| repository.set(settings, forKey: makeKey(for: card)) | ||
|
|
||
| NotificationCenter.default.post(name: .blogDashboardPersonalizationSettingsChanged, object: self) | ||
| } | ||
|
|
||
| private func getSettings(for card: DashboardCard) -> [String: Bool] { | ||
| repository.dictionary(forKey: makeKey(for: card)) as? [String: Bool] ?? [:] | ||
| } | ||
| } |
There was a problem hiding this comment.
If we go ahead with this change, keys for Blogging Prompts and Blaze will be dynamically generated. I see how this can help provide a direct mapping between DashboardCards and their persistence key without having to make that mapping explicit. However, I'm not aware of any other advantages.
The biggest disadvantage I see is that the raw key is no longer searchable in the codebase. This could come up when inspecting NSUserDefaults' content and figuring out where the keys found there are defined in the codebase. The other downside is that "key migration" logic is needed for the prompt key. There's no perfect time to remove this logic to clean up the code base, someone will have to figure out when few enough devices are using the old key that it's safe to clean this up.
What are you thoughts on these points?
There was a problem hiding this comment.
I was also debating whether to keep the constants or generate the keys dynamically. The advantage of generating them dynamically is that there are fewer changes needed when new cards that require personalization are added. The disadvantage, as you pointed out, is that they keys are no longer visible in UPRUConstants.
The biggest disadvantage I see is that the raw key is no longer searchable in the codebase.
I can suggest two options how to address it:
- Add static keys (one per card) to
UPRUConstantsand create aDashboardCard→ key mapping - Move
makeKey(for card: DashboardCard)method I added toUPRUConstants, so it's more visible
I'll be happy to change it to what you think is the best option. I think making these keys visible/searchable is a good idea, but there are dozens of UserDefaults keys defined outside of UPRUConstants, so I assumed these constants were only for the keys used in UserPersistentRepositoryUtility.
The other downside is that "key migration" logic is needed for the prompt key.
The data migration is not needed because I added .prompts as an exception:
private func makeKey(for card: DashboardCard) -> String {
if card == .prompts {
// This key was defined statically in the previous versions, and the
// naming convention wasn't matching the other keys, so it had to be
// special-cased to avoid losing data.
return "prompts-enabled-site-settings"
}
return "\(card.rawValue)-card-enabled-site-settings"
}I think we can keep it like this indefinitely: it's a small exception and is documented.
There was a problem hiding this comment.
The disadvantage, as you pointed out, is that they keys are no longer visible in
UPRUConstants.
I want to clarify that the disadvantage I pointed out above relates to seachability, not visibility. I don't think making the keys searchable requires them to be defined in the same place.
I think making these keys visible/searchable is a good idea, but there are dozens of UserDefaults keys defined outside of
UPRUConstants, so I assumed these constants were only for the keys used inUserPersistentRepositoryUtility.
I don't have an issue with defining the keys for the Blaze, Prompts, etc. cards in a different place.
The data migration is not needed because I added .prompts as an exception:
Thanks, I misunderstood it the first time around. The prompt key remains unchanged, so no migration 👍
- Add static keys (one per card) to
UPRUConstantsand create aDashboardCard→ key mapping
If you don't think the dynamic keys are required for this implementation, going with the above seems like a good option for now. Dynamic keys can always be revisited if it's clear they make adding cards significantly easier.
There was a problem hiding this comment.
I think I got your point about searchable. I tried adding the keys to UPRUConstants, but there were private, which I didn't want to change. So I added the static strings directly to makeKey and retested the migration (or lack thereof) – looks good now. Commit with the change.
Yeah, MR (merge request) is a term that's more often used in GitLab, which is what I currently use at work.
Yes, there is a bit of inconsistency with the Site Settings and with other cards where we'll be adding "Hide this" action to the context menus. I also noticed it and asked Chris about it. For Site Settings, the decision is to remove the "Show prompts" toggle entirely and keep it only in the new "Personalize Home Tab" screen. As for the "Turn off prompts", the naming is different because turning off prompts does a bit more than just hide the dashboard card: it also removes the prompts from the FAB screen.
Updated. The goal was to make sure there are no functional changes introduced by this PR. |
guarani
left a comment
There was a problem hiding this comment.
Yeah, MR (merge request) is a term that's more often used in GitLab, which is what I currently use at work.
Gotcha! I wasn't familiar with the term.
Yes, there is a bit of inconsistency with the Site Settings and with other cards where we'll be adding "Hide this" action to the context menus. I also noticed it and asked Chris about it. For Site Settings, the decision is to remove the "Show prompts" toggle entirely and keep it only in the new "Personalize Home Tab" screen. As for the "Turn off prompts", the naming is different because turning off prompts does a bit more than just hide the dashboard card: it also removes the prompts from the FAB screen.
I like this idea of only having it in the "Personalize Home Tab" screen" and no longer in the Site Settings. Makes sense 👍
Updated. The goal was to make sure there are no functional changes introduced by this PR.
Thanks! The updated tests look good. I'll hold off re-testing since I suggested a change.
| func setEnabled(_ isEnabled: Bool, for card: DashboardCard) { | ||
| var settings = getSettings(for: card) | ||
| settings[siteID] = isEnabled | ||
| repository.set(settings, forKey: makeKey(for: card)) | ||
|
|
||
| NotificationCenter.default.post(name: .blogDashboardPersonalizationSettingsChanged, object: self) | ||
| } | ||
|
|
||
| private func getSettings(for card: DashboardCard) -> [String: Bool] { | ||
| repository.dictionary(forKey: makeKey(for: card)) as? [String: Bool] ?? [:] | ||
| } | ||
| } |
There was a problem hiding this comment.
The disadvantage, as you pointed out, is that they keys are no longer visible in
UPRUConstants.
I want to clarify that the disadvantage I pointed out above relates to seachability, not visibility. I don't think making the keys searchable requires them to be defined in the same place.
I think making these keys visible/searchable is a good idea, but there are dozens of UserDefaults keys defined outside of
UPRUConstants, so I assumed these constants were only for the keys used inUserPersistentRepositoryUtility.
I don't have an issue with defining the keys for the Blaze, Prompts, etc. cards in a different place.
The data migration is not needed because I added .prompts as an exception:
Thanks, I misunderstood it the first time around. The prompt key remains unchanged, so no migration 👍
- Add static keys (one per card) to
UPRUConstantsand create aDashboardCard→ key mapping
If you don't think the dynamic keys are required for this implementation, going with the above seems like a good option for now. Dynamic keys can always be revisited if it's clear they make adding cards significantly easier.
This section is meant for something like "this change could effect XYZ functionality". It's might be read by beta testers so it helps for us to list potential unintended areas of impact to help direct any testing efforts toward these.
Sounds good!
Here I'd encourage you to mention that you did add automated tests to this PR 💯 |
|
The CI failure happens when working from a fork. I'll push your commits to a separate temporary PR to make CI go green here. |
|
Thanks, @guarani. Looks like the checks have passed. I'm looking forward to getting my first PR merged 😊 |
|
It seems GitHub is down: https://www.githubstatus.com/ |
Related issue: #20296
Hey 👋. I'm working on adding an ability to hide any of the dashboard cards. It is the first PR in the series.
The PR contains the necessary groundwork for allowing any dashboard cards to be hidden. It introduces a new
BlogDashboardPersonalizationServicetype to manage these preferences. The keys for the cards are generated dynamically. The format of the keys and the data matches the format previously used for Prompts and Blaze cards, ensuring the existing data is migrated automatically.Testing Instructions
There are no functional changes. I performed the following tests to verify there are no regressions:
Test 1
Test 2
Test 3
Test 4
Regression Notes
The existing feature for managing card visibility ("Turn off prompts", "Hide this" on the Blaze card)
I tested the scenarios from the "Testing Instructions" that included installing the previous version of the app and performing the migration.
I added full test coverage for the new service for managing preferences and also tested its integration with the service responsible for parsing the cards and deciding which ones to display
PR submission checklist:
RELEASE-NOTES.txtif necessary.