@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/android/CLAUDE.md +38 -9
  3. package/android/CORE.md +11 -4
  4. package/android/RECIPES.md +9 -3
  5. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
  6. package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
  7. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
  8. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
  9. package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
  10. package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
  11. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
  12. package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
  13. package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
  14. package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
  15. package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
  16. package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
  17. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
  18. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
  19. package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
  20. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +17 -7
  21. package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
  22. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
  23. package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
  24. package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
  25. package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
  26. package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
  27. package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
  28. package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +92 -19
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +79 -13
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +29 -12
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +45 -6
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
  49. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -1
  50. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
  51. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +72 -16
  52. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
  53. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
  54. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +164 -13
  55. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
  56. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
  57. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  58. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +107 -4
  59. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
  60. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +3 -3
  61. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
  62. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +131 -9
  63. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
  64. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  65. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
  66. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
  67. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
  68. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
  69. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
  70. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
  71. package/cli/doctor/checks/app.js +61 -13
  72. package/cli/migrate/native.js +18 -0
  73. package/cli/setup/source.js +2 -1
  74. package/cli/setup/steps/mainApplication.js +134 -19
  75. package/docs-md/architecture/transitions.md +12 -7
  76. package/docs-md/containers/modals.md +4 -2
  77. package/docs-md/containers/overlays.md +3 -1
  78. package/docs-md/containers/side-menu.md +4 -2
  79. package/docs-md/containers/tabs.md +2 -0
  80. package/docs-md/flows/forms-in-a-modal.md +1 -1
  81. package/docs-md/help/glossary.md +2 -2
  82. package/docs-md/help/troubleshooting.md +20 -3
  83. package/docs-md/index.json +12 -0
  84. package/docs-md/migration/from-react-native-navigation.md +8 -2
  85. package/docs-md/screen-ui/back-and-gestures.md +8 -2
  86. package/docs-md/screen-ui/system-ui.md +1 -1
  87. package/docs-md/start/installation.md +3 -1
  88. package/ios/CLAUDE.md +17 -5
  89. package/ios/CORE.md +13 -8
  90. package/ios/Contract/ANNCommandAnswer.mm +5 -20
  91. package/ios/Contract/ANNCommandContract.h +15 -0
  92. package/ios/Contract/ANNCommandContract.mm +78 -0
  93. package/ios/Core/ANNAppearanceDispatcher.mm +1 -1
  94. package/ios/Core/ANNErrorHandler.h +1 -1
  95. package/ios/Core/ANNErrorHandler.mm +3 -1
  96. package/ios/Core/ANNEventEmitter.h +1 -1
  97. package/ios/Core/ANNEventEmitter.mm +2 -2
  98. package/ios/Core/ANNJavaScriptCalls.h +34 -0
  99. package/ios/Core/ANNJavaScriptCalls.mm +61 -0
  100. package/ios/Core/ANNNavigationState.h +7 -3
  101. package/ios/Core/ANNNavigationState.mm +10 -5
  102. package/ios/Core/ANNNodeHelper.h +32 -12
  103. package/ios/Core/ANNNodeHelper.mm +100 -3
  104. package/ios/Core/ANNRenderWait.h +15 -1
  105. package/ios/Core/ANNRenderWait.mm +119 -17
  106. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  107. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  108. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  109. package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
  110. package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
  111. package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
  112. package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
  113. package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
  114. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  115. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  116. package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
  117. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  118. package/ios/Layouts/Stack/ANNStackController.mm +26 -3
  119. package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
  120. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  121. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  122. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  123. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  124. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  125. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
  126. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
  127. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
  128. package/ios/Options/ANNOptionsCache.h +7 -6
  129. package/ios/Options/ANNSearchBarOptions.h +4 -3
  130. package/ios/Options/ANNUserChoice.h +57 -0
  131. package/ios/Options/ANNUserChoice.mm +44 -0
  132. package/ios/Options/Params/ANNImageSource.h +61 -0
  133. package/ios/Options/Params/ANNImageSource.mm +101 -0
  134. package/ios/Options/Params/ANNOptionImages.h +37 -0
  135. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  136. package/ios/Options/Params/Image.h +11 -2
  137. package/ios/Options/Params/Image.mm +15 -2
  138. package/ios/Options/Params/ImageParser.h +5 -5
  139. package/ios/Options/Params/ImageParser.mm +28 -46
  140. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  141. package/ios/RECIPES.md +11 -5
  142. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  143. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  144. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  145. package/ios/TopBar/ANNBarButtons.h +9 -5
  146. package/ios/TopBar/ANNBarButtons.mm +211 -63
  147. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  148. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  149. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  150. package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
  151. package/ios/TopBar/ANNSearchPresenter.mm +170 -19
  152. package/ios/TopBar/ANNTopBarPresenter.h +5 -0
  153. package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
  154. package/ios/TurboModules/ANNTurboModule.mm +39 -21
  155. package/lib/module/ARCHITECTURE.md +18 -18
  156. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  157. package/lib/module/api/hooks/useNavigation.js +8 -3
  158. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  159. package/lib/module/api/navigation.js.map +1 -1
  160. package/lib/module/engine/commands.js +9 -7
  161. package/lib/module/engine/commands.js.map +1 -1
  162. package/lib/module/engine/layout.js +13 -9
  163. package/lib/module/engine/layout.js.map +1 -1
  164. package/lib/module/engine/options.js +44 -11
  165. package/lib/module/engine/options.js.map +1 -1
  166. package/lib/module/engine/places.js +44 -7
  167. package/lib/module/engine/places.js.map +1 -1
  168. package/lib/module/engine/props.js +30 -1
  169. package/lib/module/engine/props.js.map +1 -1
  170. package/lib/module/engine/rememberedOptions.js +1 -1
  171. package/lib/module/engine/rememberedOptions.js.map +1 -1
  172. package/lib/module/engine/screens.js +22 -12
  173. package/lib/module/engine/screens.js.map +1 -1
  174. package/lib/module/interfaces/contract/commands.json +101 -0
  175. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
  176. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  177. package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
  178. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  179. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  180. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  181. package/lib/typescript/api/navigation.d.ts +4 -1
  182. package/lib/typescript/api/navigation.d.ts.map +1 -1
  183. package/lib/typescript/engine/commands.d.ts.map +1 -1
  184. package/lib/typescript/engine/options.d.ts +5 -2
  185. package/lib/typescript/engine/options.d.ts.map +1 -1
  186. package/lib/typescript/engine/places.d.ts +5 -2
  187. package/lib/typescript/engine/places.d.ts.map +1 -1
  188. package/lib/typescript/engine/props.d.ts +8 -1
  189. package/lib/typescript/engine/props.d.ts.map +1 -1
  190. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  191. package/lib/typescript/engine/screens.d.ts.map +1 -1
  192. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  193. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  194. package/lib/typescript/interfaces/Events.d.ts +19 -10
  195. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  196. package/lib/typescript/interfaces/options/animations.d.ts +15 -7
  197. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  198. package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
  199. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  200. package/lib/typescript/interfaces/options/modals.d.ts +18 -13
  201. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  202. package/lib/typescript/interfaces/options/system.d.ts +6 -3
  203. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  204. package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
  205. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  206. package/package.json +1 -1
  207. package/src/ARCHITECTURE.md +18 -18
  208. package/src/adapters/NativeANNTurboModule.ts +5 -2
  209. package/src/api/events/navigationEvents.ts +1 -1
  210. package/src/api/hooks/useNavigation.ts +9 -4
  211. package/src/api/navigation.ts +4 -1
  212. package/src/engine/commands.ts +19 -7
  213. package/src/engine/layout.ts +16 -9
  214. package/src/engine/options.ts +39 -11
  215. package/src/engine/places.ts +56 -7
  216. package/src/engine/props.ts +30 -1
  217. package/src/engine/rememberedOptions.ts +4 -1
  218. package/src/engine/screens.tsx +23 -13
  219. package/src/interfaces/ComponentEvents.ts +2 -2
  220. package/src/interfaces/Events.ts +19 -10
  221. package/src/interfaces/contract/commands.json +101 -0
  222. package/src/interfaces/options/animations.ts +15 -7
  223. package/src/interfaces/options/buttons.ts +13 -5
  224. package/src/interfaces/options/modals.ts +18 -13
  225. package/src/interfaces/options/system.ts +6 -3
  226. package/src/interfaces/options/topBar.ts +22 -9
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
14
14
  /// Resolves a command's promise with `value`. Without a resolve block one line goes to the log instead.
15
15
  + (void)resolve:(nullable RCTPromiseResolveBlock)resolve of:(NSString *)method value:(nullable id)value;
16
16
 
17
- /// The log line of a command without a promise: `[ApexNavigation] <method>: <text>`.
17
+ /// The log line of a command without a promise: `[ApexNavigation] <method>: <text>`, a warning (`RCTLogWarn`).
18
18
  + (void)logFailureOf:(NSString *)method text:(NSString *)text;
19
19
 
20
20
  @end
@@ -23,8 +23,10 @@
23
23
  resolve(value);
24
24
  }
25
25
 
26
+ // A warning, not an error: a failed command without a promise (a `mergeOptions` the screen can't take) is the app's
27
+ // to notice, not a red box over it; Android logs it the same way, without a dialog.
26
28
  + (void)logFailureOf:(NSString *)method text:(NSString *)text {
27
- RCTLogError(@"[ApexNavigation] %@: %@", method, text);
29
+ RCTLogWarn(@"[ApexNavigation] %@: %@", method, text);
28
30
  }
29
31
 
30
32
  @end
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
28
28
 
29
29
  #pragma mark - The contract
30
30
 
31
- /// `ANN.AppLaunched`, without a payload. While JavaScript has no listener of it, it is kept and goes once JavaScript
31
+ /// `ANN.AppLaunched`, with an empty payload. While JavaScript has no listener of it, it is kept and goes once JavaScript
32
32
  /// adds the first one. Main queue.
33
33
  - (void)sendAppLaunched;
34
34
 
@@ -60,7 +60,7 @@ static NSUInteger ANNAppLaunchedBundle;
60
60
  return;
61
61
  }
62
62
  ANNAppLaunchedPending = NO;
63
- [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:nil];
63
+ [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:@{}];
64
64
  }
65
65
 
66
66
  + (void)eventEmitterListensToAppLaunched:(ANNTurboEventEmitter *)eventEmitter {
@@ -69,7 +69,7 @@ static NSUInteger ANNAppLaunchedBundle;
69
69
  return;
70
70
  }
71
71
  ANNAppLaunchedPending = NO;
72
- [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:nil];
72
+ [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:@{}];
73
73
  }
74
74
 
75
75
  // Only an `AppLaunched` of the module's own JavaScript or an older one: the module is made while its bundle runs,
@@ -0,0 +1,34 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @class RCTCallInvoker;
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /// The way of the calls JavaScript makes to `ANNTurboModule`, for a step that must come after them. A call without an
8
+ /// answer (`mergeOptions`) leaves the JavaScript thread as it is made, goes to the module's queue (React Native's
9
+ /// `ModuleNativeMethodCallInvoker`, a `dispatch_async` to it) and from there to the main queue (`ANNCommandRunner`):
10
+ /// a step that takes the same three hops after them comes after them on the main queue, as each of the three runs its
11
+ /// work in order. The module tells its call invoker and its queue when React Native made it, and that it went when
12
+ /// React Native invalidates it (a reload). Any thread, except `afterCallsMadeSoFar:`.
13
+ @interface ANNJavaScriptCalls : NSObject
14
+
15
+ - (instancetype)init NS_UNAVAILABLE;
16
+ + (instancetype)new NS_UNAVAILABLE;
17
+
18
+ /// The module of the JavaScript that runs now, its call invoker (the JavaScript thread) and its queue.
19
+ + (void)setCallInvoker:(nullable RCTCallInvoker *)callInvoker
20
+ methodQueue:(nullable dispatch_queue_t)methodQueue
21
+ ofModule:(id)module;
22
+
23
+ /// The module went: with no newer module told since, no JavaScript runs to wait for.
24
+ + (void)moduleWent:(id)module;
25
+
26
+ /// `block` runs on the main queue once every call JavaScript made to the module before the next task of its thread is
27
+ /// applied on the main queue: a task on the JavaScript thread, from there one on the module's queue, from there one on
28
+ /// the main queue. With no JavaScript to ask (none yet, a reload), at once. Main queue. A JavaScript thread that never
29
+ /// runs the task again (its runtime went) never runs `block`: a caller that can't wait for long has a limit of its own.
30
+ + (void)afterCallsMadeSoFar:(dispatch_block_t)block;
31
+
32
+ @end
33
+
34
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,61 @@
1
+ #import "ANNJavaScriptCalls.h"
2
+ #import "ANNAssert.h"
3
+ #import <React/RCTCallInvoker.h>
4
+ #import <React/RCTUtils.h>
5
+ #import <ReactCommon/CallInvoker.h>
6
+
7
+ // The module told last, weakly: a module that went takes its invoker and its queue with it.
8
+ static __weak id ANNCallsModule;
9
+ static RCTCallInvoker *ANNCallsInvoker;
10
+ static dispatch_queue_t ANNCallsQueue;
11
+
12
+ @implementation ANNJavaScriptCalls
13
+
14
+ + (void)setCallInvoker:(RCTCallInvoker *)callInvoker
15
+ methodQueue:(dispatch_queue_t)methodQueue
16
+ ofModule:(id)module {
17
+ @synchronized(self) {
18
+ ANNCallsModule = module;
19
+ ANNCallsInvoker = callInvoker;
20
+ ANNCallsQueue = methodQueue;
21
+ }
22
+ }
23
+
24
+ + (void)moduleWent:(id)module {
25
+ @synchronized(self) {
26
+ if (ANNCallsModule != module) {
27
+ return;
28
+ }
29
+ ANNCallsModule = nil;
30
+ ANNCallsInvoker = nil;
31
+ ANNCallsQueue = nil;
32
+ }
33
+ }
34
+
35
+ + (void)afterCallsMadeSoFar:(dispatch_block_t)block {
36
+ ANNAssertMainQueue();
37
+ RCTCallInvoker *callInvoker = nil;
38
+ dispatch_queue_t queue = nil;
39
+ @synchronized(self) {
40
+ if (ANNCallsModule) {
41
+ callInvoker = ANNCallsInvoker;
42
+ queue = ANNCallsQueue;
43
+ }
44
+ }
45
+ std::shared_ptr<facebook::react::CallInvoker> invoker =
46
+ callInvoker ? callInvoker.callInvoker : nullptr;
47
+ if (!invoker || !queue) {
48
+ block();
49
+ return;
50
+ }
51
+ dispatch_block_t done = [block copy];
52
+ // The JavaScript thread runs the task after the one it runs now, which made the calls of this moment (a commit of
53
+ // React and its layout effects); the module's queue then has those calls before this hop, and the main queue too.
54
+ invoker->invokeAsync([queue, done](facebook::jsi::Runtime &) {
55
+ dispatch_async(queue, ^{
56
+ RCTExecuteOnMainQueue(done);
57
+ });
58
+ });
59
+ }
60
+
61
+ @end
@@ -24,11 +24,15 @@ NS_ASSUME_NONNULL_BEGIN
24
24
  + (void)releaseUpdates;
25
25
 
26
26
  /// The state now, sent when it changed: at the end of a transition, at once after a tab chosen from code. A walk that
27
- /// throws logs one line and sends nothing, so its caller always goes on.
27
+ /// throws logs one line, `[ApexNavigation] <commandName>: could not report the navigation state: <reason>` (as on
28
+ /// Android; `transition` without a command), and sends nothing, so its caller always goes on.
29
+ + (void)updateNowFor:(nullable NSString *)commandName;
30
+
31
+ /// `updateNowFor:` without a command.
28
32
  + (void)updateNow;
29
33
 
30
- /// A command is about to answer: when what shows changed, the new state goes now, before the promise.
31
- + (void)reportBeforeAnswer;
34
+ /// The command `commandName` is about to answer: when what shows changed, the new state goes now, before the promise.
35
+ + (void)reportBeforeAnswerOf:(NSString *)commandName;
32
36
 
33
37
  /// Forgets the last state sent: the next one goes even if it is the same (a new bundle, `AppLaunched`).
34
38
  + (void)reset;
@@ -2,12 +2,12 @@
2
2
  #import "ANNAssert.h"
3
3
  #import "ANNBottomTabsController.h"
4
4
  #import "ANNComponentViewController.h"
5
+ #import "ANNErrorHandler.h"
5
6
  #import "ANNErrorTexts.h"
6
7
  #import "ANNModalCoordinator.h"
7
8
  #import "ANNSlotContainers.h"
8
9
  #import "ApexNavigation+Internal.h"
9
10
  #import "UIViewController+LayoutProtocol.h"
10
- #import <React/RCTLog.h>
11
11
 
12
12
  // Main queue.
13
13
  static BOOL ANNUpdateScheduled;
@@ -58,6 +58,10 @@ static BOOL ANNSameValue(id a, id b) {
58
58
  }
59
59
 
60
60
  + (void)updateNow {
61
+ [self updateNowFor:nil];
62
+ }
63
+
64
+ + (void)updateNowFor:(NSString *)commandName {
61
65
  ANNAssertMainQueue();
62
66
  NSString *screenId = nil;
63
67
  NSString *name = nil;
@@ -74,8 +78,9 @@ static BOOL ANNSameValue(id a, id b) {
74
78
  }
75
79
  } @catch (NSException *exception) {
76
80
  NSString *reason = exception.reason.length > 0 ? exception.reason : exception.name;
77
- RCTLogWarn(@"[ApexNavigation] %@",
78
- [ANNErrorTexts couldNot:@"report the navigation state" reason:reason]);
81
+ [ANNErrorHandler logFailureOf:commandName ?: @"transition"
82
+ text:[ANNErrorTexts couldNot:@"report the navigation state"
83
+ reason:reason]];
79
84
  return;
80
85
  }
81
86
  if (ANNHasLastState && ANNSameValue(screenId, ANNLastScreenId) &&
@@ -89,8 +94,8 @@ static BOOL ANNSameValue(id a, id b) {
89
94
  [ApexNavigation.eventEmitter sendNavigationStateChanged:screenId name:name tab:tab];
90
95
  }
91
96
 
92
- + (void)reportBeforeAnswer {
93
- [self updateNow];
97
+ + (void)reportBeforeAnswerOf:(NSString *)commandName {
98
+ [self updateNowFor:commandName];
94
99
  }
95
100
 
96
101
  + (void)reset {
@@ -3,6 +3,7 @@
3
3
  #import "ANNLayoutInfo.h"
4
4
  #import "ANNNavigationOptions.h"
5
5
  #import "ANNNodePresenter.h"
6
+ #import "ANNUserChoice.h"
6
7
  #import <UIKit/UIKit.h>
7
8
 
8
9
  NS_ASSUME_NONNULL_BEGIN
@@ -24,9 +25,11 @@ FOUNDATION_EXPORT NSString *const ANNContainerReadyHandlerKey;
24
25
  @property(nonatomic, weak, readonly, nullable) UIViewController *node;
25
26
 
26
27
  @property(nonatomic, strong, nullable) ANNLayoutInfo *layoutInfo;
27
- /// The node's own options: its layout options with every `mergeOptions` into it and the choices of the user the modules
28
- /// keep. The setter stores them without the one-shot options and makes every resolved options miss its cache; it is the
29
- /// only way to change them. Never change them in place: `mergeOwnOptions:` or `changeOwnOptions:`.
28
+ /// The node's own options: its layout options with every `mergeOptions` into it and what the core keeps of a merge (the
29
+ /// title component a merged text drops, side colors). The user's choices are not here but in the user layer
30
+ /// (`setUserChoice:forKey:`). The setter stores them without the one-shot options and makes every resolved options miss
31
+ /// its cache; it is the only way to change them. Never change them in place: `mergeOwnOptions:` or
32
+ /// `changeOwnOptions:`.
30
33
  @property(nonatomic, strong, nullable) ANNNavigationOptions *options;
31
34
  /// The default options of the app (`ANNDefaultOptions.current`); setting it does nothing, the defaults are the app's.
32
35
  @property(nonatomic, strong, nullable) ANNNavigationOptions *defaultOptions;
@@ -57,21 +60,38 @@ FOUNDATION_EXPORT NSString *const ANNContainerReadyHandlerKey;
57
60
  /// the layout, else the fallback (when there is none, the parent view controller).
58
61
  - (nullable UIViewController *)layoutParent;
59
62
 
60
- /// `options` merged with `options` given and stored through the setter.
63
+ /// The app's `mergeOptions` into the node: `options` merged with `options` given and stored through the setter. A
64
+ /// choice of the user layer that the merge changes leaves the layer, by Android's rules
65
+ /// (`ANNUserChoice choiceAfterMerge:own:before:after:`): a field given a value other than the app had there (the own
66
+ /// options', else the node's resolution before the merge), or the button, item or scope of the choice gone from the
67
+ /// node's resolution after it; the same value sent again keeps it.
61
68
  - (void)mergeOwnOptions:(ANNNavigationOptions *)options;
62
69
 
63
- /// A change of the own options by the core or a module: `change` gets a copy of them, and the copy is stored through
64
- /// the setter. A presenter or module that keeps a choice of the user in the node's options (a scope, tokens, a
65
- /// checked menu item, a toggled button) writes it this way, never into the options in place.
70
+ /// A change of the own options by the core: `change` gets a copy of them, and the copy is stored through the setter.
71
+ /// For what the core keeps of the app's merges (the title component a merged text drops, side colors), never for a
72
+ /// choice of the user, which goes into the user layer.
66
73
  - (void)changeOwnOptions:(void (^)(ANNNavigationOptions *options))change;
67
74
 
68
- /// The common resolve: a copy of the node's own options with the resolved options of its current child over them, down
69
- /// the visible chain. A new object each call: a container's override may change it in place.
75
+ /// The user layer: the choices the user made in native UI (a toggled button, the check of a single-selection menu, the
76
+ /// search scope, the tokens and suggestions UIKit removed, a renamed title), one per key, apart from the own options.
77
+ /// The resolution writes them over the own options, by identity (`ANNUserChoice applyTo:`): defaults < parents < own <
78
+ /// user. Setting one (nil removes it) makes every resolved options miss its cache, as a change of own options does.
79
+ /// `setDefaultOptions` never touches the layer; it goes with the node.
80
+ - (void)setUserChoice:(nullable ANNUserChoice *)choice forKey:(NSString *)key;
81
+
82
+ /// The choice under `key`, or nil.
83
+ - (nullable ANNUserChoice *)userChoiceForKey:(NSString *)key;
84
+
85
+ /// The common resolve: a copy of the node's own options with its user layer written over them, then the resolved
86
+ /// options of its current child over that, down the visible chain. A new object each call: a container's override may
87
+ /// change it in place.
70
88
  - (ANNNavigationOptions *)resolveOptions;
71
89
 
72
- /// What the node shows with: the default options, then the own options of its parents from the farthest to the nearest,
73
- /// then the node's `resolveOptions`. Cached until any options change (`ANNOptionsCache`), the node has another parent,
74
- /// or a node of its visible chain has another current child. Read-only: copy it before changing anything.
90
+ /// What the node shows with: the default options, then the own options of its parents (each with its user layer) from
91
+ /// the farthest to the nearest, then the node's `resolveOptions`; a node without a current child (a screen) writes its
92
+ /// user layer once more at the end, so a choice also reaches a list the screen takes from its stack or the defaults.
93
+ /// Cached until any options or user layer change (`ANNOptionsCache`), the node has another parent, or a node of its
94
+ /// visible chain has another current child. Read-only: copy it before changing anything.
75
95
  - (ANNNavigationOptions *)resolveOptionsWithDefault;
76
96
 
77
97
  /// `resolveOptionsWithDefault` built now, without the cache: for a reader whose node resolves through more than its
@@ -29,8 +29,14 @@ static NSPointerArray *ANNWeakNodes(NSArray<UIViewController *> *nodes) {
29
29
 
30
30
  NSString *const ANNContainerReadyHandlerKey = @"ApexNavigation.container";
31
31
 
32
+ // Set while a node works out the app's resolution (`appResolution`): no user layer is written. Main queue.
33
+ static BOOL ANNUserLayersLeftOut = NO;
34
+
32
35
  @implementation ANNNodeHelper {
33
36
  ANNNavigationOptions *_options;
37
+ // The user layer: the choices by key, and their keys in the order they were set, the order they are written in.
38
+ NSMutableDictionary<NSString *, ANNUserChoice *> *_userChoices;
39
+ NSMutableArray<NSString *> *_userChoiceKeys;
34
40
  // The ready handlers by key, and their keys in the order they were first set.
35
41
  NSMutableDictionary<NSString *, dispatch_block_t> *_readyHandlers;
36
42
  NSMutableArray<NSString *> *_readyKeys;
@@ -108,10 +114,27 @@ NSString *const ANNContainerReadyHandlerKey = @"ApexNavigation.container";
108
114
  - (void)setDefaultOptions:(ANNNavigationOptions *)defaultOptions {
109
115
  }
110
116
 
117
+ // With a user layer, the choices meet the merge as on Android: they see the app's resolution before it and after it
118
+ // (the merge in place for that, uncached), and those the merge changes leave; the setter then moves the cache once for
119
+ // both, also when a choice's rule throws: the new own options never stay with a cache of the old ones.
111
120
  - (void)mergeOwnOptions:(ANNNavigationOptions *)options {
112
- ANNNavigationOptions *merged = _options ? [_options copy] : [ANNNavigationOptions new];
121
+ ANNAssertMainQueue();
122
+ ANNNavigationOptions *own = _options ?: [ANNNavigationOptions new];
123
+ ANNNavigationOptions *merged = [own copy];
113
124
  [merged mergeOptions:options];
114
- self.options = merged;
125
+ if (_userChoiceKeys.count == 0) {
126
+ self.options = merged;
127
+ return;
128
+ }
129
+ ANNNavigationOptions *before = [self appResolution];
130
+ ANNNavigationOptions *stored = [merged withoutOneShotOptions];
131
+ _options = stored;
132
+ @try {
133
+ ANNNavigationOptions *after = [self appResolution];
134
+ [self userChoicesMeetMerge:options own:own before:before after:after];
135
+ } @finally {
136
+ self.options = stored;
137
+ }
115
138
  }
116
139
 
117
140
  - (void)changeOwnOptions:(void (^)(ANNNavigationOptions *options))change {
@@ -120,10 +143,78 @@ NSString *const ANNContainerReadyHandlerKey = @"ApexNavigation.container";
120
143
  self.options = changed;
121
144
  }
122
145
 
146
+ #pragma mark - The user layer
147
+
148
+ - (void)setUserChoice:(ANNUserChoice *)choice forKey:(NSString *)key {
149
+ ANNAssertMainQueue();
150
+ if (!choice && !_userChoices[key]) {
151
+ return;
152
+ }
153
+ if (!_userChoices) {
154
+ _userChoices = [NSMutableDictionary new];
155
+ _userChoiceKeys = [NSMutableArray new];
156
+ }
157
+ // A choice set again goes last: it is the newest.
158
+ [_userChoiceKeys removeObject:key];
159
+ if (choice) {
160
+ _userChoices[key] = choice;
161
+ [_userChoiceKeys addObject:key];
162
+ } else {
163
+ [_userChoices removeObjectForKey:key];
164
+ }
165
+ [ANNOptionsCache optionsChanged];
166
+ }
167
+
168
+ - (ANNUserChoice *)userChoiceForKey:(NSString *)key {
169
+ return _userChoices[key];
170
+ }
171
+
172
+ // Each choice answers the merge itself: it stays, keeps a part, or goes. The order of the rest stays.
173
+ - (void)userChoicesMeetMerge:(ANNNavigationOptions *)merge
174
+ own:(ANNNavigationOptions *)own
175
+ before:(ANNNavigationOptions *)before
176
+ after:(ANNNavigationOptions *)after {
177
+ for (NSString *key in [_userChoiceKeys copy]) {
178
+ ANNUserChoice *choice = _userChoices[key];
179
+ ANNUserChoice *kept = [choice choiceAfterMerge:merge own:own before:before after:after];
180
+ if (kept == choice) {
181
+ continue;
182
+ }
183
+ if (kept) {
184
+ _userChoices[key] = kept;
185
+ } else {
186
+ [_userChoices removeObjectForKey:key];
187
+ [_userChoiceKeys removeObject:key];
188
+ }
189
+ }
190
+ }
191
+
192
+ // The node's resolution with the defaults and without any user layer: what the app's options alone give (Android's
193
+ // `resolveCurrentOptions().withDefaultOptions`). Built now, never cached: the resolution reads no cache on the way.
194
+ - (ANNNavigationOptions *)appResolution {
195
+ BOOL leftOut = ANNUserLayersLeftOut;
196
+ ANNUserLayersLeftOut = YES;
197
+ @try {
198
+ return [self resolveOptionsWithDefaultUncached];
199
+ } @finally {
200
+ ANNUserLayersLeftOut = leftOut;
201
+ }
202
+ }
203
+
204
+ - (void)applyUserLayerTo:(ANNNavigationOptions *)options {
205
+ if (ANNUserLayersLeftOut) {
206
+ return;
207
+ }
208
+ for (NSString *key in _userChoiceKeys) {
209
+ [_userChoices[key] applyTo:options];
210
+ }
211
+ }
212
+
123
213
  #pragma mark - Resolving
124
214
 
125
215
  - (ANNNavigationOptions *)resolveOptions {
126
216
  ANNNavigationOptions *result = _options ? [_options copy] : [ANNNavigationOptions new];
217
+ [self applyUserLayerTo:result];
127
218
  UIViewController *child = [_node getCurrentChild];
128
219
  if (child) {
129
220
  [result mergeOptions:[child resolveOptions]];
@@ -168,7 +259,9 @@ NSString *const ANNContainerReadyHandlerKey = @"ApexNavigation.container";
168
259
  return [self resolveOfNode:node parents:parents];
169
260
  }
170
261
 
171
- // Built on copies: the defaults never go into a node's own options.
262
+ // Built on copies: the defaults never go into a node's own options. Each parent's user layer goes over its own
263
+ // options; a node without a current child writes its own layer once more at the end, for the lists it takes from its
264
+ // parents or the defaults (a toggle of a button of the stack's). Writing a choice twice gives the same result.
172
265
  - (ANNNavigationOptions *)resolveOfNode:(UIViewController *)node
173
266
  parents:(NSArray<UIViewController *> *)parents {
174
267
  ANNNavigationOptions *result = [ANNDefaultOptions.current copy];
@@ -177,8 +270,12 @@ NSString *const ANNContainerReadyHandlerKey = @"ApexNavigation.container";
177
270
  if (own) {
178
271
  [result mergeOptions:own];
179
272
  }
273
+ [parent.nodeHelper applyUserLayerTo:result];
180
274
  }
181
275
  [result mergeOptions:[node resolveOptions]];
276
+ if (![node getCurrentChild]) {
277
+ [self applyUserLayerTo:result];
278
+ }
182
279
  return result;
183
280
  }
184
281
 
@@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN
5
5
 
6
6
  /// What content a render wait waits for.
7
7
  typedef NS_ENUM(NSInteger, ANNRenderWaitContent) {
8
- /// No content: the bar components alone (a push without `waitForRender`).
8
+ /// No content: the bar components alone.
9
9
  ANNRenderWaitContentNone,
10
10
  /// Each screen of `screens` whose React content isn't ready, by its own ready handler (a root's screens). The limit
11
11
  /// marks ready the screens still waited.
@@ -59,6 +59,20 @@ typedef NS_ENUM(NSInteger, ANNRenderWaitBar) {
59
59
  /// has none).
60
60
  @property(nonatomic) BOOL unknownContentRule;
61
61
 
62
+ /// The content phase ends only once every call JavaScript made by then is applied on the main queue
63
+ /// (`ANNJavaScriptCalls`): the options a screen merges in the commit that mounted it are there before the owner's next
64
+ /// step. Also when the content is there already as the wait starts or takes its screens again (the commit that mounted
65
+ /// it may have run just now): such a phase ends after `start`, through `protect`, while JavaScript has a call invoker;
66
+ /// without one (no JavaScript running) the calls are applied already and the phase ends inside `start`, as without
67
+ /// this. Screens without React content (a native screen) end the phase at once. The limit still ends the phase
68
+ /// meanwhile. Off by default.
69
+ @property(nonatomic) BOOL settlesThroughJavaScript;
70
+
71
+ /// With `ANNRenderWaitBarAfterContent`, the bar phase that follows the content phase counts its limits from the wait's
72
+ /// start, not from its own: the time the content took comes off each kind's limit, and a kind past it by then waits no
73
+ /// more. A bar phase that starts again (`retakeScreens:`) counts from then, as without it. Off by default.
74
+ @property(nonatomic) BOOL barLimitsFromStart;
75
+
62
76
  /// Runs inside `start`, after the ready handlers are set and before the readiness test (the stack parks and renders
63
77
  /// its entry there). A ready handler that runs inside it is left to the test after it.
64
78
  @property(nonatomic, copy, nullable) dispatch_block_t setUp;