remove mutating properties from context.

This commit is contained in:
morevsavva
2023-12-07 19:57:59 +03:00
parent f91846d165
commit 7367606f8f
12 changed files with 175 additions and 105 deletions
+129 -44
View File
@@ -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,
@@ -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({
@@ -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
@@ -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,
@@ -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)
@@ -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(
@@ -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
@@ -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,
@@ -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
)
}
}
@@ -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) ?? "",
@@ -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)
} ?? []
}
@@ -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)