@apexnative/navigation 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +22 -0
- package/android/CLAUDE.md +26 -9
- package/android/CORE.md +32 -17
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +25 -9
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +33 -22
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +10 -5
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +47 -46
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +25 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +8 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +8 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +61 -15
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +25 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +85 -29
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +5 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +14 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +56 -21
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/cli/doctor/checks/app.js +61 -13
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +6 -7
- package/docs-md/containers/modals.md +2 -2
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/help/troubleshooting.md +22 -5
- package/docs-md/index.json +4 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +24 -2
- package/docs-md/start/installation.md +4 -2
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +18 -13
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +28 -30
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNPreviewPush.h +5 -5
- package/ios/Layouts/Stack/ANNPreviewPush.mm +85 -22
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +52 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +7 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +23 -4
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +1 -1
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +24 -4
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/topBar.ts +22 -9
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-alpha.5](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.4...v1.0.0-alpha.5) (2026-10-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* android back options, a launch screen copy from UILaunchScreen, modal and back fixes ([#43](https://github.com/apexnative/navigation/issues/43)) ([29f311e](https://github.com/apexnative/navigation/commit/29f311e318134f284a0c6279e6c86e15612c9343))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **android:** send the state of a tab switch from code at its start ([#44](https://github.com/apexnative/navigation/issues/44)) ([34d03d0](https://github.com/apexnative/navigation/commit/34d03d0214cf09e14329d0bae51e47d46063f054))
|
|
14
|
+
* **hooks:** measure agent folder scopes from the worktree a file is in ([#42](https://github.com/apexnative/navigation/issues/42)) ([d306137](https://github.com/apexnative/navigation/commit/d306137534d3ba551fab8a7346c7ad7300c0faf7))
|
|
15
|
+
|
|
16
|
+
## [1.0.0-alpha.4](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.3...v1.0.0-alpha.4) (2026-10-23)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **ci:** keep a closed React Native issue instead of opening a second one ([#37](https://github.com/apexnative/navigation/issues/37)) ([242030c](https://github.com/apexnative/navigation/commit/242030c8559a6fe186f44781ada85d09970c8318))
|
|
22
|
+
* **ios:** lay a waiting screen out in its place, so the large title comes in with it ([#41](https://github.com/apexnative/navigation/issues/41)) ([85bfaa8](https://github.com/apexnative/navigation/commit/85bfaa877d3775264c1ba1114942cf875f890f29))
|
|
23
|
+
* one native contract, options as layers and pushes that wait for the first render ([#39](https://github.com/apexnative/navigation/issues/39)) ([888f21e](https://github.com/apexnative/navigation/commit/888f21eb2b3e1aec413413bbdab9546a7a8f5a6b))
|
|
24
|
+
|
|
3
25
|
## [1.0.0-alpha.3](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.2...v1.0.0-alpha.3) (2026-10-22)
|
|
4
26
|
|
|
5
27
|
|
package/android/CLAUDE.md
CHANGED
|
@@ -21,10 +21,11 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
21
21
|
`OptionsReader`, `OptionsCache`, `DefaultOptions`. `params/` has the value types (`Bool`, `Text`, `Fraction`,
|
|
22
22
|
`Variant`, `ThemeColour`); `parsers/` reads them tolerantly from React Native data.
|
|
23
23
|
- `react/`: the way in and out: `NavigationTurboModule`, `CommandRunner`, `CommandAnswer`, `ErrorTexts`,
|
|
24
|
-
`OptionsCommands`, `
|
|
25
|
-
`ScreenRegions`, `ArrangementStates`, `Hinge`, `StartInfo`),
|
|
24
|
+
`OptionsCommands`, `PendingCalls` (a task behind the calls JavaScript made), `ReloadCoordinator`, `ReactGateway`,
|
|
25
|
+
`ReactView` (a React root), the stores (`ScreenInsets`, `ScreenRegions`, `ArrangementStates`, `Hinge`, `StartInfo`),
|
|
26
|
+
`LaunchLink`, `ScrollEvents`.
|
|
26
27
|
- `react/events/`: `AppearanceDispatcher`, `ScreenAppearance`, `EventEmitter`, `EventContract` (made by
|
|
27
|
-
`scripts/contract.mjs`), `
|
|
28
|
+
`scripts/contract.mjs`), `NavigationState`, `ComponentType`.
|
|
28
29
|
- `viewcontrollers/viewcontroller/`: `ViewController` (the base of every node), `Presenter`, `IReactView` (what a
|
|
29
30
|
screen shows), `ScrollEventListener`.
|
|
30
31
|
- `viewcontrollers/child/`, `parent/`: `ChildController` and the registry; `ParentController` (the base of layouts),
|
|
@@ -53,7 +54,8 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
53
54
|
- `slots/`, `safearea/`, `nativescreen/`: the views of `ANNSlot` and `ANNSafeAreaView`, the slot screen, the
|
|
54
55
|
native-screen context and calls.
|
|
55
56
|
- `splash/`, `customrow/`, `utils/`: the splash hold, theme and copy; the tabs' custom row options; helpers
|
|
56
|
-
(`UiThread`, `ImageLoader`, `MaterialTheme`, `TestIds`, `WindowFolds`,
|
|
57
|
+
(`UiThread`, `ImageLoader` with `OptionImages`, `ImageKey`, `ImageRequests`, `MaterialTheme`, `TestIds`, `WindowFolds`,
|
|
58
|
+
`DevelopmentLog`).
|
|
57
59
|
- `android/src/main/jni/`: `CMakeLists.txt` builds the codegen target with the C++ of `ios/cpp` (the slot's and the
|
|
58
60
|
safe area view's shadow nodes); `ApexNavigationSpec.h` replaces the generated header so autolinking sees their
|
|
59
61
|
descriptors.
|
|
@@ -103,15 +105,17 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
103
105
|
- System bars: `system/SystemUiOwner.kt`, `SystemUiLayers.kt`, `SystemUiRules.kt`, `SystemUiWindow.kt`,
|
|
104
106
|
`AppDirection.kt`; `viewcontrollers/parent/ContainerBackground.kt`.
|
|
105
107
|
- Events and state: `react/events/AppearanceDispatcher.kt`, `ScreenAppearance.kt`, `EventEmitter.kt`,
|
|
106
|
-
`
|
|
108
|
+
`NavigationState.kt`; `react/CommandAnswer.kt`.
|
|
107
109
|
- Options (parsing, merging): `options/Options.kt`, `OptionsReader.kt`, `OptionsCache.kt`, `DefaultOptions.kt`,
|
|
108
110
|
`params/`, `parsers/`; `viewcontrollers/viewcontroller/ViewController.kt` (resolution).
|
|
109
|
-
- Error texts: `react/ErrorTexts.kt`, made by `scripts/contract.mjs` from
|
|
111
|
+
- Error texts and answers: `react/ErrorTexts.kt` and `react/CommandContract.kt`, made by `scripts/contract.mjs` from
|
|
112
|
+
`src/interfaces/contract/errors.json` and `commands.json`.
|
|
110
113
|
- Links: `react/LaunchLink.kt`, `NavigationActivity.kt` (`onNewIntent`); `react/StartInfo.kt`.
|
|
111
114
|
- Hinge and regions: `react/Hinge.kt`, `react/ScreenRegions.kt`, `react/ScreenInsets.kt`, `utils/WindowFolds.kt`.
|
|
112
115
|
- Native screens: `nativescreen/NativeScreenContext.kt`, `NativeScreenCalls.kt`, `NativeScreenCreator.kt`;
|
|
113
116
|
`viewcontrollers/externalcomponent/`.
|
|
114
|
-
- Slots: `slots/SlotHost.kt`, `SlotView.kt`, `SlotScreenView.kt`, `SlotContainers.kt
|
|
117
|
+
- Slots: `slots/SlotHost.kt`, `SlotView.kt`, `SlotScreenView.kt`, `SlotContainers.kt` (containers and slot views by
|
|
118
|
+
slot id: a change goes straight to the views of its id, `SlotViews.kt`), `SlotViewManager.kt`.
|
|
115
119
|
- Safe area: `safearea/SafeAreaView.kt`, `SafeAreaViewManager.kt`; `viewcontrollers/component/ScreenReadiness.kt`;
|
|
116
120
|
`react/ScreenInsets.kt`.
|
|
117
121
|
- The C++ of `ANNSlot`, `ANNSafeAreaView`: `android/src/main/jni/CMakeLists.txt`, `ApexNavigationSpec.h`; sources in
|
|
@@ -125,7 +129,11 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
125
129
|
- **Module thread.** The `check` of every command with a promise: `LayoutReader` and, through it, `OptionsReader` (also
|
|
126
130
|
for `setDefaultOptions`, `mergeOptions` and the options of `dismissModal`, in the runner's `prepare` step);
|
|
127
131
|
`ReadableJson` for native-screen props. The registry of native screens is read under its lock. Images are not loaded
|
|
128
|
-
here;
|
|
132
|
+
here; they load when the options apply, through the one cache of `utils/OptionImages` (by `ImageKey`): at once on
|
|
133
|
+
main where the first frame needs them (`ImageLoader`: the top bar's buttons, menus, back button and search
|
|
134
|
+
suggestions; a tab's first icon; a new FAB), on its background thread for a file or a drawable of the app where the
|
|
135
|
+
view takes them when ready (`ImageRequests`: a changed tab icon or one that didn't load, a FAB's changed icon, the
|
|
136
|
+
icon of a FAB hidden with `visible: false`, the FAB menu's actions).
|
|
129
137
|
- **Main thread.** Everything else: the command body, nodes, views, events, the stores' writes. Any thread may read
|
|
130
138
|
`ReloadCoordinator.generation`.
|
|
131
139
|
|
|
@@ -155,13 +163,18 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
155
163
|
skips the table that decides interrupts and answers.
|
|
156
164
|
- Every event of an appearance goes through `AppearanceDispatcher`. A container with its own loop of
|
|
157
165
|
`onViewWillAppear` / `onViewDidAppear` / `onViewDisappear` sends the events in the wrong order.
|
|
158
|
-
- Never change a resolved options object: it is cached and shared. Copy it, or write through `changeOptions
|
|
166
|
+
- Never change a resolved options object: it is cached and shared. Copy it, or write through `changeOptions`; a
|
|
167
|
+
user's choice goes through `changeChoices` (the user layer), never into own options, or the next `mergeOptions`
|
|
168
|
+
wipes it.
|
|
159
169
|
- A new option field that is not in `mergeWith`, `mergeWithDefault`, `copy` or the fixture is dropped silently somewhere
|
|
160
170
|
in the chain; `WholeTreeTest` exists to catch it.
|
|
161
171
|
- A new wait is a new `RenderWait` value, not a new timer. A wait that survives a destroy or a supersede hangs a promise;
|
|
162
172
|
`cancel` it on every exit.
|
|
163
173
|
- A layer that waits to show must be a `WaitingLayer` and leave on every exit (show, supersede, destroy, root gone).
|
|
164
174
|
- Never resolve a promise yourself: only `CommandAnswer` knows whether its JavaScript went and sends the state first.
|
|
175
|
+
- An event of a screen goes to the JavaScript the screen was made for: `EventEmitter.of(node.jsGeneration)`, or an
|
|
176
|
+
emitter kept from then (it goes silent after a reload). React Native keeps one context across a reload, so a sender
|
|
177
|
+
taken from the context at event time tells the new JavaScript about a screen it never had.
|
|
165
178
|
- A signal that comes inside a layout pass or a view's attach must not move views there; the stack posts readiness
|
|
166
179
|
(`deliver`).
|
|
167
180
|
- A step that runs later (a timer, a readiness signal, an animator's end, a user callback) runs under no runner: it
|
|
@@ -196,6 +209,10 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
196
209
|
- The back gesture's preview shows the screen under the top outside any operation of the queue: it may only reveal
|
|
197
210
|
it and put it back, and its release goes through `StackQueue.cut(gesture = true)`. A command that comes meanwhile
|
|
198
211
|
stops it through `StackDisplay.stop`, like any transition it takes over.
|
|
212
|
+
- What back does (`Navigator.canHandleBack`, `keepsBackFromSystem`) is computed on events, not before each frame: a
|
|
213
|
+
new path that changes it without a command (a gesture, an animator's end, a layout's switch) calls
|
|
214
|
+
`BackCallbackState.update`, or the system's back leaves the app where it should go back. Debug builds log a missed
|
|
215
|
+
one (`BackCallbackCheck`).
|
|
199
216
|
- Without edge to edge the window resizes for the keyboard by the manifest's soft input mode, so
|
|
200
217
|
`layout.adjustResize: false` goes through the system UI owner's keyboard group (`SystemUiWindow.setKeyboardCovers`),
|
|
201
218
|
not through the screen's padding.
|
package/android/CORE.md
CHANGED
|
@@ -31,7 +31,7 @@ coordinators: RootCoordinator · StackQueue (one per stack) · ModalCoordinator
|
|
|
31
31
|
presenters: ComponentPresenter · StackPresenter + TopBarPresenter · BottomTabsPresenter · SideMenuPresenter ·
|
|
32
32
|
SplitViewPresenter · system/SystemUiOwner (status bar, navigation bar, orientation, keyboard)
|
|
33
33
|
▼
|
|
34
|
-
react/events/AppearanceDispatcher ─►
|
|
34
|
+
react/events/AppearanceDispatcher ─► NavigationState ─► EventEmitter ─► ANN.* events ─► JS
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
- **Way in.** `NavigationTurboModule` only turns a method into a `CommandRunner` call; it holds no navigation logic. It
|
|
@@ -72,18 +72,24 @@ react/events/AppearanceDispatcher ─► NavigationStateReporter ─► EventEmi
|
|
|
72
72
|
5. **Make and join.** `LayoutFactory.create` makes Profile (a `ComponentViewController`, registered in the
|
|
73
73
|
`ChildControllersRegistry`); `StackController.join` adds it; `StackQueue.push` accepts it as an entry.
|
|
74
74
|
6. **`StackQueue`** is the only way to change a stack's children. `drain` starts the operation when nothing blocks it.
|
|
75
|
-
`startPush` decides the wait:
|
|
76
|
-
|
|
75
|
+
`startPush` decides the wait: a prepared push; `waitForRender: true` (as long as it takes); unset, for a screen with
|
|
76
|
+
React content, Profile's first mount, 0.5 s at most (`PUSH_WAIT_MILLIS`); none for `false`, a native screen or a
|
|
77
|
+
stack off screen. A wait is a `RenderWait` on Profile. Its mount reaches the push through the queue's `afterCalls`
|
|
78
|
+
(`PendingCalls`: once through the JavaScript thread, the module's thread and the main thread, the way of a
|
|
79
|
+
`mergeOptions`), so the `mergeOptions` of the commit that mounted it (`useScreenOptions`) has applied when the
|
|
80
|
+
transition starts, content mounted before the push too; the prepared push starts the same way after its slot
|
|
81
|
+
mounted. Unset, the targets of elements take what is left of the 0.5 s.
|
|
77
82
|
7. **Begin.** `beginPush` raises Profile's view (`StackViews`), moves the bar and the tab bar
|
|
78
83
|
(`StackQueue.Host.barTransition`, `tabBarTransition`), calls `StackController` through `currentChildChanged`, then
|
|
79
84
|
`AppearanceDispatcher.beginTransition`: **`ComponentWillAppear`** of Profile. The transition runs
|
|
80
85
|
(`StackTransitions`, `SystemTransition`, or the screens' own animation, `AnimationRules.stackIsOwn`).
|
|
81
86
|
8. **End.** `finishPush` calls `AppearanceDispatcher.endTransition`: **`ComponentDidDisappear`** of Home, then
|
|
82
|
-
**`ComponentDidAppear`** of Profile, then
|
|
83
|
-
transition's own events (none for a push), then
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
**`ComponentDidAppear`** of Profile, then the answer's one state step (`CommandAnswer.reportStateNow`:
|
|
88
|
+
**`NavigationStateChanged`**, only if it changed), then the transition's own events (none for a push), then
|
|
89
|
+
`CommandAnswer.resolve` with Profile's id.
|
|
90
|
+
9. **Answer.** `resolve` sends the state only when no transition sent it (`reportStateNow` runs once per answer: a
|
|
91
|
+
`setRoot` with layout modals sends it here), resolves the promise, then sends **`CommandCompleted`**
|
|
92
|
+
(`CommandContract.COMPLETED`). After the task, `BackCallbackState.update` recomputes what back does.
|
|
87
93
|
|
|
88
94
|
The events go from the main thread, synchronously, through `EventEmitter.current()`; the promise is answered once.
|
|
89
95
|
|
|
@@ -122,13 +128,16 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
122
128
|
`beginTransition` starts the appearances; `endTransition` runs what left, then what came, then the state, then the
|
|
123
129
|
transition's events, then the answer. Events asked before a screen's content is ready are held in its
|
|
124
130
|
`ScreenAppearance` (until the content mounts or `HOLD_LIMIT_MILLIS`).
|
|
125
|
-
- **Navigation state.** `
|
|
126
|
-
|
|
127
|
-
`
|
|
128
|
-
|
|
131
|
+
- **Navigation state.** `NavigationState` owns it: the last state sent, the walk (`stateOf`), the posted update and its
|
|
132
|
+
holds (`NavigationState.Updates`). `AppearanceDispatcher.state` (`StateUpdates`) is the one way to ask for it:
|
|
133
|
+
`setNeedsUpdate` coalesces the asks to one posted update per pass of the main loop and keeps it while held; the
|
|
134
|
+
update calls `updateNow`. A command's answer sends it once, at once and never held (`CommandAnswer.reportStateNow`):
|
|
135
|
+
at the end of its transition, else right before its promise for the commands that change what shows; nothing for a
|
|
136
|
+
JavaScript that went. It reads the navigator through `Navigators.current()`, and `stateOf` finds the
|
|
129
137
|
`tab` as the nearest tabs of the visible screen (the last tabs met walking down from the visible layout). A `setRoot`
|
|
130
138
|
with layout modals or overlays takes `AppearanceDispatcher.StateUpdates.hold()` and releases it right before its
|
|
131
|
-
answer, so JavaScript hears one state; every hold has one `release()`.
|
|
139
|
+
answer, so JavaScript hears one state; every hold has one `release()`. The holds go when the JavaScript goes
|
|
140
|
+
(`NavigationState.javaScriptWent`), so one left by its `setRoot` never holds the next JavaScript's updates.
|
|
132
141
|
- **Answer.** `CommandAnswer`: resolves or rejects once, and nothing once the JavaScript went. The id a stack entry
|
|
133
142
|
answers with is one rule, `StackController.answerIdOf` (the queue asks it through `StackQueue.Host.answerIdOf` with
|
|
134
143
|
an `AnswerKind`): `push` answers the entry's own id (a pair's), every other command the screen it stands for (a
|
|
@@ -136,7 +145,11 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
136
145
|
- **Stack queue.** `StackQueue`: every command, back and return of a container goes through it; it keeps `entries`
|
|
137
146
|
(what JavaScript sees) and `shown` (what the container shows) and answers once.
|
|
138
147
|
- **Screen readiness.** `ScreenReadiness` (by id; written by `SafeAreaView`) and `ContentReadiness` (the `Readiness`
|
|
139
|
-
the waits ask, through `IReactView.isReady`).
|
|
148
|
+
the waits ask, through `IReactView.isReady`). As on iOS, the signals are kept (a listener that comes later hears them
|
|
149
|
+
at once), an id has a listener per key (several roots of one bar component), and each signal carries the React
|
|
150
|
+
surface it came from: a `ReactView` takes only its own surface's, so an old root with the same id never makes a new
|
|
151
|
+
one ready. An id renders at once while a root marks it (`ReactView`'s `rendersAtOnce`); the last one takes the id's
|
|
152
|
+
signals with it. A screen's id is cleared as the screen is made and destroyed, everything on reload.
|
|
140
153
|
- **Render wait.** `RenderWait`: the one wait for screens to render, used by `RootCoordinator`, `ModalCoordinator`,
|
|
141
154
|
`OverlayCoordinator` and `StackQueue`; `RenderWait.waitedScreens` walks `ParentController.waitedChildren()` at every
|
|
142
155
|
level. What differs between users are values (limit, bar phase, unknown-content rule), never steps.
|
|
@@ -166,7 +179,8 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
166
179
|
- **Back.** `NavigationActivity.passBack` → `ReactGateway.onBackPressed` (JavaScript's `BackHandler`) →
|
|
167
180
|
`invokeDefaultOnBackPressed` → `Navigator.handleBack` → `ModalCoordinator.handleBack` → the root's
|
|
168
181
|
`ParentController.handleBack` → `StackController.handleBack` → `StackQueue.cut`. `BackCallbackState` keeps the
|
|
169
|
-
system callback enabled only while back has something to do
|
|
182
|
+
system callback enabled only while back has something to do, computed again on the events that change it (its KDoc
|
|
183
|
+
lists them), never before each frame; debug builds check it (`BackCallbackCheck`).
|
|
170
184
|
- **Error texts.** `ErrorTexts`: one function per text, the same words as iOS, made with iOS's from
|
|
171
185
|
`src/interfaces/contract/errors.json` (`scripts/contract.mjs`). JavaScript adds `[navigation] <command>:`.
|
|
172
186
|
- **Event names and keys.** `EventContract`, made from the event types of `src/interfaces/`; `EventEmitter` and the
|
|
@@ -196,7 +210,7 @@ Modules are the feature classes that plug into the core through its contract (th
|
|
|
196
210
|
(`splash/`), animations (`FadeThrough`, `SystemTransition`, `TransitionTargets`, three element animators), utils
|
|
197
211
|
(`MaterialTheme`, `NavigationLog`, `awaitBarComponents`, `whenDrawn`).
|
|
198
212
|
- **Stores and singletons:** regions (`Hinge`, `ScreenInsets`, `ScreenRegions`, `WindowFolds`), pair state
|
|
199
|
-
(`ArrangementStates`), links (`LaunchLink`, `StartInfo`), events (`
|
|
213
|
+
(`ArrangementStates`), links (`LaunchLink`, `StartInfo`), events (`NavigationState`), commands
|
|
200
214
|
(`BackCallbackState`), stack (`PreparedFrame`).
|
|
201
215
|
|
|
202
216
|
How they plug in:
|
|
@@ -221,4 +235,5 @@ How they plug in:
|
|
|
221
235
|
- **Core code in a module's folder:** `viewcontrollers/splitview/SplitViewPresenter.kt`, `slots/SlotScreenView.kt`,
|
|
222
236
|
and in `views/animations/` and `views/element/` everything except the files named above.
|
|
223
237
|
- **Stores** are written on the main thread by the core and read from any thread; they are cleared with their nodes and on
|
|
224
|
-
reload.
|
|
238
|
+
reload. Their events, and the search's, go through the emitter of the JavaScript the screen was made for
|
|
239
|
+
(`EventEmitter.of(node.jsGeneration)`); the hinge keeps the `EventEmitter.current()` of the call that turned it on.
|
package/android/RECIPES.md
CHANGED
|
@@ -14,7 +14,11 @@ The steps for adding an option and for adding a command to the Android core; rea
|
|
|
14
14
|
(a repaint after a light/dark switch or `setDefaultOptions`); a system-bar option goes through `SystemUiRules`.
|
|
15
15
|
Read colors with `ThemeColour.get` at apply so they follow the theme.
|
|
16
16
|
4. The cache needs nothing: own options are written only through `ViewController.options`, `changeOptions` and
|
|
17
|
-
`mergeOptions
|
|
17
|
+
`mergeOptions` (`mergeOwnOptions`), defaults only through `DefaultOptions.set`. Never change a resolved result.
|
|
18
|
+
What the user chooses in the native UI (a menu's check, a search scope) is not written into own options: it goes
|
|
19
|
+
into the node's user layer through `ViewController.changeChoices` (`options/UserChoices.kt`), which applies it by
|
|
20
|
+
id over the resolved options and lets it go when a merge changes that field. A new kind of choice is a field of
|
|
21
|
+
`UserChoices` with its `applyTo` and `yieldTo`; `changeOptions` stays for the library's own bookkeeping of a merge.
|
|
18
22
|
5. Tests: add the field to `OptionsFixture.full()`. `WholeTreeTest` walks the tree by reflection and fails when a field
|
|
19
23
|
is not read, copied, merged or defaulted. Add cases to `ReadersTest`, `MergeRuleTest` and the area's rules test.
|
|
20
24
|
6. The docs page and a Showcase control; one meaning on both platforms, and where Android can't do it the JSDoc says so
|
|
@@ -31,8 +35,10 @@ The steps for adding an option and for adding a command to the Android core; rea
|
|
|
31
35
|
4. New layout fields are read and checked in `LayoutReader`; checks against what shows are in `LayoutChecks` or in the
|
|
32
36
|
command, before any node is made. Every rejection text is a function in `ErrorTexts` (same words as iOS),
|
|
33
37
|
made by `node scripts/contract.mjs` from `src/interfaces/contract/errors.json`: a new text goes there.
|
|
34
|
-
5. The answer goes through `CommandAnswer`:
|
|
35
|
-
(
|
|
38
|
+
5. The answer goes through `CommandAnswer`: give the command its entry in `src/interfaces/contract/commands.json`
|
|
39
|
+
(what it resolves with, `state` when it may change what shows, `completed` when it shows or dismisses) and run
|
|
40
|
+
`node scripts/contract.mjs`, which makes `CommandContract` (then the state, the promise and `CommandCompleted` follow
|
|
41
|
+
in that order). A transition ends through
|
|
36
42
|
`AppearanceDispatcher.endTransition`.
|
|
37
43
|
6. Tests: `CommandRunnerTest`, `CommandAnswerTest`, `ErrorTextsTest`, `LayoutReaderTest`; a stack operation also needs a
|
|
38
44
|
case in the queue tests (`StackQueueHarness`).
|
|
@@ -35,6 +35,7 @@ import dev.apexnative.navigation.splash.SplashTheme
|
|
|
35
35
|
import dev.apexnative.navigation.splash.SplashView
|
|
36
36
|
import dev.apexnative.navigation.system.SystemUiWindow
|
|
37
37
|
import dev.apexnative.navigation.utils.MaterialTheme
|
|
38
|
+
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackCheck
|
|
38
39
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
39
40
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
40
41
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
|
|
@@ -75,6 +76,7 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
75
76
|
private val backCallback = object : OnBackPressedCallback(false) {
|
|
76
77
|
override fun handleOnBackStarted(backEvent: BackEventCompat) {
|
|
77
78
|
backGesture = null
|
|
79
|
+
navigator?.let { backCheck?.atBack(it) }
|
|
78
80
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || BackCallbackState.hasJsListeners) return
|
|
79
81
|
val stack = navigator?.stackPoppedByBack() ?: return
|
|
80
82
|
backGesture = stack.startBackGesture(backEvent.swipeEdge == BackEventCompat.EDGE_LEFT, backEvent.touchY)
|
|
@@ -87,9 +89,11 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
87
89
|
override fun handleOnBackCancelled() {
|
|
88
90
|
backGesture?.cancel()
|
|
89
91
|
backGesture = null
|
|
92
|
+
navigator?.let(BackCallbackState::update)
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
override fun handleOnBackPressed() {
|
|
96
|
+
navigator?.let { backCheck?.atBack(it) }
|
|
93
97
|
val gesture = backGesture
|
|
94
98
|
backGesture = null
|
|
95
99
|
if (gesture?.commit() == true) BackCallbackState.update() else passBack()
|
|
@@ -99,10 +103,12 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
99
103
|
// The back gesture that previews a pop now; none.
|
|
100
104
|
private var backGesture: StackController.BackGesture? = null
|
|
101
105
|
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
private val
|
|
105
|
-
|
|
106
|
+
// Debug builds only: the check that what back does never changes without its trigger (BackCallbackState), sampled
|
|
107
|
+
// before frames and at back events. It only logs; release builds have neither the check nor the listener.
|
|
108
|
+
private val backCheck = if (BuildConfig.DEBUG) BackCallbackCheck() else null
|
|
109
|
+
private val backCheckBeforeFrame = ViewTreeObserver.OnPreDrawListener {
|
|
110
|
+
val navigator = navigator
|
|
111
|
+
if (navigator != null) backCheck?.sample(navigator, SystemClock.uptimeMillis())
|
|
106
112
|
true
|
|
107
113
|
}
|
|
108
114
|
private var frameObserver: ViewTreeObserver? = null
|
|
@@ -143,7 +149,9 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
143
149
|
SlotHost.attachIfStarted(this)
|
|
144
150
|
reactGateway.onActivityCreated(this)
|
|
145
151
|
onBackPressedDispatcher.addCallback(this, backCallback)
|
|
146
|
-
|
|
152
|
+
if (backCheck != null) {
|
|
153
|
+
frameObserver = window.decorView.viewTreeObserver.also { it.addOnPreDrawListener(backCheckBeforeFrame) }
|
|
154
|
+
}
|
|
147
155
|
// The activity's system UI: its owner remembers the theme's colors and the manifest's orientation now, before
|
|
148
156
|
// anything changes them.
|
|
149
157
|
navigator.systemUi.start(SystemUiWindow.of(this))
|
|
@@ -218,6 +226,8 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
218
226
|
isNavigationResumed = true
|
|
219
227
|
// The root, the modals and the overlays lay out again; nothing is sent.
|
|
220
228
|
navigator?.onResume()
|
|
229
|
+
// A safety net: what back does changes on its own triggers, and the system asks the callback from here on.
|
|
230
|
+
navigator?.let(BackCallbackState::update)
|
|
221
231
|
if (developerSupport() != null) reloadBroadcast.register()
|
|
222
232
|
ReloadCoordinator.launchIfDue(this)
|
|
223
233
|
}
|
|
@@ -239,6 +249,8 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
239
249
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
240
250
|
super.onWindowFocusChanged(hasFocus)
|
|
241
251
|
if (reactGateway.currentReactContext != null) reactGateway.host.onWindowFocusChange(hasFocus)
|
|
252
|
+
// A safety net, as at onResume: back after a dialog or another window over the activity.
|
|
253
|
+
if (hasFocus) navigator?.let(BackCallbackState::update)
|
|
242
254
|
}
|
|
243
255
|
|
|
244
256
|
override fun onUserLeaveHint() {
|
|
@@ -302,9 +314,11 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
302
314
|
splashHold = null
|
|
303
315
|
reloadBroadcast.release()
|
|
304
316
|
navigator?.let(BackCallbackState::detach)
|
|
305
|
-
// The window's observer now, and the one
|
|
306
|
-
|
|
307
|
-
|
|
317
|
+
// The debug check's listener: from the window's observer now, and the one it was added to, if they differ.
|
|
318
|
+
if (backCheck != null) {
|
|
319
|
+
val observers = listOfNotNull(frameObserver, window?.peekDecorView()?.viewTreeObserver).distinct()
|
|
320
|
+
observers.forEach { if (it.isAlive) it.removeOnPreDrawListener(backCheckBeforeFrame) }
|
|
321
|
+
}
|
|
308
322
|
frameObserver = null
|
|
309
323
|
SlotHost.detach(this)
|
|
310
324
|
navigator?.destroyWithActivity()
|
|
@@ -317,7 +331,9 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
317
331
|
/**
|
|
318
332
|
* Back that JavaScript's listeners didn't take: the navigation does its step first — a stack's top leaves or holds,
|
|
319
333
|
* a pane closes —; with nothing to do there, the activity's own back, as without the navigation. An app may
|
|
320
|
-
* override it (e.g. `moveTaskToBack(true)`).
|
|
334
|
+
* override it (e.g. `moveTaskToBack(true)`). The callback is off for that back only: the dispatcher's own back is
|
|
335
|
+
* the activity's, not [onBackPressed]; then it is computed again, and `hardwareBackButton.systemAnimation: false`
|
|
336
|
+
* turns it on again.
|
|
321
337
|
*/
|
|
322
338
|
override fun invokeDefaultOnBackPressed() {
|
|
323
339
|
val handled = navigator?.handleBack(CommandListenerAdapter()) == true
|
|
@@ -3,32 +3,49 @@ package dev.apexnative.navigation
|
|
|
3
3
|
import android.app.Application
|
|
4
4
|
import android.util.Log
|
|
5
5
|
import com.facebook.react.ReactApplication
|
|
6
|
-
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
|
7
|
-
import com.facebook.react.soloader.OpenSourceMergedSoMapping
|
|
8
6
|
import com.facebook.soloader.SoLoader
|
|
9
7
|
import dev.apexnative.navigation.nativescreen.NativeScreenCreator
|
|
10
8
|
import dev.apexnative.navigation.react.ReactGateway
|
|
11
9
|
import dev.apexnative.navigation.utils.NavigationLog
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
|
-
* The application class of an app with the navigation: it
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* The application class of an app with the navigation: it keeps one React host for every screen ([reactGateway]) and
|
|
13
|
+
* the native screens by name. The app gives the host (`reactHost`) and loads React Native in its `onCreate` as the
|
|
14
|
+
* React Native template does, so the load follows the app's Gradle settings (the New Architecture, edge-to-edge):
|
|
15
|
+
*
|
|
16
|
+
* ```kotlin
|
|
17
|
+
* override fun onCreate() {
|
|
18
|
+
* super.onCreate()
|
|
19
|
+
* loadReactNative(this)
|
|
20
|
+
* registerNativeScreen("Profile") { activity, context -> ProfileView(activity, context) }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The gateway, and with it the host, is made the first time something needs it (the first activity), after the app's
|
|
25
|
+
* `onCreate` loaded React Native.
|
|
17
26
|
*/
|
|
18
27
|
abstract class NavigationApplication : Application(), ReactApplication {
|
|
19
28
|
// Written on the main thread as the app registers, read on the module thread by the layout checks: under its lock.
|
|
20
29
|
private val nativeScreens = LinkedHashMap<String, NativeScreenCreator>()
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
// Made once, by the first activity ([reactGateway]); [reactGatewayOrNull] only reads it.
|
|
32
|
+
private val gateway: Lazy<ReactGateway> = lazy {
|
|
33
|
+
check(SoLoader.isInitialized()) {
|
|
34
|
+
"NavigationApplication needs React Native loaded first: call loadReactNative(this) in onCreate, " +
|
|
35
|
+
"as the React Native template does"
|
|
36
|
+
}
|
|
37
|
+
createReactGateway()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The app's React host as the navigation runs it, made when it is first asked for (the first activity's
|
|
42
|
+
* `onCreate`). React Native must be loaded by then: the app calls `loadReactNative(this)` in its `onCreate`.
|
|
43
|
+
*/
|
|
44
|
+
val reactGateway: ReactGateway by gateway
|
|
25
45
|
|
|
26
46
|
override fun onCreate() {
|
|
27
47
|
super.onCreate()
|
|
28
48
|
instance = this
|
|
29
|
-
SoLoader.init(this, OpenSourceMergedSoMapping)
|
|
30
|
-
DefaultNewArchitectureEntryPoint.load()
|
|
31
|
-
reactGateway = createReactGateway()
|
|
32
49
|
registerGeneratedScreens()
|
|
33
50
|
}
|
|
34
51
|
|
|
@@ -57,9 +74,13 @@ abstract class NavigationApplication : Application(), ReactApplication {
|
|
|
57
74
|
internal fun isNativeScreenRegistered(name: String): Boolean =
|
|
58
75
|
synchronized(nativeScreens) { nativeScreens.containsKey(name) }
|
|
59
76
|
|
|
60
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* [reactGateway] once the first activity made it; none before. It never makes it: its callers (the log, the
|
|
79
|
+
* events, a React root) may run first, also off the main thread, and the app's `reactHost` isn't theirs to make.
|
|
80
|
+
* Any thread.
|
|
81
|
+
*/
|
|
61
82
|
internal val reactGatewayOrNull: ReactGateway?
|
|
62
|
-
get() = if (
|
|
83
|
+
get() = if (gateway.isInitialized()) gateway.value else null
|
|
63
84
|
|
|
64
85
|
// The list of `npx apex-navigation codegen`, found by its name: an app without native screens has none.
|
|
65
86
|
private fun registerGeneratedScreens() {
|
|
@@ -47,5 +47,9 @@ internal object LayoutChecks {
|
|
|
47
47
|
*/
|
|
48
48
|
@JvmStatic
|
|
49
49
|
fun checkStackChild(node: LayoutNode, stackId: String): String? =
|
|
50
|
-
if (node
|
|
50
|
+
if (isStackChild(node)) null else ErrorTexts.stackTakesScreens(stackId, node.type.jsName)
|
|
51
|
+
|
|
52
|
+
/** Whether a stack takes [node]: a screen, a native screen or a pair, not a container. */
|
|
53
|
+
@JvmStatic
|
|
54
|
+
fun isStackChild(node: LayoutNode): Boolean = node.type in stackChildren
|
|
51
55
|
}
|
|
@@ -82,10 +82,14 @@ internal data class LayoutNode(
|
|
|
82
82
|
private val COLUMN_NAMES = setOf(PRIMARY, SECONDARY, SUPPLEMENTARY, COMPACT, INSPECTOR)
|
|
83
83
|
private val COLUMNS_WITHOUT_NAMES = listOf(PRIMARY, SECONDARY)
|
|
84
84
|
|
|
85
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* The column of the child at [index] of a split view with [columns]; none for a child without one, and none
|
|
87
|
+
* for a repeat of a name an earlier child has (the first takes the column), as on iOS.
|
|
88
|
+
*/
|
|
86
89
|
internal fun columnName(columns: List<String>, index: Int): String? {
|
|
87
90
|
val names = columns.ifEmpty { COLUMNS_WITHOUT_NAMES }
|
|
88
|
-
|
|
91
|
+
val name = names.getOrNull(index)?.takeIf { it in COLUMN_NAMES } ?: return null
|
|
92
|
+
return name.takeIf { names.indexOf(it) == index }
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
}
|
|
@@ -44,8 +44,8 @@ class NativeScreenContext @RestrictTo(RestrictTo.Scope.LIBRARY) constructor(
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Runs a command of the navigation from this screen in JS, as `useNavigation()` of a React screen does: push, pop,
|
|
47
|
-
* popTo, popToRoot, showInColumn, showModal, dismiss, showOverlay,
|
|
48
|
-
*
|
|
47
|
+
* popTo, popToRoot, showInColumn, showModal, dismiss, showOverlay, setOptions, selectTab, setTabBadge.
|
|
48
|
+
* [arguments] are those of the JS command; [result] gets what it resolved with.
|
|
49
49
|
*/
|
|
50
50
|
@JvmOverloads
|
|
51
51
|
fun navigate(command: String, arguments: JSONArray = JSONArray(), result: ((Result<Any?>) -> Unit)? = null) =
|
|
@@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
8
8
|
*/
|
|
9
9
|
class AnimationsOptions {
|
|
10
10
|
@JvmField var push = StackPushAnimationOptions()
|
|
11
|
-
@JvmField var pop =
|
|
11
|
+
@JvmField var pop = StackPopAnimationOptions()
|
|
12
12
|
@JvmField var setStackRoot = SetStackRootAnimationOptions()
|
|
13
13
|
@JvmField var setRoot = EnterExitAnimationOptions()
|
|
14
14
|
@JvmField var showModal = ModalAnimationOptions()
|
|
@@ -43,7 +43,7 @@ class AnimationsOptions {
|
|
|
43
43
|
val options = AnimationsOptions()
|
|
44
44
|
if (map == null) return options
|
|
45
45
|
options.push = map.group("push") { StackPushAnimationOptions.parse(it) }
|
|
46
|
-
options.pop = map.group("pop") {
|
|
46
|
+
options.pop = map.group("pop") { StackPopAnimationOptions.parse(it) }
|
|
47
47
|
options.setStackRoot = map.group("setStackRoot") { SetStackRootAnimationOptions.parse(it) }
|
|
48
48
|
options.setRoot = map.group("setRoot") { EnterExitAnimationOptions.parse(it) }
|
|
49
49
|
options.showModal = map.group("showModal") { ModalAnimationOptions.parse(it) }
|
|
@@ -8,8 +8,8 @@ import dev.apexnative.navigation.options.parsers.IconParser
|
|
|
8
8
|
* A menu of the bar: `topBar.overflowMenu`, or the `menu` of a button. Its items, and submenus among them; with
|
|
9
9
|
* `singleSelection` one item of a level has a checkmark at a time.
|
|
10
10
|
*
|
|
11
|
-
* Copied options share it, so
|
|
12
|
-
*
|
|
11
|
+
* Copied options share it, so nobody changes a menu in place but in a copy of its own: the check a user's pick moves
|
|
12
|
+
* lives in the screen's user layer (`UserChoices`), which goes into a copy as the screen's options resolve.
|
|
13
13
|
*/
|
|
14
14
|
class ButtonMenuOptions(
|
|
15
15
|
@JvmField val singleSelection: Boolean,
|
|
@@ -197,8 +197,8 @@ class ButtonOptions(@JvmField var id: Text = NullText()) {
|
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
199
|
* A copy of the menu with new items and submenus and the same values. A menu taken from the default options is copied
|
|
200
|
-
* first, so that
|
|
201
|
-
* own and resolved options share the menu
|
|
200
|
+
* first, so that nothing written into a resolved result reaches [DefaultOptions.current]; the user layer writes its
|
|
201
|
+
* checks into a copy too (`UserChoices`), and copies between a node's own and resolved options share the menu.
|
|
202
202
|
*/
|
|
203
203
|
internal fun ButtonMenuOptions.copyMenu(): ButtonMenuOptions =
|
|
204
204
|
ButtonMenuOptions(singleSelection, items.map(::copyElement), testID)
|
package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt
CHANGED
|
@@ -15,20 +15,27 @@ class HardwareBackButtonOptions {
|
|
|
15
15
|
@JvmField var popStackOnPress: Bool = NullBool()
|
|
16
16
|
/** Internal. */
|
|
17
17
|
@JvmField var dismissModalOnPress: Bool = NullBool()
|
|
18
|
+
/**
|
|
19
|
+
* `systemAnimation`: `false` keeps back in the app on the first screen too, so the system doesn't play its
|
|
20
|
+
* animation of going home; the press then does the activity's own back.
|
|
21
|
+
*/
|
|
22
|
+
@JvmField var systemAnimation: Bool = NullBool()
|
|
18
23
|
|
|
19
24
|
fun hasValue(): Boolean = bottomTabsOnPress.hasValue() || popStackOnPress.hasValue() ||
|
|
20
|
-
dismissModalOnPress.hasValue()
|
|
25
|
+
dismissModalOnPress.hasValue() || systemAnimation.hasValue()
|
|
21
26
|
|
|
22
27
|
fun mergeWith(other: HardwareBackButtonOptions) {
|
|
23
28
|
if (other.bottomTabsOnPress.hasValue()) bottomTabsOnPress = other.bottomTabsOnPress
|
|
24
29
|
if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress
|
|
25
30
|
if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress
|
|
31
|
+
if (other.systemAnimation.hasValue()) systemAnimation = other.systemAnimation
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
fun mergeWithDefault(defaults: HardwareBackButtonOptions) {
|
|
29
35
|
if (!bottomTabsOnPress.hasValue()) bottomTabsOnPress = defaults.bottomTabsOnPress
|
|
30
36
|
if (!popStackOnPress.hasValue()) popStackOnPress = defaults.popStackOnPress
|
|
31
37
|
if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaults.dismissModalOnPress
|
|
38
|
+
if (!systemAnimation.hasValue()) systemAnimation = defaults.systemAnimation
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
fun copy() = HardwareBackButtonOptions().also { it.mergeWith(this) }
|
|
@@ -43,6 +50,7 @@ class HardwareBackButtonOptions {
|
|
|
43
50
|
options.bottomTabsOnPress = TextParser.parse(map, "bottomTabsOnPress", BOTTOM_TABS_ON_PRESS)
|
|
44
51
|
options.popStackOnPress = BoolParser.parse(map, "popStackOnPress")
|
|
45
52
|
options.dismissModalOnPress = BoolParser.parse(map, "dismissModalOnPress")
|
|
53
|
+
options.systemAnimation = BoolParser.parse(map, "systemAnimation")
|
|
46
54
|
return options
|
|
47
55
|
}
|
|
48
56
|
}
|
|
@@ -3,9 +3,10 @@ package dev.apexnative.navigation.options
|
|
|
3
3
|
/**
|
|
4
4
|
* The one point where the options of the app change: a process-wide generation that grows at each change. A node's
|
|
5
5
|
* resolved options are kept while the generation stays and the nodes they were built along are the same
|
|
6
|
-
* (`ViewController.resolveCurrentOptions`).
|
|
7
|
-
* (`ViewController.options`, and `changeOptions` through it)
|
|
8
|
-
* (
|
|
6
|
+
* (`ViewController.resolveCurrentOptions`). Three places call [optionsChanged]: the write of a node's own options
|
|
7
|
+
* (`ViewController.options`, and `changeOptions` and `mergeOwnOptions` through it), the write of a node's user layer
|
|
8
|
+
* (`ViewController.changeChoices`, and a merge that takes a choice away) and the store of new default options
|
|
9
|
+
* ([DefaultOptions.set]). Nothing else changes a node's own options, its user layer or the defaults. Main thread only.
|
|
9
10
|
*/
|
|
10
11
|
object OptionsCache {
|
|
11
12
|
/** The generation of the options; it only grows. */
|
|
@@ -13,7 +14,10 @@ object OptionsCache {
|
|
|
13
14
|
var generation: Long = 0
|
|
14
15
|
private set
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* A node's own options, its user layer or the default options changed: every resolved result built before is
|
|
19
|
+
* stale.
|
|
20
|
+
*/
|
|
17
21
|
@JvmStatic
|
|
18
22
|
fun optionsChanged() {
|
|
19
23
|
generation++
|