Refactor FXIOS-15120 [Translations] Make leading page actions generic and decouple from ToolbarActions#33859
Conversation
💪 Quality guardian4 tests files modified. You're a champion of test coverage! 🚀 🧩 Neat PieceThis PR changes 578 lines. It's a substantial update, 💬 Description craftsmanGreat PR description! Reviewers salute you 🫡 ✅ New file code coverageNo new file detected so code coverage gate wasn't ran. ✅ Existing file code coverageAll modified files meet their thresholds. Client.app: Coverage: 40.76
Generated by 🚫 Danger Swift against 4cab273 |
ih-codes
left a comment
There was a problem hiding this comment.
Hi @razvanlitianu ! I'm just taking a first pass on this one (haven't run the code). I have a few concerns about some of the refactors, my comments will explain a bit more. Mostly I'm not sure I'm a big fan of how some of the lower-level reducer helper methods are no longer specialized to a certain action type, but appear to only really do anything productive for specific action types. But correct me if I'm wrong on those on a case-by-case basis!
I will take another look after hearing your thoughts on this!
| @MainActor | ||
| private static func leadingPageActions( | ||
| action: ToolbarAction, | ||
| action: Action, |
There was a problem hiding this comment.
Should this be specialized to a ToolbarAction? Or does this method really handle other action types? If it's the latter, in my opinion we should have a guard at the top of the method that clarifies the valid action types.
There was a problem hiding this comment.
Added guard action is ToolbarAction || action is TranslationsAction else { return actions } at the top to explicitly document and enforce the valid action types handled by this path.
| // Pulls the `translationConfiguration` payload off whichever action carries it. | ||
| // Both `ToolbarAction` (e.g. urlDidChange) and `TranslationsAction` (e.g. didStartTranslatingPage) | ||
| // can drive the leading translate icon, so the reducer reads the field generically. | ||
| private static func translationConfiguration(from action: Action) -> TranslationConfiguration? { | ||
| if let toolbarAction = action as? ToolbarAction { return toolbarAction.translationConfiguration } | ||
| if let translationsAction = action as? TranslationsAction { return translationsAction.translationConfiguration } | ||
| return nil | ||
| } | ||
|
|
There was a problem hiding this comment.
This logic is essentially duplicated here and in TranslationsMiddleware. 🤔
Can you think of a better way to organize this utility method? How about as an optional init for TranslationConfiguration itself?
There was a problem hiding this comment.
Moved the extraction logic into TranslationConfiguration.init?(from action: Action) on the struct itself (TranslationsConfiguration.swift) to centralize the conversion logic. Removed the private helper from AddressBarState, as well as the identical unused/dead copy from TranslationsMiddleware.
| // MARK: - Helper | ||
| @MainActor | ||
| private static func toolbarPosition(action: ToolbarAction) -> AddressToolbarPosition? { | ||
| private static func toolbarPosition(action: Action) -> AddressToolbarPosition? { |
There was a problem hiding this comment.
Similar comment here about specializing the Action... Do we need this change?
|
|
||
| @MainActor | ||
| private static func shouldUseAlternativeLocationColor(action: ToolbarAction) -> Bool { | ||
| private static func shouldUseAlternativeLocationColor(action: Action) -> Bool { |
There was a problem hiding this comment.
Another similar comment. It seems the caller should check the action type and we should leave these methods specialized instead of introducing additional checks if the only actions this function can process are ToolbarActions.
If the call sites get really messy I can understand doing this, perhaps. Can you demonstrate that, if that's the problem? In that case I hope we can add a guard here at the top showing this method really only handles the ToolbarActions.
There was a problem hiding this comment.
Reverted both helpers back to ToolbarAction parameters. In leadingPageActions, the caller now casts to ToolbarAction? and only invokes the helpers when non-nil. For the TranslationsAction path, the alternative location color is computed inline from toolbarState. This also simplified the internal logic by removing redundant casts, e.g. (action as? ToolbarAction)?.toolbarPosition became action.toolbarPosition.
| // Both `ToolbarAction` (lifecycle) and `TranslationsAction` (translation events) reach this | ||
| // handler — translation actions carry only `isTranslationsEnabled` + payload, so every other | ||
| // ToolbarAction-only field falls back to state when the action is not a `ToolbarAction`. | ||
| let toolbarAction = action as? ToolbarAction |
| return state.copyWithUpdates( | ||
| isTranslationsEnabled: action.isTranslationsEnabled ?? state.isTranslationsEnabled | ||
| ) | ||
| } |
There was a problem hiding this comment.
Now that this reducer is supporting so many different action types, what do you think about writing it like this? 🤔
switch action.self {
case is TranslationSettingsMiddlewareAction:
// TODO...
case is TranslationSettingsViewAction:
// TODO...
// etc...
default:
return defaultState(from: state)
}
Alternatively, we could switch over the actionType instead, like the ToolbarAction reducer.
Just a thought...!
There was a problem hiding this comment.
Restructured the logic from chained if let casts to a switch action { case let action as Type: ... } pattern, making the supported action types immediately visible and easier to scan.
| /// If auto-translate is enabled, triggers translation to the user's top preferred language. | ||
| /// Returns `true` if auto-translation was initiated (caller should skip manual offer). | ||
| private func tryAutoTranslate(for action: ToolbarAction, on tab: Tab?) async -> Bool { | ||
| private func tryAutoTranslate(for action: Action, on tab: Tab?) async -> Bool { |
There was a problem hiding this comment.
Same gripe from me here too haha 😄
There was a problem hiding this comment.
Narrowed the method signatures from Action to WindowUUID, since that’s the only value these paths actually use. This updated tryAutoTranslate, handleUpdatingTranslationIcon, retrieveTranslations, performTranslation, and handleErrorFromTranslatingPage to take windowUUID: WindowUUID instead of for action: Action.
|
Oh, one other thing. I remember chatting with @cyndichin about making the leading toolbar actions more generic at a higher level than this... My memory is a bit foggy but maybe she remembers more of this discussion. In any, case this PR is already large, but there may be more work we want to do in this area before we mark the parent ticket as completed, or perhaps we break this into 2 tickets. 🤔 |
|
This pull request has conflicts when rebasing. Could you fix it @razvanlitianu? 🙏 |
Thanks for the heads up, could you elaborate on what you mean by “more generic at a higher level”? Happy to break this into a follow-up ticket once I understand the intended direction. |
… and decouple from ToolbarActions Move 5 translation lifecycle cases (didStartTranslatingPage, translationCompleted, receivedTranslationLanguage, didReceiveErrorTranslating, didTranslationSettingsChange) off ToolbarActionType onto TranslationsActionType. Teach AddressBarState.reducer to observe TranslationsAction so the translate icon updates without forcing translation state changes through the toolbar reducer. Collapse the toggleTranslationsEnabled double-dispatch in TranslationSettingsMiddleware into a single TranslationsAction. Both the toolbar reducer and the settings state reducer now react to the same action, removing the redux smell flagged in PR #32529. Remove the in-code apology comment on ToolbarActionType.
…uplication - Add guard for valid action types in leadingPageActions - Move translationConfiguration extraction to TranslationConfiguration.init?(from:) - Revert toolbarPosition and shouldUseAlternativeLocationColor to ToolbarAction - Restructure TranslationSettingsState reducer with switch pattern - Narrow tryAutoTranslate call chain from Action to WindowUUID
9fafe70 to
4cab273
Compare
📜 Tickets
Jira ticket
Github issue
💡 Description
Move 5 translation lifecycle cases (didStartTranslatingPage, translationCompleted, receivedTranslationLanguage, didReceiveErrorTranslating, didTranslationSettingsChange) off ToolbarActionType onto TranslationsActionType. Teach AddressBarState.reducer to observe TranslationsAction so the translate icon updates without forcing translation state changes through the toolbar reducer.
Collapse the toggleTranslationsEnabled double-dispatch in TranslationSettingsMiddleware into a single TranslationsAction. Both the toolbar reducer and the settings state reducer now react to the same action, removing the redux smell flagged in PR #32529.
Remove the in-code apology comment on ToolbarActionType.
🎥 Demos
Demo
📝 Checklist