diff --git a/client/ios/DivKit/DivBlockModelingContext.swift b/client/ios/DivKit/DivBlockModelingContext.swift index ec4935ff1..3cac00c48 100644 --- a/client/ios/DivKit/DivBlockModelingContext.swift +++ b/client/ios/DivKit/DivBlockModelingContext.swift @@ -13,8 +13,8 @@ import AppKit public struct DivBlockModelingContext { public let cardId: DivCardID - var cardLogId: String? - var parentDivStatePath: DivStatePath? + private(set) var cardLogId: String? + private(set) var parentDivStatePath: DivStatePath? let stateManager: DivStateManager public let blockStateStorage: DivBlockStateStorage let visibilityCounter: DivVisibilityCounting @@ -30,9 +30,9 @@ public struct DivBlockModelingContext { let debugParams: DebugParams let scheduler: Scheduling let playerFactory: PlayerFactory? - var childrenA11yDescription: String? + private(set) var childrenA11yDescription: String? private(set) weak var parentScrollView: ScrollView? - public internal(set) var errorsStorage: DivErrorsStorage + public private(set) var errorsStorage: DivErrorsStorage private let persistentValuesStorage: DivPersistentValuesStorage let tooltipViewFactory: DivTooltipViewFactory? public let variablesStorage: DivVariablesStorage @@ -40,20 +40,8 @@ public struct DivBlockModelingContext { private let variableValueProvider: (String) -> Any? private let functionsProvider: FunctionsProvider private let variableTracker: ExpressionResolver.VariableTracker - - public internal(set) var parentPath: UIElementPath { - didSet { - expressionResolver = makeExpressionResolver( - variableValueProvider: variableValueProvider, - functionsProvider: functionsProvider, - parentPath: parentPath, - errorsStorage: errorsStorage, - variableTracker: variableTracker - ) - } - } - - var sizeModifier: DivSizeModifier? + public private(set) var parentPath: UIElementPath + private(set) var sizeModifier: DivSizeModifier? public init( cardId: DivCardID, @@ -82,6 +70,85 @@ public struct DivBlockModelingContext { variableTracker: DivVariableTracker? = nil, persistentValuesStorage: DivPersistentValuesStorage? = nil, tooltipViewFactory: DivTooltipViewFactory? = nil + ) { + let variableTracker: ExpressionResolver.VariableTracker = { variables in + variableTracker?.onVariablesUsed(cardId: cardId, variables: variables) + } + var extensionsHandlersDictionary = [String: DivExtensionHandler]() + extensionHandlers.forEach { + let id = $0.id + if extensionsHandlersDictionary[id] != nil { + DivKitLogger.failure("Duplicate DivExtensionHandler for: \(id)") + return + } + extensionsHandlersDictionary[id] = $0 + } + var stateInterceptorsDictionary = [String: DivStateInterceptor]() + stateInterceptors.forEach { + let id = $0.id + if stateInterceptorsDictionary[id] != nil { + DivKitLogger.failure("Duplicate DivStateInterceptor for: \(id)") + return + } + stateInterceptorsDictionary[id] = $0 + } + self.init( + cardId: cardId, + cardLogId: cardLogId, + parentPath: parentPath, + parentDivStatePath: parentDivStatePath, + stateManager: stateManager, + blockStateStorage: blockStateStorage, + visibilityCounter: visibilityCounter, + lastVisibleBoundsCache: lastVisibleBoundsCache, + imageHolderFactory: imageHolderFactory, + highPriorityImageHolderFactory: highPriorityImageHolderFactory, + divCustomBlockFactory: divCustomBlockFactory, + fontProvider: fontProvider, + flagsInfo: flagsInfo, + extensionHandlers: extensionsHandlersDictionary, + stateInterceptors: stateInterceptorsDictionary, + variablesStorage: variablesStorage, + playerFactory: playerFactory, + debugParams: debugParams, + scheduler: scheduler, + childrenA11yDescription: childrenA11yDescription, + parentScrollView: parentScrollView, + errorsStorage: errorsStorage, + layoutDirection: layoutDirection, + variableTracker: variableTracker, + persistentValuesStorage: persistentValuesStorage, + tooltipViewFactory: tooltipViewFactory + ) + } + + init( + cardId: DivCardID, + cardLogId: String?, + parentPath: UIElementPath?, + parentDivStatePath: DivStatePath?, + stateManager: DivStateManager, + blockStateStorage: DivBlockStateStorage, + visibilityCounter: DivVisibilityCounting?, + lastVisibleBoundsCache: DivLastVisibleBoundsCache?, + imageHolderFactory: DivImageHolderFactory, + highPriorityImageHolderFactory: DivImageHolderFactory?, + divCustomBlockFactory: DivCustomBlockFactory?, + fontProvider: DivFontProvider?, + flagsInfo: DivFlagsInfo, + extensionHandlers: [String: DivExtensionHandler], + stateInterceptors: [String: DivStateInterceptor], + variablesStorage: DivVariablesStorage, + playerFactory: PlayerFactory?, + debugParams: DebugParams, + scheduler: Scheduling?, + childrenA11yDescription: String?, + parentScrollView: ScrollView?, + errorsStorage: DivErrorsStorage?, + layoutDirection: UserInterfaceLayoutDirection, + variableTracker: @escaping ExpressionResolver.VariableTracker, + persistentValuesStorage: DivPersistentValuesStorage?, + tooltipViewFactory: DivTooltipViewFactory? ) { self.cardId = cardId self.cardLogId = cardLogId @@ -104,36 +171,13 @@ public struct DivBlockModelingContext { self.parentScrollView = parentScrollView self.errorsStorage = errorsStorage ?? DivErrorsStorage(errors: []) self.layoutDirection = layoutDirection - let variableTracker: ExpressionResolver.VariableTracker = { variables in - variableTracker?.onVariablesUsed(cardId: cardId, variables: variables) - } self.variableTracker = variableTracker let persistentValuesStorage = persistentValuesStorage ?? DivPersistentValuesStorage() self.persistentValuesStorage = persistentValuesStorage self.tooltipViewFactory = tooltipViewFactory self.variablesStorage = variablesStorage - - var extensionsHandlersDictionary = [String: DivExtensionHandler]() - extensionHandlers.forEach { - let id = $0.id - if extensionsHandlersDictionary[id] != nil { - DivKitLogger.failure("Duplicate DivExtensionHandler for: \(id)") - return - } - extensionsHandlersDictionary[id] = $0 - } - self.extensionHandlers = extensionsHandlersDictionary - - var stateInterceptorsDictionary = [String: DivStateInterceptor]() - stateInterceptors.forEach { - let id = $0.id - if stateInterceptorsDictionary[id] != nil { - DivKitLogger.failure("Duplicate DivStateInterceptor for: \(id)") - return - } - stateInterceptorsDictionary[id] = $0 - } - self.stateInterceptors = stateInterceptorsDictionary + self.extensionHandlers = extensionHandlers + self.stateInterceptors = stateInterceptors let variableValueProvider: (String) -> Any? = { variablesStorage.getVariableValue( @@ -149,7 +193,7 @@ public struct DivBlockModelingContext { }, persistentValuesStorage: persistentValuesStorage ) - expressionResolver = makeExpressionResolver( + self.expressionResolver = makeExpressionResolver( variableValueProvider: variableValueProvider, functionsProvider: functionsProvider, parentPath: parentPath, @@ -211,6 +255,47 @@ public struct DivBlockModelingContext { } } +extension DivBlockModelingContext { + func modifying( + cardLogId: String? = nil, + parentPath: UIElementPath? = nil, + parentDivStatePath: DivStatePath? = nil, + errorsStorage: DivErrorsStorage? = nil, + sizeModifier: DivSizeModifier? = nil + ) -> Self { + let expressionResolver: ExpressionResolver + let parentPath = parentPath ?? self.parentPath + let errorsStorage = errorsStorage ?? self.errorsStorage + if parentPath == self.parentPath { + expressionResolver = self.expressionResolver + } else { + expressionResolver = makeExpressionResolver( + variableValueProvider: variableValueProvider, + functionsProvider: functionsProvider, + parentPath: parentPath, + errorsStorage: errorsStorage, + variableTracker: variableTracker + ) + } + return modified(self) { + $0.cardLogId = cardLogId ?? self.cardLogId + $0.parentPath = parentPath + $0.parentDivStatePath = parentDivStatePath ?? self.parentDivStatePath + $0.errorsStorage = errorsStorage + $0.sizeModifier = sizeModifier ?? self.sizeModifier + $0.expressionResolver = expressionResolver + } + } + + func modifying( + childrenA11yDescription: String? + ) -> Self { + modified(self) { + $0.childrenA11yDescription = childrenA11yDescription + } + } +} + private func makeExpressionResolver( variableValueProvider: @escaping AnyCalcExpression.ValueProvider, functionsProvider: FunctionsProvider, diff --git a/client/ios/DivKit/Extensions/ArrayExtensions.swift b/client/ios/DivKit/Extensions/ArrayExtensions.swift index 36c349f06..21529e0e7 100644 --- a/client/ios/DivKit/Extensions/ArrayExtensions.swift +++ b/client/ios/DivKit/Extensions/ArrayExtensions.swift @@ -8,10 +8,10 @@ extension Array where Element == Div { mappedBy modificator: (Div, Block) throws -> T ) rethrows -> [T] { try iterativeFlatMap { div, index in - let itemContext = modified(context) { - $0.parentPath += index - $0.sizeModifier = sizeModifier - } + let itemContext = context.modifying( + parentPath: context.parentPath + index, + sizeModifier: sizeModifier + ) let block: Block do { block = try modifyError({ diff --git a/client/ios/DivKit/Extensions/DivContainer/DivContainerExtensions.swift b/client/ios/DivKit/Extensions/DivContainer/DivContainerExtensions.swift index c5c5ab3f3..6e9b4b34e 100644 --- a/client/ios/DivKit/Extensions/DivContainer/DivContainerExtensions.swift +++ b/client/ios/DivKit/Extensions/DivContainer/DivContainerExtensions.swift @@ -7,9 +7,9 @@ extension DivContainer: DivBlockModeling { public func makeBlock(context: DivBlockModelingContext) throws -> Block { try applyBaseProperties( to: { try makeBaseBlock(context: context) }, - context: modified(context) { - $0.childrenA11yDescription = resolveChildrenA11yDescription($0) - }, + context: context.modifying( + childrenA11yDescription: resolveChildrenA11yDescription(context) + ), actionsHolder: self ) } @@ -17,11 +17,11 @@ extension DivContainer: DivBlockModeling { var nonNilItems: [Div] { items ?? [] } - + private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { - let childContext = modified(context) { - $0.parentPath = $0.parentPath + (id ?? DivContainer.type) - } + let childContext = context.modifying( + parentPath: context.parentPath + (id ?? DivContainer.type) + ) let expressionResolver = context.expressionResolver let orientation = resolveOrientation(expressionResolver) switch orientation { @@ -55,9 +55,9 @@ extension DivContainer: DivBlockModeling { vertical: resolveContentAlignmentVertical(expressionResolver).alignment ) - let childrenContext = modified(context) { - $0.errorsStorage = DivErrorsStorage(errors: []) - } + let childrenContext = context.modifying( + errorsStorage: DivErrorsStorage(errors: []) + ) let children = nonNilItems.makeBlocks( context: childrenContext, sizeModifier: DivContainerSizeModifier( @@ -121,9 +121,9 @@ extension DivContainer: DivBlockModeling { uiLayoutDirection: context.layoutDirection ) - let childrenContext = modified(context) { - $0.errorsStorage = DivErrorsStorage(errors: []) - } + let childrenContext = context.modifying( + errorsStorage: DivErrorsStorage(errors: []) + ) // Before block's making we need to filter items and remove // what has "matchParent" for opposite directions diff --git a/client/ios/DivKit/Extensions/DivData/DivDataExtensions.swift b/client/ios/DivKit/Extensions/DivData/DivDataExtensions.swift index 01e63e35f..34c46d21d 100644 --- a/client/ios/DivKit/Extensions/DivData/DivDataExtensions.swift +++ b/client/ios/DivKit/Extensions/DivData/DivDataExtensions.swift @@ -20,11 +20,12 @@ extension DivData: DivBlockModeling { let stateId = String(state.stateId) let statePath = DivStatePath(rawValue: UIElementPath(stateId)) let div = state.div - let divContext = modified(context) { - $0.cardLogId = $0.cardLogId ?? logId - $0.parentPath = $0.parentPath + stateId - $0.parentDivStatePath = statePath - } + + let divContext = context.modifying( + cardLogId: logId, + parentPath: context.parentPath + stateId, + parentDivStatePath: statePath + ) stateManager.updateBlockIdsWithStateChangeTransition( statePath: statePath, diff --git a/client/ios/DivKit/Extensions/DivGallery/DivGalleryExtensions.swift b/client/ios/DivKit/Extensions/DivGallery/DivGalleryExtensions.swift index 3d70f9e75..e1bab6e8b 100644 --- a/client/ios/DivKit/Extensions/DivGallery/DivGalleryExtensions.swift +++ b/client/ios/DivKit/Extensions/DivGallery/DivGalleryExtensions.swift @@ -17,9 +17,7 @@ extension DivGallery: DivBlockModeling, DivGalleryProtocol { private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { let galleryPath = context.parentPath + (id ?? DivGallery.type) let expressionResolver = context.expressionResolver - let galleryContext = modified(context) { - $0.parentPath = galleryPath - } + let galleryContext = context.modifying(parentPath: galleryPath) let defaultAlignment = resolveCrossContentAlignment(expressionResolver) .blockAlignment let itemSpacing = resolveItemSpacing(expressionResolver) diff --git a/client/ios/DivKit/Extensions/DivGallery/DivGalleryProtocol.swift b/client/ios/DivKit/Extensions/DivGallery/DivGalleryProtocol.swift index f798db598..201955931 100644 --- a/client/ios/DivKit/Extensions/DivGallery/DivGalleryProtocol.swift +++ b/client/ios/DivKit/Extensions/DivGallery/DivGalleryProtocol.swift @@ -21,9 +21,7 @@ extension DivGalleryProtocol { scrollbar: GalleryViewModel.Scrollbar = .none ) throws -> GalleryViewModel { let expressionResolver = context.expressionResolver - let childrenContext = modified(context) { - $0.errorsStorage = DivErrorsStorage(errors: []) - } + let childrenContext = context.modifying(errorsStorage: DivErrorsStorage(errors: [])) var children: [GalleryViewModel.Item] = items.makeBlocks( context: childrenContext, sizeModifier: DivGallerySizeModifier( diff --git a/client/ios/DivKit/Extensions/DivGridExtensions.swift b/client/ios/DivKit/Extensions/DivGridExtensions.swift index 28ba77440..e5f4dd216 100644 --- a/client/ios/DivKit/Extensions/DivGridExtensions.swift +++ b/client/ios/DivKit/Extensions/DivGridExtensions.swift @@ -12,16 +12,12 @@ extension DivGrid: DivBlockModeling { private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { let gridPath = context.parentPath + DivGrid.type - let gridContext = modified(context) { - $0.parentPath = gridPath - } - let gridItemsContext = modified(gridContext) { - $0.errorsStorage = DivErrorsStorage(errors: []) - } + let gridContext = context.modifying(parentPath: gridPath) + let gridItemsContext = gridContext.modifying(errorsStorage: DivErrorsStorage(errors: [])) let gridItems = items.enumerated().compactMap { tuple in - let itemContext = modified(gridItemsContext) { - $0.parentPath = $0.parentPath + tuple.offset - } + let itemContext = gridItemsContext.modifying( + parentPath: gridItemsContext.parentPath + tuple.offset + ) do { return try tuple.element.value.makeGridItem( context: itemContext diff --git a/client/ios/DivKit/Extensions/DivPagerExtensions.swift b/client/ios/DivKit/Extensions/DivPagerExtensions.swift index ae07df6a0..565b04602 100644 --- a/client/ios/DivKit/Extensions/DivPagerExtensions.swift +++ b/client/ios/DivKit/Extensions/DivPagerExtensions.swift @@ -17,9 +17,7 @@ extension DivPager: DivBlockModeling, DivGalleryProtocol { private func makeBaseBlock(context: DivBlockModelingContext) throws -> Block { let pagerPath = context.parentPath + (id ?? DivPager.type) let expressionResolver = context.expressionResolver - let itemContext = modified(context) { - $0.parentPath = pagerPath - } + let itemContext = context.modifying(parentPath: pagerPath) let pagerModelPath = id.map { PagerPath( cardId: context.cardId.rawValue, diff --git a/client/ios/DivKit/Extensions/DivStateExtensions.swift b/client/ios/DivKit/Extensions/DivStateExtensions.swift index a764d20cc..8875b8550 100644 --- a/client/ios/DivKit/Extensions/DivStateExtensions.swift +++ b/client/ios/DivKit/Extensions/DivStateExtensions.swift @@ -148,10 +148,10 @@ extension DivBlockModelingContext { id: String, stateId: String ) -> DivBlockModelingContext { - modified(self) { - $0.parentPath = parentPath + id + stateId - $0.parentDivStatePath = parentDivStatePath + id + stateId - } + modifying( + parentPath: parentPath + id + stateId, + parentDivStatePath: parentDivStatePath + id + stateId + ) } } diff --git a/client/ios/DivKit/Extensions/DivTabsExtensions.swift b/client/ios/DivKit/Extensions/DivTabsExtensions.swift index d497fe002..3f41a590f 100644 --- a/client/ios/DivKit/Extensions/DivTabsExtensions.swift +++ b/client/ios/DivKit/Extensions/DivTabsExtensions.swift @@ -21,12 +21,8 @@ extension DivTabs: DivBlockModeling { context: DivBlockModelingContext, tabsPath: UIElementPath ) throws -> Block { - let tabsContext = modified(context) { - $0.parentPath = tabsPath - } - let tabsItemContext = modified(tabsContext) { - $0.errorsStorage = DivErrorsStorage(errors: []) - } + let tabsContext = context.modifying(parentPath: tabsPath) + let tabsItemContext = tabsContext.modifying(errorsStorage: DivErrorsStorage(errors: [])) let tabs = items.iterativeFlatMap { item, index in do { return try item.makeTab(context: tabsItemContext, index: index) @@ -192,8 +188,8 @@ extension Typo { extension DivTabs.Item { fileprivate func makeTab(context: DivBlockModelingContext, index: Int) throws -> Tab { - let tabContext = modified(context) { $0.parentPath = $0.parentPath + index } - let pageContext = modified(tabContext) { $0.parentPath += "page" } + let tabContext = context.modifying(parentPath: context.parentPath + index) + let pageContext = tabContext.modifying(parentPath: tabContext.parentPath + "page") let title = makeTitle(context: tabContext) let page = try div.value .makeBlock(context: pageContext) @@ -202,9 +198,9 @@ extension DivTabs.Item { } private func makeTitle(context: DivBlockModelingContext) -> UILink { - let titleContext = modified(context) { - $0.parentPath += "title" - } + let titleContext = context.modifying( + parentPath: context.parentPath + "title" + ) let action = titleClickAction?.uiAction(context: context) return UILink( text: resolveTitle(titleContext.expressionResolver) ?? "", diff --git a/client/ios/DivKit/Extensions/DivTooltipExtensions.swift b/client/ios/DivKit/Extensions/DivTooltipExtensions.swift index e423517bc..0b81699e8 100644 --- a/client/ios/DivKit/Extensions/DivTooltipExtensions.swift +++ b/client/ios/DivKit/Extensions/DivTooltipExtensions.swift @@ -58,9 +58,9 @@ extension Optional where Wrapped == [DivTooltip] { context: DivBlockModelingContext ) throws -> [BlockTooltip] { try self?.iterativeFlatMap { div, index in - let tooltipContext = modified(context) { - $0.parentPath = $0.parentPath + "tooltip" + index - } + let tooltipContext = context.modifying( + parentPath: context.parentPath + "tooltip" + index + ) return try div.makeTooltip(context: tooltipContext) } ?? [] } diff --git a/client/ios/DivKit/Extensions/DivVideoExtensions.swift b/client/ios/DivKit/Extensions/DivVideoExtensions.swift index 88b5f65a8..2c88233da 100644 --- a/client/ios/DivKit/Extensions/DivVideoExtensions.swift +++ b/client/ios/DivKit/Extensions/DivVideoExtensions.swift @@ -43,9 +43,7 @@ extension DivVideo: DivBlockModeling { ) let videoPath = context.parentPath + (id ?? DivVideo.type) - let videoContext = modified(context) { - $0.parentPath = videoPath - } + let videoContext = context.modifying(parentPath: videoPath) let state: VideoBlockViewState = videoContext.blockStateStorage .getState(videoContext.parentPath) ?? .init(state: autostart == true ? .playing : .paused)