@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.10
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/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +120 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +78 -25
- package/android/CORE.md +49 -25
- package/android/RECIPES.md +13 -6
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +85 -10
- 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/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SideMenuOptions.kt +6 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- 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/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +35 -23
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +12 -7
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +42 -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 +54 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -50
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +50 -81
- 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 +135 -94
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +39 -19
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +16 -4
- 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/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- 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/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- 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 +109 -21
- 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 +476 -73
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
- 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 +49 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- 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 +6 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
- 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 +43 -4
- 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 +77 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
- 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 +115 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
- 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 +30 -34
- 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/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- 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 +219 -54
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- 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 +143 -135
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- 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 +112 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/app.js +1 -1
- package/cli/doctor/checks/app.js +77 -14
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/project.js +10 -2
- package/cli/setup/index.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/setup/steps/metro.js +46 -0
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +58 -12
- package/docs-md/containers/modals.md +8 -4
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +19 -2
- package/docs-md/containers/tabs.md +5 -1
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +4 -3
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +4 -4
- package/docs-md/help/troubleshooting.md +44 -6
- package/docs-md/index.json +57 -1
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +9 -3
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +46 -2
- package/docs-md/screen-ui/bar-buttons.md +5 -5
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +17 -12
- package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +21 -3
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +216 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +61 -13
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- 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 +10 -8
- package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
- package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
- package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
- package/ios/CLAUDE.md +60 -24
- package/ios/CORE.md +44 -34
- package/ios/Contract/ANNCommandAnswer.h +13 -5
- package/ios/Contract/ANNCommandAnswer.mm +40 -32
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Contract/ANNCommandRunner.h +10 -9
- package/ios/Contract/ANNCommandRunner.mm +22 -28
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNDeepLinkQueue.h +22 -2
- package/ios/Core/ANNDeepLinkQueue.mm +121 -14
- package/ios/Core/ANNErrorHandler.h +9 -4
- package/ios/Core/ANNErrorHandler.mm +20 -2
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +6 -2
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.h +6 -0
- package/ios/Core/ANNHinge.mm +15 -4
- package/ios/Core/ANNJavaScriptCalls.h +33 -0
- package/ios/Core/ANNJavaScriptCalls.mm +52 -0
- package/ios/Core/ANNLayoutProtocol.h +5 -5
- 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/ANNParking.h +5 -4
- package/ios/Core/ANNReloadCoordinator.mm +5 -12
- package/ios/Core/ANNRenderWait.h +20 -6
- package/ios/Core/ANNRenderWait.mm +143 -56
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Core/ANNUtils.h +7 -0
- package/ios/Core/ANNUtils.mm +10 -0
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Core/ApexNavigation.mm +7 -6
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +10 -8
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
- 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/Overlay/ANNOverlayCoordinator.mm +2 -8
- package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
- package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Screen/ANNScreenSettling.h +32 -0
- package/ios/Layouts/Screen/ANNScreenSettling.mm +137 -0
- package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
- package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +184 -16
- package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- 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/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +25 -0
- package/ios/Options/ANNOptions.mm +157 -1
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNOptionsReader.mm +108 -0
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Options/ANNSideMenuOptions.h +2 -0
- package/ios/Options/ANNSideMenuOptions.mm +2 -0
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- 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/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/Options/Params/Image.h +15 -2
- package/ios/Options/Params/Image.mm +35 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +13 -6
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
- package/ios/React/ANNSurfaceHostingView.h +14 -0
- package/ios/React/ANNSurfaceHostingView.mm +144 -1
- 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/React/Slots/ANNSlotHost.mm +1 -3
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +14 -8
- package/ios/TopBar/ANNBarButtons.mm +397 -80
- 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 +50 -7
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +172 -21
- package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
- package/ios/TopBar/ANNTopBarPresenter.h +8 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +104 -33
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +40 -27
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +190 -66
- package/lib/module/api/app/createNavigation.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/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.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/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.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/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +193 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +51 -9
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +20 -11
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +49 -15
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- 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/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +61 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +77 -16
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +8 -3
- package/lib/typescript/api/app/createNavigation.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 +3 -3
- 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/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.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/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.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/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.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/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- 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/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +27 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +28 -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 +80 -22
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +34 -26
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +17 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +18 -11
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +3 -1
- package/src/ARCHITECTURE.md +40 -27
- package/src/adapters/NativeANNTurboModule.ts +12 -2
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +203 -69
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +10 -4
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/navigation.ts +4 -1
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +185 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +70 -13
- package/src/engine/layout.ts +27 -11
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +45 -19
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +74 -1
- package/src/engine/screens.tsx +76 -21
- package/src/interfaces/ComponentEvents.ts +4 -4
- package/src/interfaces/Events.ts +29 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +81 -22
- package/src/interfaces/options/bottomTabs.ts +21 -7
- package/src/interfaces/options/buttons.ts +23 -15
- package/src/interfaces/options/modals.ts +34 -26
- package/src/interfaces/options/sideMenu.ts +24 -6
- package/src/interfaces/options/stack.ts +17 -3
- package/src/interfaces/options/system.ts +18 -11
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +68 -38
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
- 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/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#import "ANNImageSource.h"
|
|
2
|
+
|
|
3
|
+
// The path of a `file:` source the app may write again: a file outside the app's bundle (documents, caches, a temporary
|
|
4
|
+
// folder); nil for any other source. The bundle's files and assets don't change while the app runs.
|
|
5
|
+
static NSString *ANNWritablePathOf(ANNImageSourceKind kind, NSURL *url) {
|
|
6
|
+
if (kind != ANNImageSourceKindFile || !url.isFileURL) {
|
|
7
|
+
return nil;
|
|
8
|
+
}
|
|
9
|
+
NSString *path = url.path;
|
|
10
|
+
NSString *bundle = NSBundle.mainBundle.bundlePath;
|
|
11
|
+
if (!path.isAbsolutePath || [path isEqualToString:bundle] ||
|
|
12
|
+
[path hasPrefix:[bundle stringByAppendingString:@"/"]]) {
|
|
13
|
+
return nil;
|
|
14
|
+
}
|
|
15
|
+
return path;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The modification date and size of the file at `path` (else of its `.png`, as the loader looks for it), or nil.
|
|
19
|
+
static NSString *ANNFileStampOf(NSString *path) {
|
|
20
|
+
NSFileManager *files = NSFileManager.defaultManager;
|
|
21
|
+
NSDictionary<NSFileAttributeKey, id> *attributes = [files attributesOfItemAtPath:path
|
|
22
|
+
error:nil];
|
|
23
|
+
if (!attributes && path.pathExtension.length == 0) {
|
|
24
|
+
attributes = [files attributesOfItemAtPath:[path stringByAppendingPathExtension:@"png"]
|
|
25
|
+
error:nil];
|
|
26
|
+
}
|
|
27
|
+
NSDate *modified = attributes.fileModificationDate;
|
|
28
|
+
if (!modified) {
|
|
29
|
+
return nil;
|
|
30
|
+
}
|
|
31
|
+
return [NSString stringWithFormat:@"%.6f|%llu", modified.timeIntervalSinceReferenceDate,
|
|
32
|
+
attributes.fileSize];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@implementation ANNImageSource {
|
|
36
|
+
// The key without the file's stamp, and the path whose stamp joins it.
|
|
37
|
+
NSString *_baseKey;
|
|
38
|
+
NSString *_writablePath;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
+ (instancetype)symbolNamed:(NSString *)name fallback:(ANNImageSource *)fallback {
|
|
42
|
+
ANNImageSource *source = [[self alloc] initWithKind:ANNImageSourceKindSymbol
|
|
43
|
+
url:nil
|
|
44
|
+
address:name
|
|
45
|
+
scale:0
|
|
46
|
+
width:0
|
|
47
|
+
height:0];
|
|
48
|
+
source->_symbolName = [name copy];
|
|
49
|
+
source->_fallback = fallback;
|
|
50
|
+
return source;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
+ (instancetype)sourceOfKind:(ANNImageSourceKind)kind
|
|
54
|
+
url:(NSURL *)url
|
|
55
|
+
address:(NSString *)address
|
|
56
|
+
scale:(CGFloat)scale
|
|
57
|
+
width:(CGFloat)width
|
|
58
|
+
height:(CGFloat)height {
|
|
59
|
+
return [[self alloc] initWithKind:kind
|
|
60
|
+
url:url
|
|
61
|
+
address:address
|
|
62
|
+
scale:scale
|
|
63
|
+
width:width
|
|
64
|
+
height:height];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (instancetype)initWithKind:(ANNImageSourceKind)kind
|
|
68
|
+
url:(NSURL *)url
|
|
69
|
+
address:(NSString *)address
|
|
70
|
+
scale:(CGFloat)scale
|
|
71
|
+
width:(CGFloat)width
|
|
72
|
+
height:(CGFloat)height {
|
|
73
|
+
if (self = [super init]) {
|
|
74
|
+
_kind = kind;
|
|
75
|
+
_url = url;
|
|
76
|
+
_address = [address copy];
|
|
77
|
+
_scale = scale;
|
|
78
|
+
_width = width;
|
|
79
|
+
_height = height;
|
|
80
|
+
_baseKey = [NSString stringWithFormat:@"%g|%g|%@", scale, width, _address];
|
|
81
|
+
_writablePath = ANNWritablePathOf(kind, url);
|
|
82
|
+
}
|
|
83
|
+
return self;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Read at each ask: a file the app writes again at the same path is another image.
|
|
87
|
+
- (NSString *)key {
|
|
88
|
+
NSString *stamp = _writablePath ? ANNFileStampOf(_writablePath) : nil;
|
|
89
|
+
return stamp ? [NSString stringWithFormat:@"%@|%@", _baseKey, stamp] : _baseKey;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- (NSString *)sourceKey {
|
|
93
|
+
NSString *own = [NSString stringWithFormat:@"%ld|%@", (long)_kind, _baseKey];
|
|
94
|
+
return _fallback ? [NSString stringWithFormat:@"%@|%@", own, _fallback.sourceKey] : own;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (BOOL)loadsLater {
|
|
98
|
+
return _kind == ANNImageSourceKindFile || _kind == ANNImageSourceKindData;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#import "ANNImageSource.h"
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
/// The images of the options, every one of them (bar buttons and their menus, the back button, tab icons, search
|
|
7
|
+
/// tokens and suggestions): one cache for the app, by the source's key (`NSCache`, its cost the image's bytes, let go
|
|
8
|
+
/// at a memory warning). The images don't depend on the JavaScript, so a reload keeps them.
|
|
9
|
+
///
|
|
10
|
+
/// Every decode runs on one serial queue of the loader: `imageOf:` waits for it, `loadLater:done:` doesn't. A failure
|
|
11
|
+
/// (a missing file, data that isn't an image or doesn't decode, an exception while decoding) never throws: no image,
|
|
12
|
+
/// and one warning per address in the log. It is not cached, as on Android: it answers for a moment (1 s, the reads of
|
|
13
|
+
/// one apply), then the next read tries again, so a file written later or Metro's server back shows.
|
|
14
|
+
@interface ANNOptionImages : NSObject
|
|
15
|
+
|
|
16
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
17
|
+
+ (instancetype)new NS_UNAVAILABLE;
|
|
18
|
+
|
|
19
|
+
/// The image of `source`: a symbol, what the cache has, or else loaded before this returns; nil on a failure. Any
|
|
20
|
+
/// thread.
|
|
21
|
+
+ (nullable UIImage *)imageOf:(ANNImageSource *)source;
|
|
22
|
+
|
|
23
|
+
/// The cache doesn't have the image of `source`, it didn't fail a moment ago, and it loads off the main queue
|
|
24
|
+
/// (`loadsLater` of the source): what `loadLater:done:` doesn't give at once. Any thread.
|
|
25
|
+
+ (BOOL)loadsLater:(ANNImageSource *)source;
|
|
26
|
+
|
|
27
|
+
/// Loads `source` on the loader's queue and gives `done` the image, or nil on a failure, on the main queue; one load
|
|
28
|
+
/// for every caller that asks for the same image meanwhile. What the cache has, or what doesn't load off the main
|
|
29
|
+
/// queue, comes at once, before this returns. Main queue.
|
|
30
|
+
+ (void)loadLater:(ANNImageSource *)source done:(void (^)(UIImage *_Nullable image))done;
|
|
31
|
+
|
|
32
|
+
/// Lets every image of the cache go. Any thread.
|
|
33
|
+
+ (void)clear;
|
|
34
|
+
|
|
35
|
+
@end
|
|
36
|
+
|
|
37
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#import "ANNOptionImages.h"
|
|
2
|
+
#import <React/RCTAssert.h>
|
|
3
|
+
#import <React/RCTLog.h>
|
|
4
|
+
#import <React/RCTUtils.h>
|
|
5
|
+
|
|
6
|
+
// The options' icons are small: 16 MB of decoded pixels holds hundreds of them.
|
|
7
|
+
static const NSUInteger ANNOptionImagesCostLimit = 16 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
// The longest piece of an address a warning names (a `data:` address is the whole image).
|
|
10
|
+
static const NSUInteger ANNOptionImagesLoggedAddressLength = 80;
|
|
11
|
+
|
|
12
|
+
// How long a failure answers for its source without a new try: the many reads of one apply ask once, a later apply
|
|
13
|
+
// tries again (a file written since, Metro's server back).
|
|
14
|
+
static const NSTimeInterval ANNOptionImagesFailureMoment = 1;
|
|
15
|
+
|
|
16
|
+
static const void *ANNOptionImagesQueueKey = &ANNOptionImagesQueueKey;
|
|
17
|
+
|
|
18
|
+
static NSCache<NSString *, UIImage *> *ANNCache;
|
|
19
|
+
static dispatch_queue_t ANNQueue;
|
|
20
|
+
// The addresses already named in a warning: one each.
|
|
21
|
+
static NSMutableSet<NSString *> *ANNWarned;
|
|
22
|
+
// The keys whose last try failed, with the system's uptime then; guarded by itself. Failures never go into the cache.
|
|
23
|
+
static NSMutableDictionary<NSString *, NSNumber *> *ANNFailures;
|
|
24
|
+
// The loads off the main queue under way, with who waits for each. Main queue.
|
|
25
|
+
static NSMutableDictionary<NSString *, NSMutableArray *> *ANNLoading;
|
|
26
|
+
|
|
27
|
+
static void ANNSetUp(void) {
|
|
28
|
+
static dispatch_once_t once;
|
|
29
|
+
dispatch_once(&once, ^{
|
|
30
|
+
ANNCache = [NSCache new];
|
|
31
|
+
ANNCache.name = @"ApexNavigation option images";
|
|
32
|
+
ANNCache.totalCostLimit = ANNOptionImagesCostLimit;
|
|
33
|
+
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
|
|
34
|
+
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
|
|
35
|
+
ANNQueue = dispatch_queue_create("dev.apexnative.navigation.images", attributes);
|
|
36
|
+
dispatch_queue_set_specific(ANNQueue, ANNOptionImagesQueueKey,
|
|
37
|
+
(void *)ANNOptionImagesQueueKey, NULL);
|
|
38
|
+
ANNWarned = [NSMutableSet new];
|
|
39
|
+
ANNFailures = [NSMutableDictionary new];
|
|
40
|
+
ANNLoading = [NSMutableDictionary new];
|
|
41
|
+
NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
|
|
42
|
+
[center addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
|
|
43
|
+
object:nil
|
|
44
|
+
queue:nil
|
|
45
|
+
usingBlock:^(NSNotification *notification) {
|
|
46
|
+
[ANNCache removeAllObjects];
|
|
47
|
+
}];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The bytes of the decoded image, the cost the cache weighs it with.
|
|
52
|
+
static NSUInteger ANNBytesOf(UIImage *image) {
|
|
53
|
+
CGImageRef cgImage = image.CGImage;
|
|
54
|
+
if (cgImage) {
|
|
55
|
+
return CGImageGetBytesPerRow(cgImage) * CGImageGetHeight(cgImage);
|
|
56
|
+
}
|
|
57
|
+
CGFloat width = image.size.width * image.scale;
|
|
58
|
+
CGFloat height = image.size.height * image.scale;
|
|
59
|
+
return (NSUInteger)MAX(1, width * height * 4);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static void ANNWarnOnce(ANNImageSource *source, NSString *why) {
|
|
63
|
+
@synchronized(ANNWarned) {
|
|
64
|
+
if ([ANNWarned containsObject:source.address]) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
[ANNWarned addObject:source.address];
|
|
68
|
+
}
|
|
69
|
+
NSString *address = source.address;
|
|
70
|
+
if (address.length > ANNOptionImagesLoggedAddressLength) {
|
|
71
|
+
address = [[address substringToIndex:ANNOptionImagesLoggedAddressLength]
|
|
72
|
+
stringByAppendingString:@"…"];
|
|
73
|
+
}
|
|
74
|
+
RCTLogWarn(@"[ApexNavigation] could not load the image %@: %@", address, why);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The last try of `key` failed a moment ago.
|
|
78
|
+
static BOOL ANNFailedJustNow(NSString *key) {
|
|
79
|
+
@synchronized(ANNFailures) {
|
|
80
|
+
NSNumber *at = ANNFailures[key];
|
|
81
|
+
if (!at) {
|
|
82
|
+
return NO;
|
|
83
|
+
}
|
|
84
|
+
NSTimeInterval since = NSProcessInfo.processInfo.systemUptime - at.doubleValue;
|
|
85
|
+
if (since < ANNOptionImagesFailureMoment) {
|
|
86
|
+
return YES;
|
|
87
|
+
}
|
|
88
|
+
[ANNFailures removeObjectForKey:key];
|
|
89
|
+
return NO;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static void ANNSetFailed(NSString *key, BOOL failed) {
|
|
94
|
+
@synchronized(ANNFailures) {
|
|
95
|
+
if (failed) {
|
|
96
|
+
ANNFailures[key] = @(NSProcessInfo.processInfo.systemUptime);
|
|
97
|
+
} else {
|
|
98
|
+
[ANNFailures removeObjectForKey:key];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// A file at the address: an image with nothing but its pixels, which can be decoded ahead. An asset of a catalog
|
|
104
|
+
// (found by name) may have a dark variant, which a decoded copy would lose.
|
|
105
|
+
static BOOL ANNIsFileAt(NSURL *url) {
|
|
106
|
+
NSString *path = url.path;
|
|
107
|
+
if (path.length == 0) {
|
|
108
|
+
return NO;
|
|
109
|
+
}
|
|
110
|
+
NSFileManager *files = NSFileManager.defaultManager;
|
|
111
|
+
if ([files fileExistsAtPath:path]) {
|
|
112
|
+
return YES;
|
|
113
|
+
}
|
|
114
|
+
return path.pathExtension.length == 0 &&
|
|
115
|
+
[files fileExistsAtPath:[path stringByAppendingPathExtension:@"png"]];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@implementation ANNOptionImages
|
|
119
|
+
|
|
120
|
+
+ (UIImage *)imageOf:(ANNImageSource *)source {
|
|
121
|
+
if (source.kind == ANNImageSourceKindSymbol) {
|
|
122
|
+
UIImage *symbol = [UIImage systemImageNamed:source.symbolName];
|
|
123
|
+
return symbol ?: (source.fallback ? [self imageOf:source.fallback] : nil);
|
|
124
|
+
}
|
|
125
|
+
ANNSetUp();
|
|
126
|
+
NSString *key = source.key;
|
|
127
|
+
UIImage *cached = [ANNCache objectForKey:key];
|
|
128
|
+
if (cached || ANNFailedJustNow(key)) {
|
|
129
|
+
return cached;
|
|
130
|
+
}
|
|
131
|
+
if (dispatch_get_specific(ANNOptionImagesQueueKey)) {
|
|
132
|
+
return [self loadOnQueue:source];
|
|
133
|
+
}
|
|
134
|
+
__block UIImage *image = nil;
|
|
135
|
+
dispatch_sync(ANNQueue, ^{
|
|
136
|
+
image = [self loadOnQueue:source];
|
|
137
|
+
});
|
|
138
|
+
return image;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// A failure of a moment ago answers at once, so an item built again when its load failed doesn't load again.
|
|
142
|
+
+ (BOOL)loadsLater:(ANNImageSource *)source {
|
|
143
|
+
if (!source.loadsLater) {
|
|
144
|
+
return NO;
|
|
145
|
+
}
|
|
146
|
+
ANNSetUp();
|
|
147
|
+
NSString *key = source.key;
|
|
148
|
+
return [ANNCache objectForKey:key] == nil && !ANNFailedJustNow(key);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
+ (void)loadLater:(ANNImageSource *)source done:(void (^)(UIImage *))done {
|
|
152
|
+
RCTAssertMainQueue();
|
|
153
|
+
if (![self loadsLater:source]) {
|
|
154
|
+
done([self imageOf:source]);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
NSString *key = source.key;
|
|
158
|
+
NSMutableArray *waiters = ANNLoading[key];
|
|
159
|
+
if (waiters) {
|
|
160
|
+
[waiters addObject:[done copy]];
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
ANNLoading[key] = [NSMutableArray arrayWithObject:[done copy]];
|
|
164
|
+
dispatch_async(ANNQueue, ^{
|
|
165
|
+
UIImage *image = [self loadOnQueue:source];
|
|
166
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
167
|
+
NSArray *waiting = ANNLoading[key];
|
|
168
|
+
[ANNLoading removeObjectForKey:key];
|
|
169
|
+
for (void (^waiter)(UIImage *) in waiting) {
|
|
170
|
+
waiter(image);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
+ (void)clear {
|
|
177
|
+
ANNSetUp();
|
|
178
|
+
[ANNCache removeAllObjects];
|
|
179
|
+
@synchronized(ANNFailures) {
|
|
180
|
+
[ANNFailures removeAllObjects];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#pragma mark - The loader's queue
|
|
185
|
+
|
|
186
|
+
// The cache again (a load that came first), else the decode. An image goes into the cache; a failure doesn't, as on
|
|
187
|
+
// Android: it answers only for a moment (`ANNFailedJustNow`), and the next apply after that tries again.
|
|
188
|
+
+ (UIImage *)loadOnQueue:(ANNImageSource *)source {
|
|
189
|
+
NSString *key = source.key;
|
|
190
|
+
UIImage *cached = [ANNCache objectForKey:key];
|
|
191
|
+
if (cached || ANNFailedJustNow(key)) {
|
|
192
|
+
return cached;
|
|
193
|
+
}
|
|
194
|
+
NSString *failure = nil;
|
|
195
|
+
UIImage *image = nil;
|
|
196
|
+
@try {
|
|
197
|
+
image = [self decode:source failure:&failure];
|
|
198
|
+
} @catch (NSException *exception) {
|
|
199
|
+
image = nil;
|
|
200
|
+
failure = [NSString
|
|
201
|
+
stringWithFormat:@"decoding it threw %@ (%@)", exception.name, exception.reason];
|
|
202
|
+
}
|
|
203
|
+
if (!image) {
|
|
204
|
+
ANNSetFailed(key, YES);
|
|
205
|
+
ANNWarnOnce(source, failure ?: @"it can't be decoded");
|
|
206
|
+
return nil;
|
|
207
|
+
}
|
|
208
|
+
ANNSetFailed(key, NO);
|
|
209
|
+
[ANNCache setObject:image forKey:key cost:ANNBytesOf(image)];
|
|
210
|
+
return image;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// The image of an address, as React Native's converter of images reads it, with the scale of the source; nil with
|
|
214
|
+
// what failed. A plain file and data are decoded here, so the first frame that shows them doesn't.
|
|
215
|
+
+ (UIImage *)decode:(ANNImageSource *)source failure:(NSString **)failure {
|
|
216
|
+
NSURL *url = source.url;
|
|
217
|
+
UIImage *image = nil;
|
|
218
|
+
BOOL decodesAhead = YES;
|
|
219
|
+
switch (source.kind) {
|
|
220
|
+
case ANNImageSourceKindFile:
|
|
221
|
+
image = RCTImageFromLocalAssetURL(url) ?: RCTImageFromLocalBundleAssetURL(url);
|
|
222
|
+
if (!image) {
|
|
223
|
+
*failure = @"there is no image at that address";
|
|
224
|
+
return nil;
|
|
225
|
+
}
|
|
226
|
+
decodesAhead = ANNIsFileAt(url);
|
|
227
|
+
break;
|
|
228
|
+
case ANNImageSourceKindData:
|
|
229
|
+
case ANNImageSourceKindMetro: {
|
|
230
|
+
// Metro's assets in development: read from the dev server, as before the cache.
|
|
231
|
+
NSError *error = nil;
|
|
232
|
+
NSData *data = url ? [NSData dataWithContentsOfURL:url options:0 error:&error] : nil;
|
|
233
|
+
if (data.length == 0) {
|
|
234
|
+
*failure = error.localizedDescription ?: @"it has no data";
|
|
235
|
+
return nil;
|
|
236
|
+
}
|
|
237
|
+
image = [UIImage imageWithData:data];
|
|
238
|
+
if (!image) {
|
|
239
|
+
*failure = @"its data is not an image";
|
|
240
|
+
return nil;
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
case ANNImageSourceKindSymbol:
|
|
245
|
+
return nil;
|
|
246
|
+
}
|
|
247
|
+
// An image UIKit can't prepare (16 bits a channel, CMYK) it still draws: it stays as it is then, decoded when it
|
|
248
|
+
// first shows.
|
|
249
|
+
UIImage *decoded = decodesAhead ? [image imageByPreparingForDisplay] : nil;
|
|
250
|
+
if (decoded) {
|
|
251
|
+
image = decoded.CGImage ? [UIImage imageWithCGImage:decoded.CGImage
|
|
252
|
+
scale:image.scale
|
|
253
|
+
orientation:image.imageOrientation]
|
|
254
|
+
: decoded;
|
|
255
|
+
}
|
|
256
|
+
if (!(image.size.width > 0) || !(image.size.height > 0)) {
|
|
257
|
+
*failure = @"it is empty";
|
|
258
|
+
return nil;
|
|
259
|
+
}
|
|
260
|
+
return [self image:image scaledBy:source];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// The scale of the source; without it the pixel width over `width`; without both, the file's own. A size that
|
|
264
|
+
// doesn't match is only logged.
|
|
265
|
+
+ (UIImage *)image:(UIImage *)image scaledBy:(ANNImageSource *)source {
|
|
266
|
+
CGFloat scale = source.scale;
|
|
267
|
+
CGFloat width = source.width;
|
|
268
|
+
CGFloat height = source.height;
|
|
269
|
+
if (scale <= 0 && width > 0 && image.CGImage) {
|
|
270
|
+
scale = CGImageGetWidth(image.CGImage) / width;
|
|
271
|
+
}
|
|
272
|
+
if (scale > 0 && image.CGImage && scale != image.scale) {
|
|
273
|
+
image = [UIImage imageWithCGImage:image.CGImage
|
|
274
|
+
scale:scale
|
|
275
|
+
orientation:image.imageOrientation];
|
|
276
|
+
}
|
|
277
|
+
if (width > 0 && height > 0 && !CGSizeEqualToSize(image.size, CGSizeMake(width, height))) {
|
|
278
|
+
RCTLogInfo(@"[ApexNavigation] an image of the options is %@, its source says %@",
|
|
279
|
+
NSStringFromCGSize(image.size), NSStringFromCGSize(CGSizeMake(width, height)));
|
|
280
|
+
}
|
|
281
|
+
return image;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@end
|
|
@@ -8,6 +8,14 @@ BOOL ANNJSONIsBoolean(id _Nullable value);
|
|
|
8
8
|
/// A JSON number that is finite: never a boolean, `NaN` or an infinity.
|
|
9
9
|
BOOL ANNJSONIsNumber(id _Nullable value);
|
|
10
10
|
|
|
11
|
+
/// Two values as the options keep them are the same: a boolean is never the same as a number, arrays and objects
|
|
12
|
+
/// compare item by item, anything else (a string, a number, `null`, a color) by `isEqual:`. nil is the same only as
|
|
13
|
+
/// nil.
|
|
14
|
+
BOOL ANNJSONValuesSame(id _Nullable a, id _Nullable b);
|
|
15
|
+
|
|
16
|
+
/// A hash of a value `ANNJSONValuesSame` compares: values that are the same have the same hash.
|
|
17
|
+
NSUInteger ANNJSONValueHash(id _Nullable value);
|
|
18
|
+
|
|
11
19
|
/// One value of the options as the JSON gave it. Immutable: trees of options may share it. `null` is a value too where
|
|
12
20
|
/// the kind has an explicit "none" (a text, a color): it takes the place of what the layers under it gave.
|
|
13
21
|
@interface ANNParam : NSObject
|
|
@@ -23,6 +31,13 @@ BOOL ANNJSONIsNumber(id _Nullable value);
|
|
|
23
31
|
/// The options gave `null`.
|
|
24
32
|
- (BOOL)isNull;
|
|
25
33
|
|
|
34
|
+
/// A param of the same class with the same value: two reads of the same JSON are the same, two params without a value
|
|
35
|
+
/// are the same. `==` and `isEqual:` stay identity.
|
|
36
|
+
- (BOOL)isSameValueAs:(nullable ANNParam *)other;
|
|
37
|
+
|
|
38
|
+
/// A hash consistent with `isSameValueAs:`.
|
|
39
|
+
- (NSUInteger)valueHash;
|
|
40
|
+
|
|
26
41
|
@end
|
|
27
42
|
|
|
28
43
|
NS_ASSUME_NONNULL_END
|
|
@@ -9,6 +9,56 @@ BOOL ANNJSONIsNumber(id value) {
|
|
|
9
9
|
return [value isKindOfClass:NSNumber.class] && !ANNJSONIsBoolean(value) && isfinite([value doubleValue]);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
BOOL ANNJSONValuesSame(id a, id b) {
|
|
13
|
+
if (a == b) {
|
|
14
|
+
return YES;
|
|
15
|
+
}
|
|
16
|
+
if (!a || !b) {
|
|
17
|
+
return NO;
|
|
18
|
+
}
|
|
19
|
+
if ([a isKindOfClass:NSArray.class]) {
|
|
20
|
+
if (![b isKindOfClass:NSArray.class] || [a count] != [b count]) {
|
|
21
|
+
return NO;
|
|
22
|
+
}
|
|
23
|
+
NSArray *items = b;
|
|
24
|
+
__block BOOL same = YES;
|
|
25
|
+
[(NSArray *)a enumerateObjectsUsingBlock:^(id item, NSUInteger index, BOOL *stop) {
|
|
26
|
+
same = ANNJSONValuesSame(item, items[index]);
|
|
27
|
+
*stop = !same;
|
|
28
|
+
}];
|
|
29
|
+
return same;
|
|
30
|
+
}
|
|
31
|
+
if ([a isKindOfClass:NSDictionary.class]) {
|
|
32
|
+
if (![b isKindOfClass:NSDictionary.class] || [a count] != [b count]) {
|
|
33
|
+
return NO;
|
|
34
|
+
}
|
|
35
|
+
NSDictionary *fields = b;
|
|
36
|
+
__block BOOL same = YES;
|
|
37
|
+
[(NSDictionary *)a enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
|
|
38
|
+
same = ANNJSONValuesSame(value, fields[key]);
|
|
39
|
+
*stop = !same;
|
|
40
|
+
}];
|
|
41
|
+
return same;
|
|
42
|
+
}
|
|
43
|
+
// `isEqual:` of NSNumber takes `true` for 1: a badge of `true` (a dot) and of `1` (a number) are not the same.
|
|
44
|
+
if (ANNJSONIsBoolean(a) != ANNJSONIsBoolean(b)) {
|
|
45
|
+
return NO;
|
|
46
|
+
}
|
|
47
|
+
return [a isEqual:b];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// An object's hash is its count only (Foundation's); an array mixes its items in, so lists of other values differ.
|
|
51
|
+
NSUInteger ANNJSONValueHash(id value) {
|
|
52
|
+
if ([value isKindOfClass:NSArray.class]) {
|
|
53
|
+
NSUInteger hash = [value count];
|
|
54
|
+
for (id item in value) {
|
|
55
|
+
hash = hash * 31 + ANNJSONValueHash(item);
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
return [value hash];
|
|
60
|
+
}
|
|
61
|
+
|
|
12
62
|
@implementation ANNParam
|
|
13
63
|
|
|
14
64
|
- (instancetype)initWithValue:(id)value {
|
|
@@ -26,4 +76,15 @@ BOOL ANNJSONIsNumber(id value) {
|
|
|
26
76
|
return _value == NSNull.null;
|
|
27
77
|
}
|
|
28
78
|
|
|
79
|
+
- (BOOL)isSameValueAs:(ANNParam *)other {
|
|
80
|
+
if (other == self) {
|
|
81
|
+
return YES;
|
|
82
|
+
}
|
|
83
|
+
return other.class == self.class && ANNJSONValuesSame(_value, other.value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
- (NSUInteger)valueHash {
|
|
87
|
+
return ANNJSONValueHash(_value);
|
|
88
|
+
}
|
|
89
|
+
|
|
29
90
|
@end
|
|
@@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
38
38
|
/// A pair that has only one of its sides.
|
|
39
39
|
- (BOOL)isPartial;
|
|
40
40
|
|
|
41
|
+
/// The same color: a pair by its sides (its dynamic color is made anew at each read), anything else by its value.
|
|
42
|
+
- (BOOL)isSameValueAs:(nullable ANNParam *)other;
|
|
43
|
+
|
|
41
44
|
@end
|
|
42
45
|
|
|
43
46
|
NS_ASSUME_NONNULL_END
|
|
@@ -40,6 +40,27 @@
|
|
|
40
40
|
return [super get];
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
- (BOOL)isSameValueAs:(ANNParam *)other {
|
|
44
|
+
if (other == self) {
|
|
45
|
+
return YES;
|
|
46
|
+
}
|
|
47
|
+
if (other.class != self.class) {
|
|
48
|
+
return NO;
|
|
49
|
+
}
|
|
50
|
+
Color *color = (Color *)other;
|
|
51
|
+
if (_light || _dark || color.light || color.dark) {
|
|
52
|
+
return ANNJSONValuesSame(_light, color.light) && ANNJSONValuesSame(_dark, color.dark);
|
|
53
|
+
}
|
|
54
|
+
return [super isSameValueAs:other];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
- (NSUInteger)valueHash {
|
|
58
|
+
if (_light || _dark) {
|
|
59
|
+
return _light.hash * 31 + _dark.hash;
|
|
60
|
+
}
|
|
61
|
+
return [super valueHash];
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
- (UIColor *)withDefault:(UIColor *)defaultValue {
|
|
44
65
|
if (!self.isPartial) {
|
|
45
66
|
return self.hasValue ? self.get : defaultValue;
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
4
|
|
|
5
|
-
/// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{
|
|
6
|
-
/// } }` with a number or a platform color on each side,
|
|
7
|
-
/// color". A value it can't read is no value and a warning
|
|
5
|
+
/// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{ light, dark }` (or an
|
|
6
|
+
/// app's `DynamicColorIOS`, `{ dynamic: { light, dark } }`) with a number or a platform color on each side,
|
|
7
|
+
/// `{ semantic: [...] }`, and `'NoColor'` (or `null`) for "no color". A value it can't read is no value and a warning
|
|
8
|
+
/// in development.
|
|
8
9
|
@interface ColorParser : NSObject
|
|
9
10
|
|
|
10
11
|
+ (Color *)parse:(nullable NSDictionary *)json key:(NSString *)key;
|
|
@@ -49,6 +49,24 @@ static UIColor *ANNOneColor(id value) {
|
|
|
49
49
|
return nil;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// A pair comes as `{ light, dark }`, either side may be left out; an app's own `DynamicColorIOS` as
|
|
53
|
+
// `{ dynamic: { light, dark } }`. Anything else is not a pair.
|
|
54
|
+
static NSDictionary *ANNColorPair(id value) {
|
|
55
|
+
if (![value isKindOfClass:NSDictionary.class]) {
|
|
56
|
+
return nil;
|
|
57
|
+
}
|
|
58
|
+
id dynamic = value[@"dynamic"];
|
|
59
|
+
if (dynamic) {
|
|
60
|
+
return [dynamic isKindOfClass:NSDictionary.class] ? dynamic : nil;
|
|
61
|
+
}
|
|
62
|
+
for (id side in value) {
|
|
63
|
+
if (![side isEqual:@"light"] && ![side isEqual:@"dark"]) {
|
|
64
|
+
return nil;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
@implementation ColorParser
|
|
53
71
|
|
|
54
72
|
+ (Color *)parse:(NSDictionary *)json key:(NSString *)key {
|
|
@@ -64,8 +82,8 @@ static UIColor *ANNOneColor(id value) {
|
|
|
64
82
|
return [[Color alloc] initWithColor:color];
|
|
65
83
|
}
|
|
66
84
|
NSString *path = [ANNOptionsReader pathOfKey:key];
|
|
67
|
-
NSDictionary *pair =
|
|
68
|
-
if (
|
|
85
|
+
NSDictionary *pair = ANNColorPair(value);
|
|
86
|
+
if (pair) {
|
|
69
87
|
UIColor *light = [self side:pair[@"light"] name:@"light" path:path];
|
|
70
88
|
UIColor *dark = [self side:pair[@"dark"] name:@"dark" path:path];
|
|
71
89
|
if (light || dark) {
|
|
@@ -77,8 +95,8 @@ static UIColor *ANNOneColor(id value) {
|
|
|
77
95
|
return [[Color alloc] initWithValue:nil];
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
// A side of `{
|
|
81
|
-
//
|
|
98
|
+
// A side of `{ light, dark }`. A side that is not there is a partial pair and is silent (the sides are optional); a
|
|
99
|
+
// side that is there but can't be read is missing too, with a warning.
|
|
82
100
|
+ (UIColor *)side:(id)value name:(NSString *)name path:(NSString *)path {
|
|
83
101
|
if (!value || value == NSNull.null) {
|
|
84
102
|
return nil;
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
+
#import "ANNImageSource.h"
|
|
1
2
|
#import "Param.h"
|
|
2
3
|
#import <UIKit/UIKit.h>
|
|
3
4
|
|
|
4
5
|
NS_ASSUME_NONNULL_BEGIN
|
|
5
6
|
|
|
6
|
-
/// An image of the options,
|
|
7
|
-
/// without it.
|
|
7
|
+
/// An image of the options: its source, read with the options, and the image, loaded when an element asks for it
|
|
8
|
+
/// through the shared cache (`ANNOptionImages`). A source that doesn't load is no image: the element shows without it.
|
|
9
|
+
/// A value that is a `UIImage` is that image, with no source: an element made again from an image that came already
|
|
10
|
+
/// (a tab's icon loaded off the main queue) doesn't ask the cache again.
|
|
8
11
|
@interface Image : Param
|
|
9
12
|
|
|
13
|
+
/// The image, loaded at once when the cache doesn't have it; nil without a source and when it doesn't load.
|
|
10
14
|
- (nullable UIImage *)get;
|
|
15
|
+
/// The image, or `defaultValue` without a source and when it doesn't load. `hasValue` says only that the options name
|
|
16
|
+
/// an image: a choice between an image and what shows without one reads the image itself.
|
|
11
17
|
- (nullable UIImage *)withDefault:(nullable UIImage *)defaultValue;
|
|
12
18
|
|
|
19
|
+
/// What the options name; nil without a value.
|
|
20
|
+
@property(nonatomic, strong, readonly, nullable) ANNImageSource *source;
|
|
21
|
+
|
|
22
|
+
/// The same image the options name: a source by its `sourceKey` (what a file holds now isn't compared, so a file the
|
|
23
|
+
/// app writes again at the same path is the same value), an image that came already by identity.
|
|
24
|
+
- (BOOL)isSameValueAs:(nullable ANNParam *)other;
|
|
25
|
+
|
|
13
26
|
@end
|
|
14
27
|
|
|
15
28
|
NS_ASSUME_NONNULL_END
|