UNPKG

react-native-navigation

Version:

React Native Navigation - truly native navigation for iOS and Android

229 lines (194 loc) • 10 kB
import cloneDeepWith from 'lodash/cloneDeepWith'; import cloneDeep from 'lodash/cloneDeep'; import map from 'lodash/map'; import { CommandsObserver } from '../events/CommandsObserver'; import { NativeCommandsSender } from '../adapters/NativeCommandsSender'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; import { Options } from '../interfaces/Options'; import { Layout, LayoutRoot } from '../interfaces/Layout'; import { LayoutTreeParser } from './LayoutTreeParser'; import { LayoutTreeCrawler } from './LayoutTreeCrawler'; import { OptionsProcessor } from './OptionsProcessor'; import { Store } from '../components/Store'; import { LayoutProcessor } from '../processors/LayoutProcessor'; import { CommandName } from '../interfaces/CommandName'; import { OptionsCrawler } from './OptionsCrawler'; export class Commands { constructor( private readonly store: Store, private readonly nativeCommandsSender: NativeCommandsSender, private readonly layoutTreeParser: LayoutTreeParser, private readonly layoutTreeCrawler: LayoutTreeCrawler, private readonly commandsObserver: CommandsObserver, private readonly uniqueIdProvider: UniqueIdProvider, private readonly optionsProcessor: OptionsProcessor, private readonly layoutProcessor: LayoutProcessor, private readonly optionsCrawler: OptionsCrawler ) {} public setRoot(simpleApi: LayoutRoot) { const input = cloneLayout(simpleApi); this.optionsCrawler.crawl(input.root); const processedRoot = this.layoutProcessor.process(input.root, CommandName.SetRoot); const root = this.layoutTreeParser.parse(processedRoot); const modals = map(input.modals, (modal) => { this.optionsCrawler.crawl(modal); const processedModal = this.layoutProcessor.process(modal, CommandName.SetRoot); return this.layoutTreeParser.parse(processedModal); }); const overlays = map(input.overlays, (overlay: any) => { this.optionsCrawler.crawl(overlay); const processedOverlay = this.layoutProcessor.process(overlay, CommandName.SetRoot); return this.layoutTreeParser.parse(processedOverlay); }); const commandId = this.uniqueIdProvider.generate(CommandName.SetRoot); this.commandsObserver.notify(CommandName.SetRoot, { commandId, layout: { root, modals, overlays }, }); this.layoutTreeCrawler.crawl(root, CommandName.SetRoot); modals.forEach((modalLayout) => { this.layoutTreeCrawler.crawl(modalLayout, CommandName.SetRoot); }); overlays.forEach((overlayLayout) => { this.layoutTreeCrawler.crawl(overlayLayout, CommandName.SetRoot); }); const result = this.nativeCommandsSender.setRoot(commandId, { root, modals, overlays }); return result; } public setDefaultOptions(options: Options) { const input = cloneDeep(options); this.optionsProcessor.processDefaultOptions(input, CommandName.SetDefaultOptions); this.nativeCommandsSender.setDefaultOptions(input); this.commandsObserver.notify(CommandName.SetDefaultOptions, { options }); } public mergeOptions(componentId: string, options: Options) { const input = cloneDeep(options); const component = this.store.getComponentInstance(componentId); const componentProps = this.store.getPropsForId(componentId) || undefined; this.optionsProcessor.processOptions(CommandName.MergeOptions, input, componentProps); if (component && !component.isMounted) console.warn( `Navigation.mergeOptions was invoked on component with id: ${componentId} before it is mounted, this can cause UI issues and should be avoided.\n Use static options instead.` ); this.nativeCommandsSender.mergeOptions(componentId, input); this.commandsObserver.notify(CommandName.MergeOptions, { componentId, options }); } public updateProps(componentId: string, props: object, callback?: () => void) { this.store.updateProps(componentId, props, callback); this.commandsObserver.notify(CommandName.UpdateProps, { componentId, props }); } public showModal<P>(layout: Layout<P>) { const layoutCloned = cloneLayout(layout); this.optionsCrawler.crawl(layoutCloned); const layoutProcessed = this.layoutProcessor.process(layoutCloned, CommandName.ShowModal); const layoutNode = this.layoutTreeParser.parse(layoutProcessed); const commandId = this.uniqueIdProvider.generate(CommandName.ShowModal); this.commandsObserver.notify(CommandName.ShowModal, { commandId, layout: layoutNode }); this.layoutTreeCrawler.crawl(layoutNode, CommandName.ShowModal); const result = this.nativeCommandsSender.showModal(commandId, layoutNode); return result; } public dismissModal(componentId: string, mergeOptions?: Options) { const commandId = this.uniqueIdProvider.generate(CommandName.DismissModal); this.optionsProcessor.processOptions(CommandName.DismissModal, mergeOptions); const result = this.nativeCommandsSender.dismissModal(commandId, componentId, mergeOptions); this.commandsObserver.notify(CommandName.DismissModal, { commandId, componentId, mergeOptions, }); return result; } public dismissAllModals(mergeOptions?: Options) { const commandId = this.uniqueIdProvider.generate(CommandName.DismissAllModals); this.optionsProcessor.processOptions(CommandName.DismissAllModals, mergeOptions); const result = this.nativeCommandsSender.dismissAllModals(commandId, mergeOptions); this.commandsObserver.notify(CommandName.DismissAllModals, { commandId, mergeOptions }); return result; } public push<P>(componentId: string, simpleApi: Layout<P>) { const input = cloneLayout(simpleApi); this.optionsCrawler.crawl(input); const layoutProcessed = this.layoutProcessor.process(input, CommandName.Push); const layout = this.layoutTreeParser.parse(layoutProcessed); const commandId = this.uniqueIdProvider.generate(CommandName.Push); this.commandsObserver.notify(CommandName.Push, { commandId, componentId, layout }); this.layoutTreeCrawler.crawl(layout, CommandName.Push); const result = this.nativeCommandsSender.push(commandId, componentId, layout); return result; } public pop(componentId: string, mergeOptions?: Options) { const commandId = this.uniqueIdProvider.generate(CommandName.Pop); this.optionsProcessor.processOptions(CommandName.Pop, mergeOptions); const result = this.nativeCommandsSender.pop(commandId, componentId, mergeOptions); this.commandsObserver.notify(CommandName.Pop, { commandId, componentId, mergeOptions }); return result; } public popTo(componentId: string, mergeOptions?: Options) { const commandId = this.uniqueIdProvider.generate(CommandName.PopTo); this.optionsProcessor.processOptions(CommandName.PopTo, mergeOptions); const result = this.nativeCommandsSender.popTo(commandId, componentId, mergeOptions); this.commandsObserver.notify(CommandName.PopTo, { commandId, componentId, mergeOptions }); return result; } public popToRoot(componentId: string, mergeOptions?: Options) { const commandId = this.uniqueIdProvider.generate(CommandName.PopToRoot); this.optionsProcessor.processOptions(CommandName.PopToRoot, mergeOptions); const result = this.nativeCommandsSender.popToRoot(commandId, componentId, mergeOptions); this.commandsObserver.notify(CommandName.PopToRoot, { commandId, componentId, mergeOptions }); return result; } public setStackRoot<P>(componentId: string, children: Layout<P>[]) { const input = map(cloneLayout(children), (simpleApi) => { this.optionsCrawler.crawl(simpleApi); const layoutProcessed = this.layoutProcessor.process(simpleApi, CommandName.SetStackRoot); const layout = this.layoutTreeParser.parse(layoutProcessed); return layout; }); const commandId = this.uniqueIdProvider.generate(CommandName.SetStackRoot); this.commandsObserver.notify(CommandName.SetStackRoot, { commandId, componentId, layout: input, }); input.forEach((layoutNode) => { this.layoutTreeCrawler.crawl(layoutNode, CommandName.SetStackRoot); }); const result = this.nativeCommandsSender.setStackRoot(commandId, componentId, input); return result; } public showOverlay<P>(simpleApi: Layout<P>) { const input = cloneLayout(simpleApi); this.optionsCrawler.crawl(input); const layoutProcessed = this.layoutProcessor.process(input, CommandName.ShowOverlay); const layout = this.layoutTreeParser.parse(layoutProcessed); const commandId = this.uniqueIdProvider.generate(CommandName.ShowOverlay); this.commandsObserver.notify(CommandName.ShowOverlay, { commandId, layout }); this.layoutTreeCrawler.crawl(layout, CommandName.ShowOverlay); const result = this.nativeCommandsSender.showOverlay(commandId, layout); return result; } public dismissOverlay(componentId: string) { const commandId = this.uniqueIdProvider.generate(CommandName.DismissOverlay); const result = this.nativeCommandsSender.dismissOverlay(commandId, componentId); this.commandsObserver.notify(CommandName.DismissOverlay, { commandId, componentId }); return result; } public dismissAllOverlays() { const commandId = this.uniqueIdProvider.generate(CommandName.DismissAllOverlays); const result = this.nativeCommandsSender.dismissAllOverlays(commandId); this.commandsObserver.notify(CommandName.DismissAllOverlays, { commandId }); return result; } public getLaunchArgs() { const commandId = this.uniqueIdProvider.generate(CommandName.GetLaunchArgs); const result = this.nativeCommandsSender.getLaunchArgs(commandId); this.commandsObserver.notify(CommandName.GetLaunchArgs, { commandId }); return result; } } function cloneLayout<L>(layout: L): L { return cloneDeepWith(layout, (value, key) => { if (key === 'passProps' && typeof value === 'object' && value !== null) return { ...value }; }); }