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

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 (125) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +147 -51
  3. package/android/CLAUDE.md +10 -4
  4. package/android/CORE.md +4 -1
  5. package/android/RECIPES.md +4 -3
  6. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
  7. package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
  8. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
  9. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
  10. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
  11. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
  12. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
  13. package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
  14. package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +101 -87
  15. package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
  16. package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
  17. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
  18. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +12 -8
  19. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +12 -11
  20. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +2 -2
  21. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
  22. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +1 -1
  23. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -3
  24. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +8 -1
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +2 -1
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +2 -1
  29. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +5 -5
  30. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
  31. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +85 -27
  32. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
  33. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
  34. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
  35. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +45 -1
  36. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
  37. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
  38. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
  39. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
  42. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
  43. package/cli/app.js +1 -1
  44. package/cli/project.js +6 -2
  45. package/cli/setup/index.js +13 -0
  46. package/cli/setup/steps/appDelegate.js +66 -11
  47. package/cli/setup/steps/sceneDelegate.js +108 -0
  48. package/docs-md/architecture/transitions.md +1 -1
  49. package/docs-md/guides/deep-links.md +15 -33
  50. package/docs-md/start/installation.md +1 -0
  51. package/ios/Animations/ANNTransitionAnimator.mm +13 -11
  52. package/ios/Animations/ANNTransitionWait.mm +6 -7
  53. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  54. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  55. package/ios/Animations/Elements/ANNElementTransition.h +7 -5
  56. package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
  57. package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
  58. package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
  59. package/ios/CLAUDE.md +10 -2
  60. package/ios/CORE.md +3 -2
  61. package/ios/Contract/ANNCommandAnswer.h +4 -3
  62. package/ios/Contract/ANNCommandAnswer.mm +12 -2
  63. package/ios/Contract/ANNCommandRunner.h +2 -2
  64. package/ios/Contract/ANNCommandRunner.mm +1 -1
  65. package/ios/Contract/ANNErrorTexts.h +6 -2
  66. package/ios/Contract/ANNErrorTexts.mm +18 -10
  67. package/ios/Core/ANNDeepLinkQueue.h +20 -1
  68. package/ios/Core/ANNDeepLinkQueue.mm +107 -12
  69. package/ios/Core/ANNErrorHandler.h +8 -3
  70. package/ios/Core/ANNErrorHandler.mm +17 -1
  71. package/ios/Core/ANNEventContract.h +246 -0
  72. package/ios/Core/ANNEventContract.mm +225 -0
  73. package/ios/Core/ANNEventEmitter.h +5 -1
  74. package/ios/Core/ANNEventEmitter.mm +117 -80
  75. package/ios/Core/ANNHinge.mm +6 -4
  76. package/ios/Core/ANNSceneDelegate.mm +25 -15
  77. package/ios/Core/ANNSystemUI.h +3 -2
  78. package/ios/Core/ANNSystemUI.mm +7 -0
  79. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
  80. package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
  81. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  82. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  83. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  84. package/ios/Layouts/Root/ANNRootCoordinator.mm +12 -3
  85. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  86. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  87. package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
  88. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  89. package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
  90. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
  91. package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
  92. package/ios/Options/ANNSheetOptions.h +4 -1
  93. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  94. package/ios/RECIPES.md +2 -1
  95. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  96. package/ios/TurboModules/ANNTurboModule.mm +5 -2
  97. package/lib/module/ARCHITECTURE.md +7 -4
  98. package/lib/module/api/app/createNavigation.js +2 -0
  99. package/lib/module/api/app/createNavigation.js.map +1 -1
  100. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  101. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  102. package/lib/module/interfaces/contract/errors.json +275 -0
  103. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  104. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  105. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  106. package/lib/typescript/interfaces/Events.d.ts +9 -0
  107. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  108. package/lib/typescript/interfaces/options/animations.d.ts +17 -13
  109. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  110. package/lib/typescript/interfaces/options/modals.d.ts +15 -11
  111. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  112. package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
  113. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  114. package/lib/typescript/interfaces/options/system.d.ts +4 -3
  115. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/src/ARCHITECTURE.md +7 -4
  118. package/src/api/app/createNavigation.ts +2 -0
  119. package/src/api/linking/dispatchedLinks.ts +18 -0
  120. package/src/interfaces/Events.ts +10 -0
  121. package/src/interfaces/contract/errors.json +275 -0
  122. package/src/interfaces/options/animations.ts +17 -13
  123. package/src/interfaces/options/modals.ts +15 -11
  124. package/src/interfaces/options/sideMenu.ts +7 -6
  125. package/src/interfaces/options/system.ts +4 -3
@@ -1,5 +1,6 @@
1
1
  #import "ANNEventEmitter.h"
2
2
  #import "ANNAssert.h"
3
+ #import "ANNEventContract.h"
3
4
  #import "ANNReloadCoordinator.h"
4
5
  #import "ANNTurboEventEmitter.h"
5
6
  #import <React/RCTUtils.h>
@@ -24,8 +25,6 @@ static NSString *ANNComponentTypeName(ANNReactComponentType componentType) {
24
25
  static BOOL ANNAppLaunchedPending;
25
26
  static NSUInteger ANNAppLaunchedBundle;
26
27
 
27
- static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
28
-
29
28
  @implementation ANNEventEmitter
30
29
 
31
30
  - (instancetype)init {
@@ -42,12 +41,11 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
42
41
  // The generation is checked where the event goes out, on the main queue: an event sent from another queue just
43
42
  // before a reload is dropped too.
44
43
  - (void)sendEvent:(NSString *)name body:(NSDictionary<NSString *, id> *)body {
45
- NSString *eventName = [@"ANN." stringByAppendingString:name];
46
44
  RCTExecuteOnMainQueue(^{
47
45
  if (!self.isCurrent) {
48
46
  return;
49
47
  }
50
- [ANNTurboEventEmitter.current sendIfListened:eventName body:body];
48
+ [ANNTurboEventEmitter.current sendIfListened:name body:body];
51
49
  });
52
50
  }
53
51
 
@@ -62,7 +60,7 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
62
60
  return;
63
61
  }
64
62
  ANNAppLaunchedPending = NO;
65
- [eventEmitter sendIfListened:ANNAppLaunchedEvent body:nil];
63
+ [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:nil];
66
64
  }
67
65
 
68
66
  + (void)eventEmitterListensToAppLaunched:(ANNTurboEventEmitter *)eventEmitter {
@@ -71,7 +69,7 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
71
69
  return;
72
70
  }
73
71
  ANNAppLaunchedPending = NO;
74
- [eventEmitter sendIfListened:ANNAppLaunchedEvent body:nil];
72
+ [eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:nil];
75
73
  }
76
74
 
77
75
  // Only an `AppLaunched` of the module's own JavaScript or an older one: the module is made while its bundle runs,
@@ -87,11 +85,12 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
87
85
  - (void)sendCommandCompleted:(NSString *)commandName
88
86
  commandId:(NSString *)commandId
89
87
  completionTime:(double)completionTime {
90
- [self sendEvent:@"CommandCompleted"
88
+ const auto &keys = ANNEventContract.CommandCompleted;
89
+ [self sendEvent:keys.event
91
90
  body:@{
92
- @"commandName" : commandName,
93
- @"commandId" : commandId,
94
- @"completionTime" : @(completionTime),
91
+ keys.commandName : commandName,
92
+ keys.commandId : commandId,
93
+ keys.completionTime : @(completionTime),
95
94
  }];
96
95
  }
97
96
 
@@ -100,7 +99,7 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
100
99
  - (void)sendComponentWillAppear:(NSString *)componentId
101
100
  componentName:(NSString *)componentName
102
101
  componentType:(ANNReactComponentType)componentType {
103
- [self sendComponentEvent:@"ComponentWillAppear"
102
+ [self sendComponentEvent:ANNEventContract.ComponentWillAppear.event
104
103
  componentId:componentId
105
104
  componentName:componentName
106
105
  componentType:componentType];
@@ -109,7 +108,7 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
109
108
  - (void)sendComponentDidAppear:(NSString *)componentId
110
109
  componentName:(NSString *)componentName
111
110
  componentType:(ANNReactComponentType)componentType {
112
- [self sendComponentEvent:@"ComponentDidAppear"
111
+ [self sendComponentEvent:ANNEventContract.ComponentDidAppear.event
113
112
  componentId:componentId
114
113
  componentName:componentName
115
114
  componentType:componentType];
@@ -118,21 +117,23 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
118
117
  - (void)sendComponentDidDisappear:(NSString *)componentId
119
118
  componentName:(NSString *)componentName
120
119
  componentType:(ANNReactComponentType)componentType {
121
- [self sendComponentEvent:@"ComponentDidDisappear"
120
+ [self sendComponentEvent:ANNEventContract.ComponentDidDisappear.event
122
121
  componentId:componentId
123
122
  componentName:componentName
124
123
  componentType:componentType];
125
124
  }
126
125
 
126
+ // The three appearance events have the same keys.
127
127
  - (void)sendComponentEvent:(NSString *)name
128
128
  componentId:(NSString *)componentId
129
129
  componentName:(NSString *)componentName
130
130
  componentType:(ANNReactComponentType)componentType {
131
+ const auto &keys = ANNEventContract.ComponentDidAppear;
131
132
  [self sendEvent:name
132
133
  body:@{
133
- @"componentId" : componentId ?: @"",
134
- @"componentName" : componentName ?: @"",
135
- @"componentType" : ANNComponentTypeName(componentType),
134
+ keys.componentId : componentId ?: @"",
135
+ keys.componentName : componentName ?: @"",
136
+ keys.componentType : ANNComponentTypeName(componentType),
136
137
  }];
137
138
  }
138
139
 
@@ -143,58 +144,76 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
143
144
  - (void)sendOnNavigationButtonPressed:(NSString *)componentId
144
145
  buttonId:(NSString *)buttonId
145
146
  selected:(NSNumber *)selected {
147
+ const auto &keys = ANNEventContract.NavigationButtonPressed;
146
148
  NSMutableDictionary *body =
147
- [@{@"componentId" : componentId ?: @"", @"buttonId" : buttonId ?: @""} mutableCopy];
149
+ [@{keys.componentId : componentId ?: @"", keys.buttonId : buttonId ?: @""} mutableCopy];
148
150
  if (selected) {
149
- body[@"selected"] = @(selected.boolValue);
151
+ body[keys.selected] = @(selected.boolValue);
150
152
  }
151
- [self sendEvent:@"NavigationButtonPressed" body:body];
153
+ [self sendEvent:keys.event body:body];
152
154
  }
153
155
 
154
156
  - (void)sendScreenPopped:(NSString *)componentId {
155
- [self sendEvent:@"ScreenPopped" body:@{@"componentId" : componentId ?: @""}];
157
+ const auto &keys = ANNEventContract.ScreenPopped;
158
+ [self sendEvent:keys.event body:@{keys.componentId : componentId ?: @""}];
156
159
  }
157
160
 
158
161
  - (void)sendNavigationStateChanged:(NSString *)screenId name:(NSString *)name tab:(NSNumber *)tab {
159
- id screen = screenId ? @{@"id" : screenId, @"name" : name ?: @""} : (id)NSNull.null;
160
- [self sendEvent:@"NavigationStateChanged"
161
- body:@{@"screen" : screen, @"tab" : tab ?: (id)NSNull.null}];
162
+ const auto &keys = ANNEventContract.NavigationStateChanged;
163
+ const auto &screenKeys = ANNEventContract.NavigationStateChangedScreen;
164
+ id screen = screenId ? @{screenKeys.id : screenId, screenKeys.name : name ?: @""}
165
+ : (id)NSNull.null;
166
+ [self sendEvent:keys.event body:@{keys.screen : screen, keys.tab : tab ?: (id)NSNull.null}];
162
167
  }
163
168
 
164
169
  - (void)sendScreenInsetsChanged:(NSString *)componentId
165
170
  insets:(NSDictionary<NSString *, NSNumber *> *)insets {
171
+ const auto &keys = ANNEventContract.ScreenInsetsChanged;
166
172
  NSMutableDictionary *body = [insets mutableCopy] ?: [NSMutableDictionary new];
167
- body[@"componentId"] = componentId;
168
- [self sendEvent:@"ScreenInsetsChanged" body:body];
173
+ body[keys.componentId] = componentId;
174
+ [self sendEvent:keys.event body:body];
169
175
  }
170
176
 
171
177
  - (void)sendScreenRegionsChanged:(NSString *)componentId
172
178
  regions:(NSDictionary<NSString *, NSArray *> *)regions {
179
+ const auto &keys = ANNEventContract.ScreenRegionsChanged;
173
180
  NSMutableDictionary *body = [regions mutableCopy] ?: [NSMutableDictionary new];
174
- body[@"componentId"] = componentId;
175
- [self sendEvent:@"ScreenRegionsChanged" body:body];
181
+ body[keys.componentId] = componentId;
182
+ [self sendEvent:keys.event body:body];
176
183
  }
177
184
 
178
185
  - (void)sendSlotDestroyed:(NSString *)componentId {
179
- [self sendEvent:@"SlotDestroyed" body:@{@"componentId" : componentId ?: @""}];
186
+ const auto &keys = ANNEventContract.SlotDestroyed;
187
+ [self sendEvent:keys.event body:@{keys.componentId : componentId ?: @""}];
188
+ }
189
+
190
+ - (void)sendDeepLinkDispatched:(NSURL *)url {
191
+ const auto &keys = ANNEventContract.DeepLinkDispatched;
192
+ [self sendEvent:keys.event body:@{keys.url : url.absoluteString ?: @""}];
180
193
  }
181
194
 
182
195
  - (void)sendTitleRenamed:(NSString *)componentId text:(NSString *)text {
183
- [self sendEvent:@"TitleRenamed"
184
- body:@{@"componentId" : componentId ?: @"", @"text" : text ?: @""}];
196
+ const auto &keys = ANNEventContract.TitleRenamed;
197
+ [self sendEvent:keys.event
198
+ body:@{keys.componentId : componentId ?: @"", keys.text : text ?: @""}];
185
199
  }
186
200
 
187
201
  - (void)sendKeyCommandPressed:(NSString *)componentId keyCommandId:(NSString *)keyCommandId {
188
- [self sendEvent:@"KeyCommandPressed"
189
- body:@{@"componentId" : componentId ?: @"", @"keyCommandId" : keyCommandId ?: @""}];
202
+ const auto &keys = ANNEventContract.KeyCommandPressed;
203
+ [self sendEvent:keys.event
204
+ body:@{
205
+ keys.componentId : componentId ?: @"",
206
+ keys.keyCommandId : keyCommandId ?: @"",
207
+ }];
190
208
  }
191
209
 
192
210
  - (void)sendPreviewCompleted:(NSString *)componentId
193
211
  previewComponentId:(NSString *)previewComponentId {
194
- [self sendEvent:@"PreviewCompleted"
212
+ const auto &keys = ANNEventContract.PreviewCompleted;
213
+ [self sendEvent:keys.event
195
214
  body:@{
196
- @"componentId" : componentId ?: @"",
197
- @"previewComponentId" : previewComponentId ?: @"",
215
+ keys.componentId : componentId ?: @"",
216
+ keys.previewComponentId : previewComponentId ?: @"",
198
217
  }];
199
218
  }
200
219
 
@@ -203,21 +222,24 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
203
222
  - (void)sendModalDismissed:(NSString *)componentId
204
223
  componentName:(NSString *)componentName
205
224
  modalsDismissed:(NSUInteger)modalsDismissed {
206
- [self sendEvent:@"ModalDismissed"
225
+ const auto &keys = ANNEventContract.ModalDismissed;
226
+ [self sendEvent:keys.event
207
227
  body:@{
208
- @"componentId" : componentId ?: @"",
209
- @"componentName" : componentName ?: @"",
210
- @"modalsDismissed" : @(modalsDismissed),
228
+ keys.componentId : componentId ?: @"",
229
+ keys.componentName : componentName ?: @"",
230
+ keys.modalsDismissed : @(modalsDismissed),
211
231
  }];
212
232
  }
213
233
 
214
234
  - (void)sendModalAttemptedToDismiss:(NSString *)componentId {
215
- [self sendEvent:@"ModalAttemptedToDismiss" body:@{@"componentId" : componentId ?: @""}];
235
+ const auto &keys = ANNEventContract.ModalAttemptedToDismiss;
236
+ [self sendEvent:keys.event body:@{keys.componentId : componentId ?: @""}];
216
237
  }
217
238
 
218
239
  - (void)sendSheetDetentChanged:(NSString *)componentId detent:(id)detent {
219
- [self sendEvent:@"SheetDetentChanged"
220
- body:@{@"componentId" : componentId ?: @"", @"detent" : detent ?: @""}];
240
+ const auto &keys = ANNEventContract.SheetDetentChanged;
241
+ [self sendEvent:keys.event
242
+ body:@{keys.componentId : componentId ?: @"", keys.detent : detent ?: @""}];
221
243
  }
222
244
 
223
245
  #pragma mark - Search
@@ -227,82 +249,94 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
227
249
  isFocused:(BOOL)isFocused
228
250
  scope:(NSString *)scope
229
251
  tokens:(NSArray<NSString *> *)tokens {
252
+ const auto &keys = ANNEventContract.SearchBarUpdated;
230
253
  NSMutableDictionary *body = [@{
231
- @"componentId" : componentId ?: @"",
232
- @"text" : text ?: @"",
233
- @"isFocused" : @(isFocused),
254
+ keys.componentId : componentId ?: @"",
255
+ keys.text : text ?: @"",
256
+ keys.isFocused : @(isFocused),
234
257
  } mutableCopy];
235
- body[@"scope"] = scope;
236
- body[@"tokens"] = tokens;
237
- [self sendEvent:@"SearchBarUpdated" body:body];
258
+ body[keys.scope] = scope;
259
+ body[keys.tokens] = tokens;
260
+ [self sendEvent:keys.event body:body];
238
261
  }
239
262
 
240
263
  - (void)sendOnSearchBarSubmitted:(NSString *)componentId
241
264
  text:(NSString *)text
242
265
  scope:(NSString *)scope
243
266
  tokens:(NSArray<NSString *> *)tokens {
267
+ const auto &keys = ANNEventContract.SearchBarSubmitted;
244
268
  NSMutableDictionary *body =
245
- [@{@"componentId" : componentId ?: @"", @"text" : text ?: @""} mutableCopy];
246
- body[@"scope"] = scope;
247
- body[@"tokens"] = tokens;
248
- [self sendEvent:@"SearchBarSubmitted" body:body];
269
+ [@{keys.componentId : componentId ?: @"", keys.text : text ?: @""} mutableCopy];
270
+ body[keys.scope] = scope;
271
+ body[keys.tokens] = tokens;
272
+ [self sendEvent:keys.event body:body];
249
273
  }
250
274
 
251
275
  - (void)sendOnSearchBarCancelPressed:(NSString *)componentId {
252
- [self sendEvent:@"SearchBarCancelPressed" body:@{@"componentId" : componentId ?: @""}];
276
+ const auto &keys = ANNEventContract.SearchBarCancelPressed;
277
+ [self sendEvent:keys.event body:@{keys.componentId : componentId ?: @""}];
253
278
  }
254
279
 
255
280
  - (void)sendOnSearchSuggestionPicked:(NSString *)componentId suggestionId:(NSString *)suggestionId {
256
- [self sendEvent:@"SearchSuggestionPicked"
257
- body:@{@"componentId" : componentId ?: @"", @"suggestionId" : suggestionId ?: @""}];
281
+ const auto &keys = ANNEventContract.SearchSuggestionPicked;
282
+ [self sendEvent:keys.event
283
+ body:@{
284
+ keys.componentId : componentId ?: @"",
285
+ keys.suggestionId : suggestionId ?: @"",
286
+ }];
258
287
  }
259
288
 
260
289
  #pragma mark - Split view, pair, hinge
261
290
 
262
291
  - (void)sendSplitViewColumnsStacked:(NSString *)componentId stacked:(BOOL)stacked {
263
- [self sendEvent:@"SplitViewColumnsStacked"
264
- body:@{@"componentId" : componentId ?: @"", @"stacked" : @(stacked)}];
292
+ const auto &keys = ANNEventContract.SplitViewColumnsStacked;
293
+ [self sendEvent:keys.event
294
+ body:@{keys.componentId : componentId ?: @"", keys.stacked : @(stacked)}];
265
295
  }
266
296
 
267
297
  - (void)sendSplitViewDisplayModeChanged:(NSString *)componentId
268
298
  displayMode:(NSString *)displayMode
269
299
  inspectorVisible:(NSNumber *)inspectorVisible {
300
+ const auto &keys = ANNEventContract.SplitViewDisplayModeChanged;
270
301
  NSMutableDictionary *body = [@{
271
- @"componentId" : componentId ?: @"",
272
- @"displayMode" : displayMode ?: @"automatic",
302
+ keys.componentId : componentId ?: @"",
303
+ keys.displayMode : displayMode ?: @"automatic",
273
304
  } mutableCopy];
274
- body[@"inspectorVisible"] = inspectorVisible;
275
- [self sendEvent:@"SplitViewDisplayModeChanged" body:body];
305
+ body[keys.inspectorVisible] = inspectorVisible;
306
+ [self sendEvent:keys.event body:body];
276
307
  }
277
308
 
278
309
  - (void)sendArrangementChanged:(NSString *)componentId state:(NSDictionary<NSString *, id> *)state {
310
+ const auto &keys = ANNEventContract.ArrangementChanged;
279
311
  NSMutableDictionary *body = [state mutableCopy] ?: [NSMutableDictionary new];
280
- body[@"componentId"] = componentId;
281
- [self sendEvent:@"ArrangementChanged" body:body];
312
+ body[keys.componentId] = componentId;
313
+ [self sendEvent:keys.event body:body];
282
314
  }
283
315
 
284
316
  - (void)sendHingeChanged:(NSDictionary<NSString *, id> *)value {
285
- [self sendEvent:@"HingeChanged" body:value ?: @{}];
317
+ [self sendEvent:ANNEventContract.HingeChanged.event body:value ?: @{}];
286
318
  }
287
319
 
288
320
  #pragma mark - Native screens, tabs
289
321
 
290
322
  - (void)sendNativeScreenCall:(NSDictionary<NSString *, id> *)call {
291
- [self sendEvent:@"NativeScreenCall" body:call];
323
+ [self sendEvent:ANNEventContract.NativeScreenCall.event body:call];
292
324
  }
293
325
 
294
326
  - (void)sendNativeScreenReleased:(NSString *)componentId {
295
- [self sendEvent:@"NativeScreenReleased" body:@{@"componentId" : componentId ?: @""}];
327
+ const auto &keys = ANNEventContract.NativeScreenReleased;
328
+ [self sendEvent:keys.event body:@{keys.componentId : componentId ?: @""}];
296
329
  }
297
330
 
298
331
  - (void)sendBottomTabSelected:(NSUInteger)selectedTabIndex
299
332
  unselectedTabIndex:(NSUInteger)unselectedTabIndex
300
333
  bottomTabsId:(NSString *)bottomTabsId {
301
- [self sendEvent:@"BottomTabSelected"
334
+ const auto &keys = ANNEventContract.BottomTabSelected;
335
+ [self sendEvent:keys.event
302
336
  body:@{
303
- @"selectedTabIndex" : @(selectedTabIndex),
304
- @"unselectedTabIndex" : @(unselectedTabIndex),
305
- @"bottomTabsId" : bottomTabsId ?: @"",
337
+ keys.selectedTabIndex : @(selectedTabIndex),
338
+ keys.unselectedTabIndex : @(unselectedTabIndex),
339
+ keys.bottomTabsId : bottomTabsId ?: @"",
306
340
  }];
307
341
  }
308
342
 
@@ -310,28 +344,31 @@ static NSString *const ANNAppLaunchedEvent = @"ANN.AppLaunched";
310
344
  bottomTabsId:(NSString *)bottomTabsId
311
345
  screenId:(NSString *)screenId
312
346
  wasSelected:(BOOL)wasSelected {
347
+ const auto &keys = ANNEventContract.BottomTabPressed;
313
348
  NSMutableDictionary *body = [@{
314
- @"tabIndex" : @(tabIndex),
315
- @"bottomTabsId" : bottomTabsId ?: @"",
316
- @"wasSelected" : @(wasSelected),
349
+ keys.tabIndex : @(tabIndex),
350
+ keys.bottomTabsId : bottomTabsId ?: @"",
351
+ keys.wasSelected : @(wasSelected),
317
352
  } mutableCopy];
318
- body[@"screenId"] = screenId;
319
- [self sendEvent:@"BottomTabPressed" body:body];
353
+ body[keys.screenId] = screenId;
354
+ [self sendEvent:keys.event body:body];
320
355
  }
321
356
 
322
357
  - (void)sendBottomTabLongPressed:(NSUInteger)selectedTabIndex
323
358
  bottomTabsId:(NSString *)bottomTabsId {
324
- [self sendEvent:@"BottomTabLongPressed"
359
+ const auto &keys = ANNEventContract.BottomTabLongPressed;
360
+ [self sendEvent:keys.event
325
361
  body:@{
326
- @"selectedTabIndex" : @(selectedTabIndex),
327
- @"bottomTabsId" : bottomTabsId ?: @"",
362
+ keys.selectedTabIndex : @(selectedTabIndex),
363
+ keys.bottomTabsId : bottomTabsId ?: @"",
328
364
  }];
329
365
  }
330
366
 
331
367
  - (void)sendBottomAccessoryPlacementChanged:(NSString *)componentId
332
368
  placement:(NSString *)placement {
333
- [self sendEvent:@"BottomAccessoryPlacementChanged"
334
- body:@{@"componentId" : componentId ?: @"", @"placement" : placement ?: @""}];
369
+ const auto &keys = ANNEventContract.BottomAccessoryPlacementChanged;
370
+ [self sendEvent:keys.event
371
+ body:@{keys.componentId : componentId ?: @"", keys.placement : placement ?: @""}];
335
372
  }
336
373
 
337
374
  @end
@@ -1,5 +1,6 @@
1
1
  #import "ANNHinge.h"
2
2
  #import "ANNAssert.h"
3
+ #import "ANNEventContract.h"
3
4
  #import "ANNUtils.h"
4
5
 
5
6
  NSNotificationName const ANNHingeDidChangeNotification = @"ANNHingeDidChange";
@@ -34,11 +35,12 @@ static NSDictionary<NSString *, id> *ANNHingeValue(UIHinge *hinge) API_AVAILABLE
34
35
  if (!hinge) {
35
36
  return @{};
36
37
  }
38
+ const auto &keys = ANNEventContract.HingeChanged;
37
39
  NSString *state = ANNHingeStateName(hinge.status);
38
40
  if (hinge.status == UIHingeStatusUnknown) {
39
- return @{@"state" : state};
41
+ return @{keys.state : state};
40
42
  }
41
- return @{@"state" : state, @"angle" : @(round(hinge.angle * 1800 / M_PI) / 10)};
43
+ return @{keys.state : state, keys.angle : @(round(hinge.angle * 1800 / M_PI) / 10)};
42
44
  }
43
45
  #endif
44
46
 
@@ -94,13 +96,13 @@ static NSDictionary<NSString *, id> *ANNHingeValue(UIHinge *hinge) API_AVAILABLE
94
96
  if ([ANNLastHinge isEqualToDictionary:value]) {
95
97
  return;
96
98
  }
97
- previousState = ANNLastHinge[@"state"];
99
+ previousState = ANNLastHinge[ANNEventContract.HingeChanged.state];
98
100
  ANNLastHinge = value;
99
101
  }
100
102
  if (ANNHingeUpdatesEnabled) {
101
103
  [ANNHingeEventEmitter sendHingeChanged:value];
102
104
  }
103
- id state = value[@"state"];
105
+ id state = value[ANNEventContract.HingeChanged.state];
104
106
  if (previousState != state && ![previousState isEqual:state]) {
105
107
  [NSNotificationCenter.defaultCenter postNotificationName:ANNHingeDidChangeNotification
106
108
  object:nil];
@@ -1,9 +1,10 @@
1
1
  #import "ANNSceneDelegate.h"
2
+ #import "ANNDeepLinkQueue.h"
2
3
  #import "ApexNavigation.h"
3
- #import <React/RCTLinkingManager.h>
4
4
 
5
5
  // On the scene life cycle UIKit stops calling the app delegate's URL, user activity and foreground/background
6
- // methods. The scene forwards them, so AppDelegate overrides (and the deep links of ANNDeepLinkQueue) keep working.
6
+ // methods. The scene forwards them, so AppDelegate overrides (and the deep links of ANNDeepLinkQueue) keep working; the
7
+ // links keep their scene, through which React Native 0.88 and newer take them.
7
8
  @implementation ANNSceneDelegate
8
9
 
9
10
  - (void)scene:(UIScene *)scene
@@ -15,21 +16,21 @@
15
16
  self.window = [ApexNavigation attachToWindowScene:(UIWindowScene *)scene];
16
17
 
17
18
  for (UIOpenURLContext *context in connectionOptions.URLContexts) {
18
- [self ann_openURLContext:context];
19
+ [self ann_openURLContext:context ofScene:scene];
19
20
  }
20
21
  for (NSUserActivity *activity in connectionOptions.userActivities) {
21
- [self ann_continueUserActivity:activity];
22
+ [self ann_continueUserActivity:activity ofScene:scene];
22
23
  }
23
24
  }
24
25
 
25
26
  - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
26
27
  for (UIOpenURLContext *context in URLContexts) {
27
- [self ann_openURLContext:context];
28
+ [self ann_openURLContext:context ofScene:scene];
28
29
  }
29
30
  }
30
31
 
31
32
  - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
32
- [self ann_continueUserActivity:userActivity];
33
+ [self ann_continueUserActivity:userActivity ofScene:scene];
33
34
  }
34
35
 
35
36
  - (void)sceneDidBecomeActive:(UIScene *)scene {
@@ -62,7 +63,7 @@
62
63
 
63
64
  #pragma mark - private
64
65
 
65
- - (void)ann_openURLContext:(UIOpenURLContext *)context {
66
+ - (void)ann_openURLContext:(UIOpenURLContext *)context ofScene:(UIScene *)scene {
66
67
  UIApplication *application = UIApplication.sharedApplication;
67
68
  id<UIApplicationDelegate> appDelegate = application.delegate;
68
69
  if ([appDelegate respondsToSelector:@selector(application:openURL:options:)]) {
@@ -70,22 +71,31 @@
70
71
  options[UIApplicationOpenURLOptionsSourceApplicationKey] = context.options.sourceApplication;
71
72
  options[UIApplicationOpenURLOptionsAnnotationKey] = context.options.annotation;
72
73
  options[UIApplicationOpenURLOptionsOpenInPlaceKey] = @(context.options.openInPlace);
73
- [appDelegate application:application openURL:context.URL options:options];
74
+ [ANNDeepLinkQueue forwardURLContext:context
75
+ ofScene:scene
76
+ through:^{
77
+ [appDelegate application:application openURL:context.URL options:options];
78
+ }];
74
79
  } else {
75
- [RCTLinkingManager application:application openURL:context.URL options:@{}];
80
+ [ANNDeepLinkQueue openURLContext:context ofScene:scene];
76
81
  }
77
82
  }
78
83
 
79
- - (void)ann_continueUserActivity:(NSUserActivity *)activity {
84
+ - (void)ann_continueUserActivity:(NSUserActivity *)activity ofScene:(UIScene *)scene {
80
85
  UIApplication *application = UIApplication.sharedApplication;
81
86
  id<UIApplicationDelegate> appDelegate = application.delegate;
82
87
  if ([appDelegate respondsToSelector:@selector(application:continueUserActivity:restorationHandler:)]) {
83
- [appDelegate application:application
84
- continueUserActivity:activity
85
- restorationHandler:^(NSArray<id<UIUserActivityRestoring>> *restorableObjects){
86
- }];
88
+ [ANNDeepLinkQueue forwardUserActivity:activity
89
+ ofScene:scene
90
+ through:^{
91
+ [appDelegate application:application
92
+ continueUserActivity:activity
93
+ restorationHandler:^(
94
+ NSArray<id<UIUserActivityRestoring>> *restorableObjects){
95
+ }];
96
+ }];
87
97
  } else if ([activity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
88
- [RCTLinkingManager application:application openURL:activity.webpageURL options:@{}];
98
+ [ANNDeepLinkQueue openUserActivity:activity ofScene:scene];
89
99
  }
90
100
  }
91
101
 
@@ -36,8 +36,9 @@ typedef NS_ENUM(NSInteger, ANNSystemUIQuestion) {
36
36
  /// The owner adds the asks UIKit doesn't make — a merge, a side menu at rest, an overlay, a root replaced, the bar of a
37
37
  /// stack shown or hidden, the orientation after an appearance — and makes them once per turn of the main queue: a
38
38
  /// trigger marks groups, the marks of a turn are coalesced, and at its end UIKit is asked again for each marked group
39
- /// in the main window and every overlay window. It keeps no values but the last status bar of each window, which only
40
- /// chooses whether a change of the style animates. Never a wait. Main queue.
39
+ /// in the main window and every overlay window. Each ask of the status bar also lays out the screens' blurs under it
40
+ /// (`statusBar.blur`), then and on the next turn. It keeps no values but the last status bar of each window, which
41
+ /// only chooses whether a change of the style animates. Never a wait. Main queue.
41
42
  @interface ANNSystemUI : NSObject
42
43
 
43
44
  - (instancetype)init NS_UNAVAILABLE;
@@ -1,5 +1,6 @@
1
1
  #import "ANNSystemUI.h"
2
2
  #import "ANNAssert.h"
3
+ #import "ANNComponentPresenter.h"
3
4
  #import "ANNComponentViewController.h"
4
5
  #import "ANNDefaultOptions.h"
5
6
  #import "ANNOverlayCoordinator.h"
@@ -280,6 +281,12 @@ static NSArray<UIViewController *> *ANNChainOf(UIWindow *window) {
280
281
  ask();
281
282
  }
282
283
  }
284
+ // The status bar hidden or shown moves no view where the safe area stays (a device with a notch): the blurs under
285
+ // it take its frame now, and once more on the next turn of the main queue, in case UIKit settles it after the ask.
286
+ [ANNComponentPresenter statusBarDidChange];
287
+ dispatch_async(dispatch_get_main_queue(), ^{
288
+ [ANNComponentPresenter statusBarDidChange];
289
+ });
283
290
  }
284
291
 
285
292
  // A controller that is no node of the layout (React Native's `<Modal>`, an alert, the splash) animates as UIKit's
@@ -4,6 +4,7 @@
4
4
 
5
5
  #import "ANNArrangementState.h"
6
6
  #import "ANNComponentViewController.h"
7
+ #import "ANNEventContract.h"
7
8
  #import "ANNExternalViewController.h"
8
9
  #import "ANNShownScreens.h"
9
10
  #import "ANNUtils.h"
@@ -130,11 +131,12 @@ static BOOL ANNKeepsState(UIViewController *screen) {
130
131
 
131
132
  static NSDictionary<NSString *, id> *ANNStateOf(NSString *placement, NSString *layout, BOOL front,
132
133
  BOOL hidden) {
134
+ const auto &keys = ANNEventContract.ArrangementChanged;
133
135
  return @{
134
- @"placement" : placement,
135
- @"layout" : layout,
136
- @"front" : @(front),
137
- @"hidden" : @(hidden),
136
+ keys.placement : placement,
137
+ keys.layout : layout,
138
+ keys.front : @(front),
139
+ keys.hidden : @(hidden),
138
140
  };
139
141
  }
140
142
 
@@ -407,7 +407,7 @@ static void ANNCollectIds(UIViewController *node, NSMutableSet<NSString *> *ids)
407
407
  ANNNavigationOptions *resolved = [root resolveOptionsWithDefault];
408
408
  ANNModalPresentation *presentation =
409
409
  [[ANNModalPresentation alloc] initWithOptions:resolved animated:modal.animatedOverride];
410
- NSString *problem = [presentation problemPresentingFrom:presenting windowRoot:windowRoot];
410
+ NSString *problem = [presentation fitToPresenting:presenting windowRoot:windowRoot];
411
411
  if (problem) {
412
412
  [self failShowOf:modal reason:problem];
413
413
  done();
@@ -20,7 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
20
20
 
21
21
  /// `modalPresentationStyle`: one to one; unset is UIKit's automatic style; an unknown string full screen.
22
22
  @property(nonatomic, readonly) UIModalPresentationStyle style;
23
- /// `modalTransitionStyle`: `coverVertical` unless one of the others is given.
23
+ /// `modalTransitionStyle`: `coverVertical` unless one of the others is given; `partialCurl` only where UIKit curls
24
+ /// (`fitToPresenting:windowRoot:`).
24
25
  @property(nonatomic, readonly) UIModalTransitionStyle transitionStyle;
25
26
  /// The present is animated: `enter.enabled` is not `false`.
26
27
  @property(nonatomic, readonly) BOOL animated;
@@ -47,10 +48,12 @@ NS_ASSUME_NONNULL_BEGIN
47
48
  animation:(nullable ANNModalAnimationOptions *)animation
48
49
  under:(nullable UIViewController *)under;
49
50
 
50
- /// Why UIKit can't make this show from `presenting` (`partialCurl` only full screen, to and from), as the reason of the
51
- /// rejection; nil when it can. `windowRoot` is the main window's root.
52
- - (nullable NSString *)problemPresentingFrom:(UIViewController *)presenting
53
- windowRoot:(nullable UIViewController *)windowRoot;
51
+ /// Fits the show to `presenting`, before `prepare:…`: `partialCurl` stays only for a full-screen modal over a
52
+ /// controller that fills the screen (`presenting` is `windowRoot`, the main window's root, or a full-screen modal);
53
+ /// otherwise `transitionStyle` becomes `coverVertical`. Returns why UIKit can't make the show (`presenting` was shown
54
+ /// with `partialCurl`), as the reason of the rejection; nil when it can.
55
+ - (nullable NSString *)fitToPresenting:(UIViewController *)presenting
56
+ windowRoot:(nullable UIViewController *)windowRoot;
54
57
 
55
58
  /// Before `presentViewController:`: the style and the transition, the presentation's delegate, the popover kept a
56
59
  /// popover (`adaptive: false`), the sheet, the size, the popover's source on `presentingLayout`, the hold, the