@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.3
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.
- package/CHANGELOG.md +38 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +31 -6
- package/android/CORE.md +7 -2
- package/android/RECIPES.md +4 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +101 -87
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +76 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +86 -16
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +41 -23
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +24 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +42 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +29 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +2 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +16 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +148 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +24 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +51 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +105 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +123 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +19 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +85 -27
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +45 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
- package/cli/app.js +1 -1
- package/cli/project.js +6 -2
- package/cli/setup/index.js +13 -0
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/docs-md/architecture/transitions.md +7 -1
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +4 -2
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/index.json +8 -0
- package/docs-md/screen-ui/back-and-gestures.md +6 -0
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +1 -0
- package/ios/Animations/ANNTransitionAnimator.mm +13 -11
- package/ios/Animations/ANNTransitionWait.mm +6 -7
- package/ios/Animations/Elements/ANNElementFinder.h +57 -0
- package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
- package/ios/Animations/Elements/ANNElementTransition.h +7 -5
- package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
- package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
- package/ios/CLAUDE.md +10 -2
- package/ios/CORE.md +3 -2
- package/ios/Contract/ANNCommandAnswer.h +4 -3
- package/ios/Contract/ANNCommandAnswer.mm +12 -2
- package/ios/Contract/ANNCommandRunner.h +2 -2
- package/ios/Contract/ANNCommandRunner.mm +1 -1
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Core/ANNDeepLinkQueue.h +20 -1
- package/ios/Core/ANNDeepLinkQueue.mm +107 -12
- package/ios/Core/ANNErrorHandler.h +8 -3
- package/ios/Core/ANNErrorHandler.mm +17 -1
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +5 -1
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.mm +6 -4
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
- package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
- package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
- package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
- package/ios/Layouts/Root/ANNRootCoordinator.mm +12 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/RECIPES.md +2 -1
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +5 -2
- package/lib/module/ARCHITECTURE.md +7 -4
- package/lib/module/api/app/createNavigation.js +2 -0
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
- package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
- package/lib/typescript/interfaces/Events.d.ts +9 -0
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +27 -18
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +33 -24
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +10 -6
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +7 -4
- package/src/api/app/createNavigation.ts +2 -0
- package/src/api/hooks/useNavigation.ts +8 -3
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/interfaces/Events.ts +10 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +27 -18
- package/src/interfaces/options/modals.ts +33 -24
- package/src/interfaces/options/sideMenu.ts +7 -6
- package/src/interfaces/options/system.ts +10 -6
|
@@ -1,8 +1,58 @@
|
|
|
1
1
|
#import "ANNElementFinder.h"
|
|
2
2
|
#import "ANNAppearanceDispatcher.h"
|
|
3
|
+
#import "ANNElementTransitionOptions.h"
|
|
4
|
+
#import "ANNSharedElementTransitionOptions.h"
|
|
5
|
+
#import "Text.h"
|
|
3
6
|
#import "UIViewController+LayoutProtocol.h"
|
|
7
|
+
#import <React/RCTLog.h>
|
|
4
8
|
#import <React/RCTViewComponentView.h>
|
|
5
9
|
|
|
10
|
+
static NSString *ANNNativeIDOfView(UIView *view) {
|
|
11
|
+
return [view isKindOfClass:RCTViewComponentView.class]
|
|
12
|
+
? ((RCTViewComponentView *)view).nativeId
|
|
13
|
+
: nil;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Depth first, as the search by one id: the first view of the walk with an id keeps it. NO once every id is found.
|
|
17
|
+
static BOOL ANNCollectViews(UIView *view, NSSet<NSString *> *nativeIDs,
|
|
18
|
+
NSMutableDictionary<NSString *, UIView *> *found) {
|
|
19
|
+
NSString *nativeID = ANNNativeIDOfView(view);
|
|
20
|
+
if (nativeID && !found[nativeID] && [nativeIDs containsObject:nativeID]) {
|
|
21
|
+
found[nativeID] = view;
|
|
22
|
+
if (found.count == nativeIDs.count) {
|
|
23
|
+
return NO;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (UIView *subview in view.subviews) {
|
|
27
|
+
if (!ANNCollectViews(subview, nativeIDs, found)) {
|
|
28
|
+
return NO;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return YES;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
NSString *ANNNativeIDOf(Text *value) {
|
|
35
|
+
NSString *text = [value withDefault:nil];
|
|
36
|
+
return [text isKindOfClass:NSString.class] && text.length > 0 ? text : nil;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void ANNElementSkipped(NSString *element, NSString *reason) {
|
|
40
|
+
RCTLogWarn(@"[ApexNavigation] the transition's element %@ is skipped, it shows and hides with "
|
|
41
|
+
@"its screen while the rest plays: %@",
|
|
42
|
+
element, reason);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
BOOL ANNElementStep(NSString *element, NS_NOESCAPE dispatch_block_t work) {
|
|
46
|
+
@try {
|
|
47
|
+
work();
|
|
48
|
+
return YES;
|
|
49
|
+
} @catch (NSException *exception) {
|
|
50
|
+
ANNElementSkipped(element, [NSString stringWithFormat:@"%@ (%@)", exception.reason ?: @"",
|
|
51
|
+
exception.name]);
|
|
52
|
+
return NO;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
6
56
|
@implementation ANNElementFinder
|
|
7
57
|
|
|
8
58
|
+ (UIView *)searchRootOf:(UIViewController *)node {
|
|
@@ -17,8 +67,7 @@
|
|
|
17
67
|
if (nativeID.length == 0 || !root) {
|
|
18
68
|
return nil;
|
|
19
69
|
}
|
|
20
|
-
if ([root
|
|
21
|
-
[((RCTViewComponentView *)root).nativeId isEqualToString:nativeID]) {
|
|
70
|
+
if ([ANNNativeIDOfView(root) isEqualToString:nativeID]) {
|
|
22
71
|
return root;
|
|
23
72
|
}
|
|
24
73
|
for (UIView *subview in root.subviews) {
|
|
@@ -34,4 +83,127 @@
|
|
|
34
83
|
return [self viewWithNativeID:nativeID inView:[self searchRootOf:node]];
|
|
35
84
|
}
|
|
36
85
|
|
|
86
|
+
+ (NSDictionary<NSString *, UIView *> *)viewsWithNativeIDs:(NSSet<NSString *> *)nativeIDs
|
|
87
|
+
inView:(UIView *)root {
|
|
88
|
+
NSMutableDictionary<NSString *, UIView *> *found = [NSMutableDictionary new];
|
|
89
|
+
if (root && nativeIDs.count > 0) {
|
|
90
|
+
ANNCollectViews(root, nativeIDs, found);
|
|
91
|
+
}
|
|
92
|
+
return found;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@end
|
|
96
|
+
|
|
97
|
+
@implementation ANNTransitionViews {
|
|
98
|
+
NSSet<NSString *> *_leavingIDs;
|
|
99
|
+
NSSet<NSString *> *_targetIDs;
|
|
100
|
+
NSSet<NSString *> *_elementIDs;
|
|
101
|
+
NSDictionary<NSString *, UIView *> *_leavingViews;
|
|
102
|
+
NSDictionary<NSString *, UIView *> *_arrivingViews;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (instancetype)initWithPairs:(NSArray<ANNSharedElementTransitionOptions *> *)pairs
|
|
106
|
+
elements:(NSArray<ANNElementTransitionOptions *> *)elements
|
|
107
|
+
fromNode:(UIViewController *)fromNode
|
|
108
|
+
toNode:(UIViewController *)toNode {
|
|
109
|
+
if (self = [super init]) {
|
|
110
|
+
_leavingRoot = [ANNElementFinder searchRootOf:fromNode];
|
|
111
|
+
_arrivingRoot = [ANNElementFinder searchRootOf:toNode];
|
|
112
|
+
NSMutableSet<NSString *> *sources = [NSMutableSet new];
|
|
113
|
+
NSMutableSet<NSString *> *targets = [NSMutableSet new];
|
|
114
|
+
NSMutableSet<NSString *> *identifiers = [NSMutableSet new];
|
|
115
|
+
for (ANNSharedElementTransitionOptions *pair in pairs) {
|
|
116
|
+
NSString *fromId = ANNNativeIDOf(pair.fromId);
|
|
117
|
+
NSString *toId = ANNNativeIDOf(pair.toId);
|
|
118
|
+
if (fromId) {
|
|
119
|
+
[sources addObject:fromId];
|
|
120
|
+
}
|
|
121
|
+
if (toId) {
|
|
122
|
+
[targets addObject:toId];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (ANNElementTransitionOptions *element in elements) {
|
|
126
|
+
NSString *identifier = ANNNativeIDOf(element.identifier);
|
|
127
|
+
if (identifier) {
|
|
128
|
+
[identifiers addObject:identifier];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
[sources unionSet:identifiers];
|
|
132
|
+
_leavingIDs = sources;
|
|
133
|
+
_targetIDs = targets;
|
|
134
|
+
_elementIDs = identifiers;
|
|
135
|
+
}
|
|
136
|
+
return self;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
- (UIView *)leavingViewWithNativeID:(NSString *)nativeID {
|
|
140
|
+
if (nativeID.length == 0) {
|
|
141
|
+
return nil;
|
|
142
|
+
}
|
|
143
|
+
if (!_leavingViews) {
|
|
144
|
+
_leavingViews = [ANNElementFinder viewsWithNativeIDs:_leavingIDs inView:_leavingRoot];
|
|
145
|
+
}
|
|
146
|
+
return _leavingViews[nativeID];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
- (UIView *)arrivingViewWithNativeID:(NSString *)nativeID {
|
|
150
|
+
if (nativeID.length == 0) {
|
|
151
|
+
return nil;
|
|
152
|
+
}
|
|
153
|
+
if (!_arrivingViews) {
|
|
154
|
+
// An element is looked for in the arriving screen only when the leaving one doesn't have it: the walk stops
|
|
155
|
+
// sooner.
|
|
156
|
+
NSMutableSet<NSString *> *ids = [_targetIDs mutableCopy];
|
|
157
|
+
for (NSString *identifier in _elementIDs) {
|
|
158
|
+
if (![self leavingViewWithNativeID:identifier]) {
|
|
159
|
+
[ids addObject:identifier];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
_arrivingViews = [ANNElementFinder viewsWithNativeIDs:ids inView:_arrivingRoot];
|
|
163
|
+
}
|
|
164
|
+
return _arrivingViews[nativeID];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@end
|
|
168
|
+
|
|
169
|
+
@implementation ANNIsolatedElement {
|
|
170
|
+
id<ANNFrameClient> _client;
|
|
171
|
+
NSString *_name;
|
|
172
|
+
BOOL _failed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
- (instancetype)initWithClient:(id<ANNFrameClient>)client name:(NSString *)name {
|
|
176
|
+
if (self = [super init]) {
|
|
177
|
+
_client = client;
|
|
178
|
+
_name = [name copy];
|
|
179
|
+
}
|
|
180
|
+
return self;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#pragma mark - ANNFrameClient
|
|
184
|
+
|
|
185
|
+
- (void)applyAtTime:(NSTimeInterval)time {
|
|
186
|
+
if (_failed) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
id<ANNFrameClient> client = _client;
|
|
190
|
+
if (!ANNElementStep(_name, ^{
|
|
191
|
+
[client applyAtTime:time];
|
|
192
|
+
})) {
|
|
193
|
+
_failed = YES;
|
|
194
|
+
[self restore];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
- (NSTimeInterval)endTime {
|
|
199
|
+
return _client.endTime;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
- (void)restore {
|
|
203
|
+
id<ANNFrameClient> client = _client;
|
|
204
|
+
ANNElementStep(_name, ^{
|
|
205
|
+
[client restore];
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
37
209
|
@end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import <UIKit/UIKit.h>
|
|
3
3
|
|
|
4
4
|
@class ANNElementTransitionOptions;
|
|
5
|
+
@class ANNTransitionViews;
|
|
5
6
|
|
|
6
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
7
8
|
|
|
@@ -11,20 +12,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
12
|
@interface ANNElementTransition : NSObject <ANNFrameClient>
|
|
12
13
|
|
|
13
14
|
/// The items found: each `id` first in the leaving screen's React view, then in the arriving one; an item not found or
|
|
14
|
-
/// without an animating property is left out.
|
|
15
|
+
/// without an animating property is left out, and so is one whose start fails (`ANNElementStep`).
|
|
15
16
|
+ (NSArray<ANNElementTransition *> *)
|
|
16
17
|
transitionsOf:(nullable NSArray<ANNElementTransitionOptions *> *)elements
|
|
17
|
-
|
|
18
|
-
arrivingRoot:(nullable UIView *)arrivingRoot;
|
|
18
|
+
views:(ANNTransitionViews *)views;
|
|
19
19
|
|
|
20
20
|
/// Every item's `id` is found in one of the two screens (the wait's test).
|
|
21
21
|
+ (BOOL)allFound:(nullable NSArray<ANNElementTransitionOptions *> *)elements
|
|
22
|
-
|
|
23
|
-
arrivingRoot:(nullable UIView *)arrivingRoot;
|
|
22
|
+
views:(ANNTransitionViews *)views;
|
|
24
23
|
|
|
25
24
|
- (instancetype)init NS_UNAVAILABLE;
|
|
26
25
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
27
26
|
|
|
27
|
+
/// The item's `id`, in quotes: its name in the log.
|
|
28
|
+
@property(nonatomic, copy, readonly) NSString *name;
|
|
29
|
+
|
|
28
30
|
@end
|
|
29
31
|
|
|
30
32
|
NS_ASSUME_NONNULL_END
|
|
@@ -7,51 +7,49 @@
|
|
|
7
7
|
ANNViewTracks *_tracks;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
- (instancetype)initWithTracks:(ANNViewTracks *)tracks {
|
|
10
|
+
- (instancetype)initWithTracks:(ANNViewTracks *)tracks name:(NSString *)name {
|
|
11
11
|
if (self = [super init]) {
|
|
12
12
|
_tracks = tracks;
|
|
13
|
+
_name = [name copy];
|
|
13
14
|
}
|
|
14
15
|
return self;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
+ (UIView *)viewOf:(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
NSString *identifier = [element.identifier withDefault:nil];
|
|
21
|
-
if (![identifier isKindOfClass:NSString.class] || identifier.length == 0) {
|
|
22
|
-
return nil;
|
|
23
|
-
}
|
|
24
|
-
return [ANNElementFinder viewWithNativeID:identifier inView:leavingRoot]
|
|
25
|
-
?: [ANNElementFinder viewWithNativeID:identifier inView:arrivingRoot];
|
|
18
|
+
+ (UIView *)viewOf:(NSString *)identifier views:(ANNTransitionViews *)views {
|
|
19
|
+
return [views leavingViewWithNativeID:identifier]
|
|
20
|
+
?: [views arrivingViewWithNativeID:identifier];
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
+ (NSArray<ANNElementTransition *> *)
|
|
29
24
|
transitionsOf:(NSArray<ANNElementTransitionOptions *> *)elements
|
|
30
|
-
|
|
31
|
-
arrivingRoot:(UIView *)arrivingRoot {
|
|
25
|
+
views:(ANNTransitionViews *)views {
|
|
32
26
|
NSMutableArray<ANNElementTransition *> *transitions = [NSMutableArray new];
|
|
33
27
|
// One view takes one item: a second item for the same view would read the values the first one writes.
|
|
34
28
|
NSHashTable<UIView *> *taken = [NSHashTable weakObjectsHashTable];
|
|
35
29
|
for (ANNElementTransitionOptions *element in elements) {
|
|
36
|
-
|
|
30
|
+
NSString *identifier = ANNNativeIDOf(element.identifier);
|
|
31
|
+
UIView *view = [self viewOf:identifier views:views];
|
|
37
32
|
if (!view || [taken containsObject:view]) {
|
|
38
33
|
continue;
|
|
39
34
|
}
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
NSString *name = [NSString stringWithFormat:@"'%@'", identifier];
|
|
36
|
+
__block ANNViewTracks *tracks = nil;
|
|
37
|
+
if (!ANNElementStep(name, ^{
|
|
38
|
+
tracks = [[ANNViewTracks alloc] initWithView:view element:element];
|
|
39
|
+
}) ||
|
|
40
|
+
!tracks.hasTracks) {
|
|
42
41
|
continue;
|
|
43
42
|
}
|
|
44
43
|
[taken addObject:view];
|
|
45
|
-
[transitions addObject:[[ANNElementTransition alloc] initWithTracks:tracks]];
|
|
44
|
+
[transitions addObject:[[ANNElementTransition alloc] initWithTracks:tracks name:name]];
|
|
46
45
|
}
|
|
47
46
|
return transitions;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
+ (BOOL)allFound:(NSArray<ANNElementTransitionOptions *> *)elements
|
|
51
|
-
|
|
52
|
-
arrivingRoot:(UIView *)arrivingRoot {
|
|
50
|
+
views:(ANNTransitionViews *)views {
|
|
53
51
|
for (ANNElementTransitionOptions *element in elements) {
|
|
54
|
-
if (![self viewOf:element
|
|
52
|
+
if (![self viewOf:ANNNativeIDOf(element.identifier) views:views]) {
|
|
55
53
|
return NO;
|
|
56
54
|
}
|
|
57
55
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import <UIKit/UIKit.h>
|
|
3
3
|
|
|
4
4
|
@class ANNSharedElementTransitionOptions;
|
|
5
|
+
@class ANNTransitionViews;
|
|
5
6
|
|
|
6
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
7
8
|
|
|
@@ -18,18 +19,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
18
19
|
/// grows, the last when it shrinks.
|
|
19
20
|
@interface ANNSharedElementFlight : NSObject <ANNFrameClient>
|
|
20
21
|
|
|
21
|
-
/// The flights of the pairs whose two views are found (`fromId` in the leaving screen, `toId` in the arriving one)
|
|
22
|
-
///
|
|
23
|
-
///
|
|
22
|
+
/// The flights of the pairs whose two views are found (`fromId` in the leaving screen, `toId` in the arriving one); the
|
|
23
|
+
/// others are skipped and their views stay as they are. A pair found that can't fly — a view without a size, a
|
|
24
|
+
/// picture that can't be taken, a place that can't be worked out, an exception in its start (`ANNElementStep`) — is
|
|
25
|
+
/// skipped too, with a line in the log. The pictures are taken now, then the React views hide and the copies go into
|
|
26
|
+
/// `container` by the `zIndex` of their `to` views, ascending (no `layer.zPosition`).
|
|
24
27
|
+ (NSArray<ANNSharedElementFlight *> *)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
container:(UIView *)container;
|
|
28
|
+
flightsOf:(nullable NSArray<ANNSharedElementTransitionOptions *> *)pairs
|
|
29
|
+
views:(ANNTransitionViews *)views
|
|
30
|
+
container:(UIView *)container;
|
|
29
31
|
|
|
30
32
|
/// Every pair's `toId` is found in the arriving screen (the wait's test).
|
|
31
33
|
+ (BOOL)allTargetsFound:(nullable NSArray<ANNSharedElementTransitionOptions *> *)pairs
|
|
32
|
-
|
|
34
|
+
views:(ANNTransitionViews *)views;
|
|
33
35
|
|
|
34
36
|
- (instancetype)init NS_UNAVAILABLE;
|
|
35
37
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
@@ -37,6 +39,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
37
39
|
/// The copy that flies (a view of the core in the container).
|
|
38
40
|
@property(nonatomic, strong, readonly) UIView *copyView;
|
|
39
41
|
|
|
42
|
+
/// The pair's two ids, in quotes: its name in the log.
|
|
43
|
+
@property(nonatomic, copy, readonly) NSString *name;
|
|
44
|
+
|
|
40
45
|
@end
|
|
41
46
|
|
|
42
47
|
NS_ASSUME_NONNULL_END
|
|
@@ -103,6 +103,25 @@ static ANNFlightEnd ANNFlightEndOf(UIView *view, UIView *screenRoot, UIView *con
|
|
|
103
103
|
return end;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
static BOOL ANNIsFiniteRect(CGRect rect) {
|
|
107
|
+
return isfinite(rect.origin.x) && isfinite(rect.origin.y) && isfinite(rect.size.width) &&
|
|
108
|
+
isfinite(rect.size.height);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static BOOL ANNFlightEndIsFinite(ANNFlightEnd end) {
|
|
112
|
+
const CATransform3D t = end.transform;
|
|
113
|
+
const CGFloat values[] = {t.m11, t.m12, t.m13, t.m14, t.m21, t.m22, t.m23, t.m24,
|
|
114
|
+
t.m31, t.m32, t.m33, t.m34, t.m41, t.m42, t.m43, t.m44};
|
|
115
|
+
for (CGFloat value : values) {
|
|
116
|
+
if (!isfinite(value)) {
|
|
117
|
+
return NO;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return isfinite(end.center.x) && isfinite(end.center.y) && isfinite(end.size.width) &&
|
|
121
|
+
isfinite(end.size.height) && isfinite(end.cornerRadius) && isfinite(end.visibleRadius) &&
|
|
122
|
+
ANNIsFiniteRect(end.visible);
|
|
123
|
+
}
|
|
124
|
+
|
|
106
125
|
// The picture of the view's layer and its sublayers at the screen's scale, in the layer's own coordinates.
|
|
107
126
|
static UIImage *ANNPictureOf(UIView *view) {
|
|
108
127
|
CGSize size = view.bounds.size;
|
|
@@ -140,15 +159,10 @@ static UIImage *ANNPictureOf(UIView *view) {
|
|
|
140
159
|
BOOL _restored;
|
|
141
160
|
}
|
|
142
161
|
|
|
143
|
-
+ (NSString *)textOf:(Text *)value {
|
|
144
|
-
NSString *text = [value withDefault:nil];
|
|
145
|
-
return [text isKindOfClass:NSString.class] && text.length > 0 ? text : nil;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
162
|
+ (BOOL)allTargetsFound:(NSArray<ANNSharedElementTransitionOptions *> *)pairs
|
|
149
|
-
|
|
163
|
+
views:(ANNTransitionViews *)views {
|
|
150
164
|
for (ANNSharedElementTransitionOptions *pair in pairs) {
|
|
151
|
-
if (![
|
|
165
|
+
if (![views arrivingViewWithNativeID:ANNNativeIDOf(pair.toId)]) {
|
|
152
166
|
return NO;
|
|
153
167
|
}
|
|
154
168
|
}
|
|
@@ -156,27 +170,31 @@ static UIImage *ANNPictureOf(UIView *view) {
|
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
+ (NSArray<ANNSharedElementFlight *> *)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
container:(UIView *)container {
|
|
173
|
+
flightsOf:(NSArray<ANNSharedElementTransitionOptions *> *)pairs
|
|
174
|
+
views:(ANNTransitionViews *)views
|
|
175
|
+
container:(UIView *)container {
|
|
163
176
|
NSMutableArray<ANNSharedElementFlight *> *flights = [NSMutableArray new];
|
|
164
177
|
NSHashTable<UIView *> *taken = [NSHashTable weakObjectsHashTable];
|
|
165
178
|
for (ANNSharedElementTransitionOptions *pair in pairs) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
UIView *
|
|
169
|
-
|
|
179
|
+
NSString *fromId = ANNNativeIDOf(pair.fromId);
|
|
180
|
+
NSString *toId = ANNNativeIDOf(pair.toId);
|
|
181
|
+
UIView *from = [views leavingViewWithNativeID:fromId];
|
|
182
|
+
UIView *to = [views arrivingViewWithNativeID:toId];
|
|
170
183
|
if (!from || !to || from == to || [taken containsObject:from] ||
|
|
171
184
|
[taken containsObject:to]) {
|
|
172
185
|
continue;
|
|
173
186
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
NSString *name = [NSString stringWithFormat:@"'%@' -> '%@'", fromId, toId];
|
|
188
|
+
__block ANNSharedElementFlight *flight = nil;
|
|
189
|
+
ANNElementStep(name, ^{
|
|
190
|
+
flight = [[self alloc] initWithPair:pair
|
|
191
|
+
from:from
|
|
192
|
+
to:to
|
|
193
|
+
leavingRoot:views.leavingRoot
|
|
194
|
+
arrivingRoot:views.arrivingRoot
|
|
195
|
+
container:container
|
|
196
|
+
name:name];
|
|
197
|
+
});
|
|
180
198
|
if (flight) {
|
|
181
199
|
[taken addObject:from];
|
|
182
200
|
[taken addObject:to];
|
|
@@ -193,10 +211,18 @@ static UIImage *ANNPictureOf(UIView *view) {
|
|
|
193
211
|
}
|
|
194
212
|
return a->_zIndex < b->_zIndex ? NSOrderedAscending : NSOrderedDescending;
|
|
195
213
|
}];
|
|
214
|
+
// A copy that can't take off goes away, and its pair's views show as they were.
|
|
215
|
+
NSMutableArray<ANNSharedElementFlight *> *flying = [NSMutableArray new];
|
|
196
216
|
for (ANNSharedElementFlight *flight in ordered) {
|
|
197
|
-
|
|
217
|
+
if (ANNElementStep(flight.name, ^{
|
|
218
|
+
[flight takeOffIn:container];
|
|
219
|
+
})) {
|
|
220
|
+
[flying addObject:flight];
|
|
221
|
+
} else {
|
|
222
|
+
[flight restore];
|
|
223
|
+
}
|
|
198
224
|
}
|
|
199
|
-
return
|
|
225
|
+
return flying;
|
|
200
226
|
}
|
|
201
227
|
|
|
202
228
|
- (instancetype)initWithPair:(ANNSharedElementTransitionOptions *)pair
|
|
@@ -204,19 +230,27 @@ static UIImage *ANNPictureOf(UIView *view) {
|
|
|
204
230
|
to:(UIView *)to
|
|
205
231
|
leavingRoot:(UIView *)leavingRoot
|
|
206
232
|
arrivingRoot:(UIView *)arrivingRoot
|
|
207
|
-
container:(UIView *)container
|
|
233
|
+
container:(UIView *)container
|
|
234
|
+
name:(NSString *)name {
|
|
208
235
|
// A view of no size gives no picture: the pair is skipped and both views stay as they are.
|
|
209
236
|
if (from.bounds.size.width <= 0 || from.bounds.size.height <= 0 || to.bounds.size.width <= 0 ||
|
|
210
237
|
to.bounds.size.height <= 0) {
|
|
238
|
+
ANNElementSkipped(name, @"a view of the pair has no size, it isn't laid out");
|
|
211
239
|
return nil;
|
|
212
240
|
}
|
|
213
241
|
if (!(self = [super init])) {
|
|
214
242
|
return nil;
|
|
215
243
|
}
|
|
244
|
+
_name = [name copy];
|
|
216
245
|
_fromView = from;
|
|
217
246
|
_toView = to;
|
|
218
247
|
_from = ANNFlightEndOf(from, leavingRoot, container);
|
|
219
248
|
_to = ANNFlightEndOf(to, arrivingRoot, container);
|
|
249
|
+
// A place that isn't a number would stop the copy's layer with an exception at its first frame.
|
|
250
|
+
if (!ANNFlightEndIsFinite(_from) || !ANNFlightEndIsFinite(_to)) {
|
|
251
|
+
ANNElementSkipped(name, @"the place of a view of the pair can't be worked out");
|
|
252
|
+
return nil;
|
|
253
|
+
}
|
|
220
254
|
_text = ANNIsText(from) || ANNIsText(to);
|
|
221
255
|
_grows = _to.size.height >= _from.size.height;
|
|
222
256
|
_rightToLeft =
|
|
@@ -231,6 +265,7 @@ static UIImage *ANNPictureOf(UIView *view) {
|
|
|
231
265
|
UIImage *fromImage = ANNPictureOf(from);
|
|
232
266
|
UIImage *toImage = ANNPictureOf(to);
|
|
233
267
|
if (!fromImage || !toImage) {
|
|
268
|
+
ANNElementSkipped(name, @"no picture of a view of the pair can be taken");
|
|
234
269
|
return nil;
|
|
235
270
|
}
|
|
236
271
|
_copy = [UIView new];
|
package/ios/CLAUDE.md
CHANGED
|
@@ -58,9 +58,9 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
58
58
|
- Preview — `Layouts/Stack/`: `ANNPreviewPush`; `Layouts/Screen/`: `ANNPreviewInteraction`
|
|
59
59
|
- Animations, transitions — `Animations/` (plan and animator at the top; `Elements/` for shared elements and zoom; `Interpolators/` for curves and tracks)
|
|
60
60
|
- System bars, orientation, direction — `Core/`: `ANNSystemUI`, `ANNSystemUIRules`, `ANNAppDirection`, `ANNNodePresenter` (the answers to UIKit)
|
|
61
|
-
- Events and state — `Core/`: `ANNEventEmitter`, `ANNAppearanceDispatcher`, `ANNScreenAppearance`, `ANNNavigationState`; `TurboModules/`: `ANNTurboEventEmitter`
|
|
61
|
+
- Events and state — `Core/`: `ANNEventEmitter`, `ANNEventContract` (names and keys, made by `scripts/contract.mjs`), `ANNAppearanceDispatcher`, `ANNScreenAppearance`, `ANNNavigationState`; `TurboModules/`: `ANNTurboEventEmitter`
|
|
62
62
|
- Options: parsing, merging, defaults — `Options/`: `ANNOptions`, `ANNOptionField`, `ANNOptionsReader`, `ANNDefaultOptions`, `ANNOptionsCache`; `Core/`: `ANNOptionsRepaint`
|
|
63
|
-
- Error texts — `Contract/`: `ANNErrorTexts
|
|
63
|
+
- Error texts — `Contract/`: `ANNErrorTexts` (made from `src/interfaces/contract/errors.json` by `scripts/contract.mjs`); `Core/`: `ANNErrorHandler`
|
|
64
64
|
- Links — `Core/`: `ANNDeepLinkQueue`; the `getLaunchLink` method of `ANNTurboModule`
|
|
65
65
|
- Key commands — `Core/`: `ANNKeyCommands`; `Layouts/Screen/`: `ANNComponentViewController`
|
|
66
66
|
- Hinge, regions — `Core/`: `ANNHinge`; `Layouts/Screen/`: `ANNScreenRegions`; `Layouts/Arrangement/`: `ANNArrangementState`
|
|
@@ -114,6 +114,8 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
114
114
|
`descendantDidAppear:`.
|
|
115
115
|
- Hold and release of the navigation state come in pairs; a `setRoot` chain with layout modals or overlays holds it
|
|
116
116
|
until its answer.
|
|
117
|
+
- UIKit draws a dismissal without animation too (a sheet's dim goes a frame before its card): what goes out of sight
|
|
118
|
+
that way goes under a snapshot of the window (the old root's modals at `setRoot`, `startTransitionOf:done:`).
|
|
117
119
|
- Use an `@available` check for newer OS APIs, and `#if __IPHONE_OS_VERSION_MAX_ALLOWED >= …` for names of a newer SDK.
|
|
118
120
|
- React Native hears of the app's light or dark style only from its hosting views in the window. A view that stands in
|
|
119
121
|
for one (`ANNSlotScreenView`, the screens of one tree) posts the trait change itself, with the window filter of
|
|
@@ -123,6 +125,12 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
123
125
|
(the root, a modal, an overlay: their record is listed and parked by then), and the modal's end steps inside UIKit's
|
|
124
126
|
completions and delegate calls (`protectEndOf:showing:step:`, `protectBatch:step:`); the overlay's guard is
|
|
125
127
|
`protectWaitOf:step:`.
|
|
128
|
+
- Each step of a transition's element (a pair's start and take-off, an element's start, every frame and restore
|
|
129
|
+
through `ANNIsolatedElement`) runs in `ANNElementStep`: an exception skips that element only. A new step of an
|
|
130
|
+
element goes there too, or one bad element stops the transition (a frame's exception ends the app). The elements of
|
|
131
|
+
a transition are found in one walk per screen (`ANNTransitionViews`).
|
|
132
|
+
- An `ANNCommandAnswer` that goes away unanswered rejects (`notAnswered:`), but only once nothing holds it: a record,
|
|
133
|
+
list or queue that keeps an answer still answers it on every exit, or the promise waits as long as the record lives.
|
|
126
134
|
- On iOS 26 UIKit draws a screen's large title, for a whole pop or swipe back, inside the scroll view the bar follows
|
|
127
135
|
for the top edge: a detached scroll view or a list below the bar hides it. `ANNLargeTitlePresenter` lays its title
|
|
128
136
|
host over the screen's view for the transition and fills the band behind the title from the screen's top
|
package/ios/CORE.md
CHANGED
|
@@ -50,7 +50,8 @@ dispatcher.
|
|
|
50
50
|
- **Appearance dispatcher** (`ANNAppearanceDispatcher`): the only sender of the appearance events; it also ends a
|
|
51
51
|
transition in the fixed order and resolves the command's answer.
|
|
52
52
|
- **Event emitter** (`ANNEventEmitter`, `ANNTurboEventEmitter`): sends `ANN.<Name>` to JS on the main queue, and drops
|
|
53
|
-
events of a JavaScript that went.
|
|
53
|
+
events of a JavaScript that went. The names and keys come from `ANNEventContract`, made from the event types of
|
|
54
|
+
`src/interfaces/` (`scripts/contract.mjs`).
|
|
54
55
|
|
|
55
56
|
## How a command flows
|
|
56
57
|
|
|
@@ -127,7 +128,7 @@ Each of these owns one rule. Call it; do not copy it.
|
|
|
127
128
|
- `ANNModalCoordinator`, `ANNModal`: One record per modal, its states and waiting commands. Called by: Commands, root, state, repaint, reload.
|
|
128
129
|
- `ANNPresentationGate`: The one queue of every UIKit present and dismiss in the main window. Called by: Modals, root, reload, search.
|
|
129
130
|
- `ANNOverlayCoordinator`: Overlays, each in its own window, kept over a new root. Called by: Commands, root, reload.
|
|
130
|
-
- `ANNSystemUI`: The system UI of the windows: marks groups, coalesces, asks UIKit once per turn; who decides. `ANNSystemUIRules` has the pure rules. Called by: Screens, containers, `ANNShownScreens`, `mergeOptions`.
|
|
131
|
+
- `ANNSystemUI`: The system UI of the windows: marks groups, coalesces, asks UIKit once per turn; who decides; after a status bar ask, the screens' status bar blurs take its frame. `ANNSystemUIRules` has the pure rules. Called by: Screens, containers, `ANNShownScreens`, `mergeOptions`.
|
|
131
132
|
- `ANNReloadCoordinator`: The generation of the JavaScript; the reset after a reload. Called by: Runner, emitter, answer.
|
|
132
133
|
- Stack answer id: `ANNStackAnswerIdOf(entry, command)` in `ANNStackQueue.h`: the id a stack command answers with. `push` answers the node's own id (a pair's id); every other command, the entry's options screen (a pair's primary). Called by: The queue, `ANNStackCommands`, `ANNPreviewPush`, `ANNSplitViewCommands`.
|
|
133
134
|
- Waited screens: `waitedComponentViewControllers`: the screens a wait is about (a pair: the ones it puts in the window). `ANNStackScreensOfEntry` is every screen of an entry, for what means all of them. Called by: `ANNRenderWait` owners: root, stack queue, modals, overlays, `ANNSplitViewCommands`.
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
4
|
|
|
5
5
|
/// The one answer of a command with a promise: the first `resolve:` or `reject:` answers, any later one does nothing,
|
|
6
|
-
/// and nothing answers once the JavaScript that sent the command went.
|
|
6
|
+
/// and nothing answers once the JavaScript that sent the command went. One that goes away unanswered while its
|
|
7
|
+
/// JavaScript runs rejects `[ANNErrorTexts notAnswered:]`. `ANNCommandRunner` makes it. Main queue.
|
|
7
8
|
@interface ANNCommandAnswer : NSObject
|
|
8
9
|
|
|
9
10
|
- (instancetype)initWithCommandName:(NSString *)commandName
|
|
10
11
|
commandId:(nullable NSString *)commandId
|
|
11
12
|
generation:(NSUInteger)generation
|
|
12
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
13
|
-
reject:(RCTPromiseRejectBlock)reject NS_DESIGNATED_INITIALIZER;
|
|
13
|
+
resolve:(nullable RCTPromiseResolveBlock)resolve
|
|
14
|
+
reject:(nullable RCTPromiseRejectBlock)reject NS_DESIGNATED_INITIALIZER;
|
|
14
15
|
|
|
15
16
|
- (instancetype)init NS_UNAVAILABLE;
|
|
16
17
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
@@ -41,6 +41,16 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
41
41
|
return self;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// The last reference went without an answer: nothing took the command, or what took it let it go. The promise of a
|
|
45
|
+
// JavaScript that still runs rejects, so it never waits forever. Any thread: the promise blocks are React Native's,
|
|
46
|
+
// safe from any thread, and the generation is readable from any thread.
|
|
47
|
+
- (void)dealloc {
|
|
48
|
+
if (_answered || !self.isCurrent) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
[ANNErrorHandler reject:_reject of:_commandName text:[ANNErrorTexts notAnswered:_commandName]];
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
- (BOOL)isCurrent {
|
|
45
55
|
return _generation == ANNReloadCoordinator.generation;
|
|
46
56
|
}
|
|
@@ -77,7 +87,7 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
77
87
|
reason:reason]];
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
|
-
resolve
|
|
90
|
+
[ANNErrorHandler resolve:resolve of:_commandName value:value];
|
|
81
91
|
if (_commandId && [ANNCompletedCommandNames() containsObject:_commandName]) {
|
|
82
92
|
double completionTime = floor(NSDate.date.timeIntervalSince1970 * 1000);
|
|
83
93
|
[ApexNavigation.eventEmitter sendCommandCompleted:_commandName
|
|
@@ -93,7 +103,7 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
93
103
|
RCTPromiseRejectBlock reject = _reject;
|
|
94
104
|
_resolve = nil;
|
|
95
105
|
_reject = nil;
|
|
96
|
-
[ANNErrorHandler reject:reject text:text];
|
|
106
|
+
[ANNErrorHandler reject:reject of:_commandName text:text];
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
@end
|
|
@@ -22,8 +22,8 @@ typedef void (^ANNCommandBody)(id _Nullable layout, ANNCommandAnswer *answer);
|
|
|
22
22
|
+ (void)run:(NSString *)name
|
|
23
23
|
commandId:(nullable NSString *)commandId
|
|
24
24
|
what:(NSString *)what
|
|
25
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
26
|
-
reject:(RCTPromiseRejectBlock)reject
|
|
25
|
+
resolve:(nullable RCTPromiseResolveBlock)resolve
|
|
26
|
+
reject:(nullable RCTPromiseRejectBlock)reject
|
|
27
27
|
check:(nullable ANNCommandCheck)check
|
|
28
28
|
body:(ANNCommandBody)body;
|
|
29
29
|
|
|
@@ -37,7 +37,7 @@ static NSString *ANNRejectionOf(NSException *exception, NSString *what) {
|
|
|
37
37
|
error = ANNRejectionOf(exception, what);
|
|
38
38
|
}
|
|
39
39
|
if (error) {
|
|
40
|
-
[ANNErrorHandler reject:reject text:error];
|
|
40
|
+
[ANNErrorHandler reject:reject of:name text:error];
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/errors.json: change that file and run the script again.
|
|
2
|
+
|
|
1
3
|
#import <Foundation/Foundation.h>
|
|
2
4
|
|
|
3
5
|
NS_ASSUME_NONNULL_BEGIN
|
|
@@ -51,12 +53,12 @@ FOUNDATION_EXPORT NSExceptionName const ANNCommandRejectionException;
|
|
|
51
53
|
+ (NSString *)stackTakesScreens:(NSString *)stackId type:(NSString *)type;
|
|
52
54
|
+ (NSString *)emptyStack:(NSString *)stackId;
|
|
53
55
|
+ (NSString *)emptyTabs:(NSString *)tabsId;
|
|
56
|
+
/// Only Android checks it: its tab bar shows at most 5 tabs.
|
|
54
57
|
+ (NSString *)tooManyTabs:(NSString *)tabsId count:(NSUInteger)count;
|
|
55
58
|
+ (NSString *)splitViewColumns:(NSString *)splitViewId;
|
|
59
|
+
/// Only iOS checks it: Android has pairs on every version.
|
|
56
60
|
+ (NSString *)pairUnsupported:(NSString *)pairId;
|
|
57
61
|
+ (NSString *)pairNeedsTwoScreens:(NSString *)pairId;
|
|
58
|
-
|
|
59
|
-
/// The other cases of the layout check: the texts both platforms use meanwhile.
|
|
60
62
|
+ (NSString *)nodeWithoutName:(NSString *)type nodeId:(NSString *)nodeId;
|
|
61
63
|
+ (NSString *)rootMissing;
|
|
62
64
|
+ (NSString *)tabsInTabs:(NSString *)tabsId;
|
|
@@ -77,6 +79,8 @@ FOUNDATION_EXPORT NSExceptionName const ANNCommandRejectionException;
|
|
|
77
79
|
+ (NSString *)couldNot:(NSString *)what reason:(NSString *)reason;
|
|
78
80
|
/// A command whose part of the native core is not written yet.
|
|
79
81
|
+ (NSString *)notDoneYet:(NSString *)method;
|
|
82
|
+
/// A command that nothing in the native core answered: what took it let it go, or nothing took it.
|
|
83
|
+
+ (NSString *)notAnswered:(NSString *)method;
|
|
80
84
|
|
|
81
85
|
/// An `ANNCommandRejectionException` with `text` as its reason: the command that runs rejects with `text` as it is.
|
|
82
86
|
+ (NSException *)rejection:(NSString *)text;
|