From 6dbdba132c18fa65afbb7d052abfee36a71ecebd Mon Sep 17 00:00:00 2001 From: robot-divkit Date: Mon, 3 Feb 2025 16:22:49 +0300 Subject: [PATCH] Release 31.1.0 commit_hash:4f420b84516ccd998b30b388011d3a6c46a8f62c --- .mapping.json | 13 ++ DivKit/Actions/ClearFocusActionHandler.swift | 2 +- DivKit/Actions/DivActionHandler.swift | 191 ++++++++++-------- DivKit/Actions/DivActionHandlingContext.swift | 10 +- DivKit/Actions/DivActionURLHandler.swift | 138 +------------ DivKit/Actions/DownloadActionHandler.swift | 62 ++++++ .../Actions/FocusElementActionHandler.swift | 6 +- DivKit/Actions/ScrollActionHandler.swift | 4 +- DivKit/Actions/SetStateActionHandler.swift | 2 +- DivKit/Actions/SubmitActionHandler.swift | 2 +- DivKit/Actions/TimerActionHandler.swift | 2 +- DivKit/Actions/VideoActionHandler.swift | 5 +- DivKit/Debug/DebugBlock.swift | 9 +- DivKit/DivKitComponents.swift | 3 +- DivKit/DivKitInfo.swift | 2 +- .../CustomFunctions/DivFunctionsStorage.swift | 5 + .../Extensions/DivAction/DivActionBase.swift | 16 ++ .../DivBase/DivBaseExtensions.swift | 23 ++- .../DivData/DivDataPatchExtensions.swift | 2 + DivKit/Extensions/DivPagerExtensions.swift | 5 +- DivKit/Extensions/DivShadowExtensions.swift | 19 ++ DivKit/Extensions/DivTextExtensions.swift | 26 ++- DivKit/Tooltips/DivTooltipViewFactory.swift | 1 + .../generated_sources/DivActionDownload.swift | 8 +- .../DivActionDownloadTemplate.swift | 14 +- DivKit/generated_sources/DivData.swift | 15 +- .../generated_sources/DivDataTemplate.swift | 22 ++ DivKit/generated_sources/DivPager.swift | 79 +++++--- .../generated_sources/DivPagerTemplate.swift | 32 ++- DivKit/generated_sources/DivTooltip.swift | 31 ++- DivKit/generated_sources/DivTooltipMode.swift | 41 ++++ .../DivTooltipModeModal.swift | 27 +++ .../DivTooltipModeModalTemplate.swift | 38 ++++ .../DivTooltipModeNonModal.swift | 27 +++ .../DivTooltipModeNonModalTemplate.swift | 38 ++++ .../DivTooltipModeTemplate.swift | 110 ++++++++++ .../DivTooltipTemplate.swift | 79 +++++++- LayoutKit/LayoutKit/Blocks/CornerRadii.swift | 4 + .../LayoutKit/Blocks/Grid/GridBlock.swift | 2 +- ...ecoratingBlock+UIViewRenderableBlock.swift | 1 + .../LayoutKit/UI/Views/GalleryView.swift | 8 +- .../ViewModels/PagerViewLayout.swift | 59 ++++-- Specs/DivKit/31.1.0/DivKit.podspec | 24 +++ .../31.1.0/DivKitExtensions.podspec | 22 ++ .../31.1.0/DivKit_LayoutKit.podspec | 24 +++ .../31.1.0/DivKit_LayoutKitInterface.podspec | 23 +++ .../31.1.0/DivKit_Serialization.podspec | 23 +++ 47 files changed, 953 insertions(+), 346 deletions(-) create mode 100644 DivKit/Actions/DownloadActionHandler.swift create mode 100644 DivKit/Extensions/DivShadowExtensions.swift create mode 100644 DivKit/generated_sources/DivTooltipMode.swift create mode 100644 DivKit/generated_sources/DivTooltipModeModal.swift create mode 100644 DivKit/generated_sources/DivTooltipModeModalTemplate.swift create mode 100644 DivKit/generated_sources/DivTooltipModeNonModal.swift create mode 100644 DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift create mode 100644 DivKit/generated_sources/DivTooltipModeTemplate.swift create mode 100644 Specs/DivKit/31.1.0/DivKit.podspec create mode 100644 Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec create mode 100644 Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec create mode 100644 Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec create mode 100644 Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec diff --git a/.mapping.json b/.mapping.json index e359c6c5..d3dd5b30 100644 --- a/.mapping.json +++ b/.mapping.json @@ -12,6 +12,7 @@ "DivKit/Actions/DivActionURLHandler.swift":"divkit/public-ios/DivKit/Actions/DivActionURLHandler.swift", "DivKit/Actions/DivUrlHandler.swift":"divkit/public-ios/DivKit/Actions/DivUrlHandler.swift", "DivKit/Actions/DivVideoAction.swift":"divkit/public-ios/DivKit/Actions/DivVideoAction.swift", + "DivKit/Actions/DownloadActionHandler.swift":"divkit/public-ios/DivKit/Actions/DownloadActionHandler.swift", "DivKit/Actions/FocusElementActionHandler.swift":"divkit/public-ios/DivKit/Actions/FocusElementActionHandler.swift", "DivKit/Actions/ScrollActionHandler.swift":"divkit/public-ios/DivKit/Actions/ScrollActionHandler.swift", "DivKit/Actions/ScrollMode.swift":"divkit/public-ios/DivKit/Actions/ScrollMode.swift", @@ -132,6 +133,7 @@ "DivKit/Extensions/DivPointExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivPointExtensions.swift", "DivKit/Extensions/DivSelectExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivSelectExtensions.swift", "DivKit/Extensions/DivSeparatorExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivSeparatorExtensions.swift", + "DivKit/Extensions/DivShadowExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivShadowExtensions.swift", "DivKit/Extensions/DivSizeExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivSizeExtensions.swift", "DivKit/Extensions/DivSizeUnitExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivSizeUnitExtensions.swift", "DivKit/Extensions/DivSliderExtensions.swift":"divkit/public-ios/DivKit/Extensions/DivSliderExtensions.swift", @@ -497,6 +499,12 @@ "DivKit/generated_sources/DivTimer.swift":"divkit/public-ios/DivKit/generated_sources/DivTimer.swift", "DivKit/generated_sources/DivTimerTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTimerTemplate.swift", "DivKit/generated_sources/DivTooltip.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltip.swift", + "DivKit/generated_sources/DivTooltipMode.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipMode.swift", + "DivKit/generated_sources/DivTooltipModeModal.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipModeModal.swift", + "DivKit/generated_sources/DivTooltipModeModalTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipModeModalTemplate.swift", + "DivKit/generated_sources/DivTooltipModeNonModal.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipModeNonModal.swift", + "DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift", + "DivKit/generated_sources/DivTooltipModeTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipModeTemplate.swift", "DivKit/generated_sources/DivTooltipTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTooltipTemplate.swift", "DivKit/generated_sources/DivTransform.swift":"divkit/public-ios/DivKit/generated_sources/DivTransform.swift", "DivKit/generated_sources/DivTransformTemplate.swift":"divkit/public-ios/DivKit/generated_sources/DivTransformTemplate.swift", @@ -1027,6 +1035,7 @@ "Specs/DivKit/30.8.0/DivKit.podspec":"divkit/public-ios/Specs/DivKit/30.8.0/DivKit.podspec", "Specs/DivKit/30.9.0/DivKit.podspec":"divkit/public-ios/Specs/DivKit/30.9.0/DivKit.podspec", "Specs/DivKit/31.0.0/DivKit.podspec":"divkit/public-ios/Specs/DivKit/31.0.0/DivKit.podspec", + "Specs/DivKit/31.1.0/DivKit.podspec":"divkit/public-ios/Specs/DivKit/31.1.0/DivKit.podspec", "Specs/DivKitExtensions/24.3.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/24.3.0/DivKitExtensions.podspec", "Specs/DivKitExtensions/25.0.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/25.0.0/DivKitExtensions.podspec", "Specs/DivKitExtensions/25.1.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/25.1.0/DivKitExtensions.podspec", @@ -1113,6 +1122,7 @@ "Specs/DivKitExtensions/30.8.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/30.8.0/DivKitExtensions.podspec", "Specs/DivKitExtensions/30.9.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/30.9.0/DivKitExtensions.podspec", "Specs/DivKitExtensions/31.0.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/31.0.0/DivKitExtensions.podspec", + "Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec":"divkit/public-ios/Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec", "Specs/DivKit_LayoutKit/28.0.1/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/28.0.1/DivKit_LayoutKit.podspec", "Specs/DivKit_LayoutKit/28.1.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/28.1.0/DivKit_LayoutKit.podspec", "Specs/DivKit_LayoutKit/28.10.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/28.10.0/DivKit_LayoutKit.podspec", @@ -1181,6 +1191,7 @@ "Specs/DivKit_LayoutKit/30.8.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/30.8.0/DivKit_LayoutKit.podspec", "Specs/DivKit_LayoutKit/30.9.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/30.9.0/DivKit_LayoutKit.podspec", "Specs/DivKit_LayoutKit/31.0.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/31.0.0/DivKit_LayoutKit.podspec", + "Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec":"divkit/public-ios/Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec", "Specs/DivKit_LayoutKitInterface/28.0.1/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/28.0.1/DivKit_LayoutKitInterface.podspec", "Specs/DivKit_LayoutKitInterface/28.1.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/28.1.0/DivKit_LayoutKitInterface.podspec", "Specs/DivKit_LayoutKitInterface/28.10.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/28.10.0/DivKit_LayoutKitInterface.podspec", @@ -1249,6 +1260,7 @@ "Specs/DivKit_LayoutKitInterface/30.8.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/30.8.0/DivKit_LayoutKitInterface.podspec", "Specs/DivKit_LayoutKitInterface/30.9.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/30.9.0/DivKit_LayoutKitInterface.podspec", "Specs/DivKit_LayoutKitInterface/31.0.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/31.0.0/DivKit_LayoutKitInterface.podspec", + "Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec":"divkit/public-ios/Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec", "Specs/DivKit_Serialization/28.0.1/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/28.0.1/DivKit_Serialization.podspec", "Specs/DivKit_Serialization/28.1.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/28.1.0/DivKit_Serialization.podspec", "Specs/DivKit_Serialization/28.10.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/28.10.0/DivKit_Serialization.podspec", @@ -1317,6 +1329,7 @@ "Specs/DivKit_Serialization/30.8.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/30.8.0/DivKit_Serialization.podspec", "Specs/DivKit_Serialization/30.9.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/30.9.0/DivKit_Serialization.podspec", "Specs/DivKit_Serialization/31.0.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/31.0.0/DivKit_Serialization.podspec", + "Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec":"divkit/public-ios/Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec", "Specs/LayoutKit/24.3.0/LayoutKit.podspec":"divkit/public-ios/Specs/LayoutKit/24.3.0/LayoutKit.podspec", "Specs/LayoutKit/25.0.0/LayoutKit.podspec":"divkit/public-ios/Specs/LayoutKit/25.0.0/LayoutKit.podspec", "Specs/LayoutKit/25.1.0/LayoutKit.podspec":"divkit/public-ios/Specs/LayoutKit/25.1.0/LayoutKit.podspec", diff --git a/DivKit/Actions/ClearFocusActionHandler.swift b/DivKit/Actions/ClearFocusActionHandler.swift index ca2fb0c0..6dba7e51 100644 --- a/DivKit/Actions/ClearFocusActionHandler.swift +++ b/DivKit/Actions/ClearFocusActionHandler.swift @@ -3,6 +3,6 @@ import Foundation final class ClearFocusActionHandler { func handle(context: DivActionHandlingContext) { context.blockStateStorage.clearFocus() - context.updateCard(.state(context.path.cardId)) + context.updateCard(.state(context.cardId)) } } diff --git a/DivKit/Actions/DivActionHandler.swift b/DivKit/Actions/DivActionHandler.swift index 6e73d485..036fb163 100644 --- a/DivKit/Actions/DivActionHandler.swift +++ b/DivKit/Actions/DivActionHandler.swift @@ -15,7 +15,6 @@ public final class DivActionHandler { typealias UpdateCardAction = (DivActionURLHandler.UpdateReason) -> Void - private let divActionURLHandler: DivActionURLHandler private let urlHandler: DivUrlHandler private let trackVisibility: TrackVisibility private let trackDisappear: TrackVisibility @@ -33,6 +32,7 @@ public final class DivActionHandler { private let clearFocusActionHandler = ClearFocusActionHandler() private let copyToClipboardActionHandler = CopyToClipboardActionHandler() private let dictSetValueActionHandler = DictSetValueActionHandler() + private let downloadActionHandler: DownloadActionHandler private let focusElementActionHandler = FocusElementActionHandler() private let scrollActionHandler: ScrollActionHandler private let setStateActionHandler: SetStateActionHandler @@ -103,33 +103,6 @@ public final class DivActionHandler { animatorController: DivAnimatorController, flags: DivFlagsInfo ) { - let scrollActionHandler = ScrollActionHandler( - blockStateStorage: blockStateStorage, - updateCard: updateCard - ) - self.scrollActionHandler = scrollActionHandler - - let setStateActionHandler = SetStateActionHandler(stateUpdater: stateUpdater) - self.setStateActionHandler = setStateActionHandler - - let timerActionHandler = TimerActionHandler(performer: performTimerAction) - self.timerActionHandler = timerActionHandler - - let tooltipActionHandler = TooltipActionHandler( - performer: tooltipActionPerformer, - showTooltip: showTooltip - ) - self.tooltipActionHandler = tooltipActionHandler - - self.divActionURLHandler = DivActionURLHandler( - patchProvider: patchProvider, - updateCard: updateCard, - persistentValuesStorage: persistentValuesStorage, - scrollActionHandler: scrollActionHandler, - setStateActionHandler: setStateActionHandler, - timerActionHandler: timerActionHandler, - tooltipActionHandler: tooltipActionHandler - ) self.urlHandler = urlHandler self.trackVisibility = trackVisibility self.trackDisappear = trackDisappear @@ -143,10 +116,24 @@ public final class DivActionHandler { self.flags = flags animatorActionHandler = AnimatorActionHandler(animatorController: animatorController) + downloadActionHandler = DownloadActionHandler( + patchProvider: patchProvider, + updateCard: updateCard + ) + scrollActionHandler = ScrollActionHandler( + blockStateStorage: blockStateStorage, + updateCard: updateCard + ) + setStateActionHandler = SetStateActionHandler(stateUpdater: stateUpdater) setStoredValueActionHandler = SetStoredValueActionHandler( persistentValuesStorage: persistentValuesStorage ) submitActionHandler = SubmitActionHandler(submitter: submitter) + timerActionHandler = TimerActionHandler(performer: performTimerAction) + tooltipActionHandler = TooltipActionHandler( + performer: tooltipActionPerformer, + showTooltip: showTooltip + ) } public func handle( @@ -204,8 +191,9 @@ public final class DivActionHandler { } else { path } + let info = action.resolveInfo(expressionResolver, path: path, source: source) let context = DivActionHandlingContext( - path: path, + info: info, expressionResolver: expressionResolver, variablesStorage: variablesStorage, blockStateStorage: blockStateStorage, @@ -213,7 +201,6 @@ public final class DivActionHandler { updateCard: updateCard ) - var isHandled = true switch action.typed { case let .divActionAnimatorStart(action): animatorActionHandler.handle(action, context: context) @@ -231,6 +218,8 @@ public final class DivActionHandler { copyToClipboardActionHandler.handle(action, context: context) case let .divActionDictSetValue(action): dictSetValueActionHandler.handle(action, context: context) + case let .divActionDownload(action): + downloadActionHandler.handle(action, context: context) case let .divActionFocusElement(action): focusElementActionHandler.handle(action, context: context) case let .divActionHideTooltip(action): @@ -253,88 +242,114 @@ public final class DivActionHandler { timerActionHandler.handle(action, context: context) case let .divActionVideo(action): videoActionHandler.handle(action, context: context) - case .divActionDownload: - break case .none: - isHandled = false - } - - let logId = action.resolveLogId(expressionResolver) ?? "" - let logUrl = action.resolveLogUrl(expressionResolver) - let referer = action.resolveReferer(expressionResolver) - - let divActionInfo = DivActionInfo( - path: path, - logId: logId, - url: action.resolveUrl(expressionResolver), - logUrl: logUrl, - referer: referer, - source: source, - payload: action.payload - ) - - if !isHandled { - handleUrl( - action, - info: divActionInfo, - context: context, - sender: sender - ) + handleUrl(action, context: context, sender: sender) } - reporter.reportAction( - cardId: cardId, - info: divActionInfo - ) + reporter.reportAction(cardId: cardId, info: info) if source == .visibility { - trackVisibility(logId, cardId) + trackVisibility(info.logId, cardId) } else if source == .disappear { - trackDisappear(logId, cardId) + trackDisappear(info.logId, cardId) } } private func handleUrl( _ action: DivActionBase, - info: DivActionInfo, context: DivActionHandlingContext, sender: AnyObject? ) { - guard let url = info.url else { + guard let url = context.info.url else { return } - let isDivActionURLHandled = divActionURLHandler.handleURL( - url, - info: info, - context: context, - completion: { [weak self] result in - guard let self else { - return - } - let callbackActions: [DivAction] = switch result { - case .success: - action.downloadCallbacks?.onSuccessActions ?? [] - case .failure: - action.downloadCallbacks?.onFailActions ?? [] - } - for action in callbackActions { - self.handle( - action, - path: info.path, - source: info.source, - sender: sender + if let intent = DivActionIntent(url: url) { + let cardId = context.cardId + switch intent { + case let .showTooltip(id, multiple): + let tooltipInfo = TooltipInfo(id: id, showsOnStart: false, multiple: multiple) + tooltipActionHandler.showTooltip(tooltipInfo) + case let .hideTooltip(id): + tooltipActionHandler.hideTooltip(id: id) + case let .download(patchUrl): + downloadActionHandler.handle( + url: patchUrl, + context: context, + onSuccessActions: action.downloadCallbacks?.onSuccessActions, + onFailActions: action.downloadCallbacks?.onFailActions + ) + case let .setState(divStatePath, lifetime): + setStateActionHandler.handle( + divStatePath: divStatePath, + lifetime: lifetime, + context: context + ) + case let .setVariable(name, value): + context.variablesStorage.update( + path: context.path, + name: DivVariableName(rawValue: name), + value: value + ) + case let .setCurrentItem(id, index): + scrollActionHandler.scrollToItem(cardId: cardId, id: id, index: index) + case let .setNextItem(id, step, overflow): + scrollActionHandler.scrollToNextItem( + cardId: cardId, + id: id, + step: step, + overflow: overflow + ) + case let .setPreviousItem(id, step, overflow): + scrollActionHandler.scrollToNextItem( + cardId: cardId, + id: id, + step: -step, + overflow: overflow + ) + case let .scroll(id, mode): + switch mode { + case .start: + scrollActionHandler.scrollToStart(cardId: cardId, id: id) + case .end: + scrollActionHandler.scrollToEnd(cardId: cardId, id: id) + case let .forward(offset, overflow): + scrollActionHandler.scrollToOffset( + cardId: cardId, + id: id, + offset: offset, + isRelative: true, + overflow: overflow + ) + case let .backward(offset, overflow): + scrollActionHandler.scrollToOffset( + cardId: cardId, + id: id, + offset: -offset, + isRelative: true, + overflow: overflow ) + case let .position(position): + scrollActionHandler.scrollToOffset(cardId: cardId, id: id, offset: position) } + case let .video(id: id, action: action): + context.blockStateStorage.setState( + id: id, + cardId: cardId, + state: VideoBlockViewState(state: action == .play ? .playing : .paused) + ) + context.updateCard(.state(cardId)) + case let .timer(timerId, action): + timerActionHandler.handle(cardId: cardId, timerId: timerId, action: action) + case let .setStoredValue(storedValue): + persistentValuesStorage.set(value: storedValue) } - ) - - if isDivActionURLHandled { return } + let info = context.info if !flags.useUrlHandlerForVisibilityActions, - (info.source == .visibility || info.source == .disappear) { + info.source == .visibility || info.source == .disappear { return } diff --git a/DivKit/Actions/DivActionHandlingContext.swift b/DivKit/Actions/DivActionHandlingContext.swift index 4e27fe41..3d3852ea 100644 --- a/DivKit/Actions/DivActionHandlingContext.swift +++ b/DivKit/Actions/DivActionHandlingContext.swift @@ -1,10 +1,18 @@ import LayoutKit struct DivActionHandlingContext { - let path: UIElementPath + let info: DivActionInfo let expressionResolver: ExpressionResolver let variablesStorage: DivVariablesStorage let blockStateStorage: DivBlockStateStorage let actionHandler: DivActionHandler let updateCard: DivActionHandler.UpdateCardAction + + var cardId: DivCardID { + info.cardId + } + + var path: UIElementPath { + info.path + } } diff --git a/DivKit/Actions/DivActionURLHandler.swift b/DivKit/Actions/DivActionURLHandler.swift index 68fabf0c..5cfa9c80 100644 --- a/DivKit/Actions/DivActionURLHandler.swift +++ b/DivKit/Actions/DivActionURLHandler.swift @@ -1,10 +1,8 @@ -import CoreGraphics import Foundation import LayoutKit import VGSL -/// Deprecated. Use `DivActionHandler`. -public final class DivActionURLHandler { +public enum DivActionURLHandler { @frozen public enum UpdateReason { case patch(DivCardID, DivPatch) @@ -13,138 +11,4 @@ public final class DivActionURLHandler { case variable([DivCardID: Set]) case external } - - private let patchProvider: DivPatchProvider - private let updateCard: DivActionHandler.UpdateCardAction - private let persistentValuesStorage: DivPersistentValuesStorage - private let setStateActionHandler: SetStateActionHandler - private let scrollActionHandler: ScrollActionHandler - private let timerActionHandler: TimerActionHandler - private let tooltipActionHandler: TooltipActionHandler - - init( - patchProvider: DivPatchProvider, - updateCard: @escaping DivActionHandler.UpdateCardAction, - persistentValuesStorage: DivPersistentValuesStorage, - scrollActionHandler: ScrollActionHandler, - setStateActionHandler: SetStateActionHandler, - timerActionHandler: TimerActionHandler, - tooltipActionHandler: TooltipActionHandler - ) { - self.patchProvider = patchProvider - self.updateCard = updateCard - self.persistentValuesStorage = persistentValuesStorage - self.setStateActionHandler = setStateActionHandler - self.scrollActionHandler = scrollActionHandler - self.timerActionHandler = timerActionHandler - self.tooltipActionHandler = tooltipActionHandler - } - - func handleURL( - _ url: URL, - info: DivActionInfo, - context: DivActionHandlingContext, - completion: @escaping (Result) -> Void = { _ in } - ) -> Bool { - guard let intent = DivActionIntent(url: url) else { - return false - } - - let cardId = info.path.cardId - switch intent { - case let .showTooltip(id, multiple): - let tooltipInfo = TooltipInfo(id: id, showsOnStart: false, multiple: multiple) - tooltipActionHandler.showTooltip(tooltipInfo) - case let .hideTooltip(id): - tooltipActionHandler.hideTooltip(id: id) - case let .download(patchUrl): - patchProvider.getPatch( - url: patchUrl, - info: info, - completion: { [unowned self] in - self.applyPatch(cardId: cardId, result: $0, completion: completion) - } - ) - case let .setState(divStatePath, lifetime): - setStateActionHandler.handle( - divStatePath: divStatePath, - lifetime: lifetime, - context: context - ) - case let .setVariable(name, value): - context.variablesStorage.update( - path: info.path, - name: DivVariableName(rawValue: name), - value: value - ) - case let .setCurrentItem(id, index): - scrollActionHandler.scrollToItem(cardId: cardId, id: id, index: index) - case let .setNextItem(id, step, overflow): - scrollActionHandler.scrollToNextItem( - cardId: cardId, - id: id, - step: step, - overflow: overflow - ) - case let .setPreviousItem(id, step, overflow): - scrollActionHandler.scrollToNextItem( - cardId: cardId, - id: id, - step: -step, - overflow: overflow - ) - case let .scroll(id, mode): - switch mode { - case .start: - scrollActionHandler.scrollToStart(cardId: cardId, id: id) - case .end: - scrollActionHandler.scrollToEnd(cardId: cardId, id: id) - case let .forward(offset, overflow): - scrollActionHandler.scrollToOffset( - cardId: cardId, - id: id, - offset: offset, - isRelative: true, - overflow: overflow - ) - case let .backward(offset, overflow): - scrollActionHandler.scrollToOffset( - cardId: cardId, - id: id, - offset: -offset, - isRelative: true, - overflow: overflow - ) - case let .position(position): - scrollActionHandler.scrollToOffset(cardId: cardId, id: id, offset: position) - } - case let .video(id: id, action: action): - context.blockStateStorage.setState( - id: id, - cardId: cardId, - state: VideoBlockViewState(state: action == .play ? .playing : .paused) - ) - context.updateCard(.state(cardId)) - case let .timer(timerId, action): - timerActionHandler.handle(cardId: cardId, timerId: timerId, action: action) - case let .setStoredValue(storedValue): - persistentValuesStorage.set(value: storedValue) - } - - return true - } - - private func applyPatch( - cardId: DivCardID, - result: Result, - completion: @escaping (Result) -> Void - ) { - switch result { - case let .success(patch): - updateCard(.patch(cardId, patch)) - completion(.success(())) - case let .failure(error): - completion(.failure(error)) - } - } } diff --git a/DivKit/Actions/DownloadActionHandler.swift b/DivKit/Actions/DownloadActionHandler.swift new file mode 100644 index 00000000..ea90821c --- /dev/null +++ b/DivKit/Actions/DownloadActionHandler.swift @@ -0,0 +1,62 @@ +import Foundation +import VGSL + +final class DownloadActionHandler { + private let patchProvider: DivPatchProvider + private let updateCard: DivActionHandler.UpdateCardAction + + init( + patchProvider: DivPatchProvider, + updateCard: @escaping DivActionHandler.UpdateCardAction + ) { + self.patchProvider = patchProvider + self.updateCard = updateCard + } + + func handle(_ action: DivActionDownload, context: DivActionHandlingContext) { + guard let url = action.resolveUrl(context.expressionResolver) else { + return + } + + handle( + url: url, + context: context, + onSuccessActions: action.onSuccessActions, + onFailActions: action.onFailActions + ) + } + + func handle( + url: URL, + context: DivActionHandlingContext, + onSuccessActions: [DivAction]?, + onFailActions: [DivAction]? + ) { + let info = context.info + patchProvider.getPatch( + url: url, + info: info, + completion: { [weak self] in + guard let self else { + return + } + let callbackActions: [DivAction] + switch $0 { + case let .success(patch): + updateCard(.patch(info.cardId, patch)) + callbackActions = onSuccessActions ?? [] + case .failure: + callbackActions = onFailActions ?? [] + } + for action in callbackActions { + context.actionHandler.handle( + action, + path: info.path, + source: .callback, + sender: nil + ) + } + } + ) + } +} diff --git a/DivKit/Actions/FocusElementActionHandler.swift b/DivKit/Actions/FocusElementActionHandler.swift index e6b9812b..ad39692d 100644 --- a/DivKit/Actions/FocusElementActionHandler.swift +++ b/DivKit/Actions/FocusElementActionHandler.swift @@ -5,18 +5,16 @@ final class FocusElementActionHandler { guard let elementId = action.resolveElementId(context.expressionResolver) else { return } - let cardId = context.path.cardId + let cardId = context.cardId if let previousCard = context.blockStateStorage.getFocusedElement()?.cardId, previousCard != cardId { context.updateCard(.state(previousCard)) } - let element = IdAndCardId(id: elementId, cardId: cardId) - context.blockStateStorage.setFocused( isFocused: true, - element: element + element: IdAndCardId(id: elementId, cardId: cardId) ) context.updateCard(.state(cardId)) } diff --git a/DivKit/Actions/ScrollActionHandler.swift b/DivKit/Actions/ScrollActionHandler.swift index 8f8bfec2..a4d0ad0a 100644 --- a/DivKit/Actions/ScrollActionHandler.swift +++ b/DivKit/Actions/ScrollActionHandler.swift @@ -26,7 +26,7 @@ final class ScrollActionHandler { } let itemCount = action.resolveItemCount(expressionResolver) - let cardId = context.path.cardId + let cardId = context.cardId if itemCount == 0 { let offset = action.resolveOffset(expressionResolver) scrollToOffset( @@ -47,7 +47,7 @@ final class ScrollActionHandler { return } - let cardId = context.path.cardId + let cardId = context.cardId switch action.destination { case let .indexDestination(destination): if let index = destination.resolveValue(expressionResolver) { diff --git a/DivKit/Actions/SetStateActionHandler.swift b/DivKit/Actions/SetStateActionHandler.swift index 90e66a7d..7138532d 100644 --- a/DivKit/Actions/SetStateActionHandler.swift +++ b/DivKit/Actions/SetStateActionHandler.swift @@ -26,7 +26,7 @@ final class SetStateActionHandler { lifetime: DivStateLifetime, context: DivActionHandlingContext ) { - let cardId = context.path.cardId + let cardId = context.cardId let fullStatePath: DivStatePath = if let tooltipId = context.path.tooltipId, divStatePath.isLocal { DivStatePath.makeDivStatePath( diff --git a/DivKit/Actions/SubmitActionHandler.swift b/DivKit/Actions/SubmitActionHandler.swift index 5e329395..24ddb430 100644 --- a/DivKit/Actions/SubmitActionHandler.swift +++ b/DivKit/Actions/SubmitActionHandler.swift @@ -20,7 +20,7 @@ final class SubmitActionHandler { } let containerData = context.variablesStorage.getVariables( - cardId: context.path.cardId, + cardId: context.cardId, elementId: containerId ).map( key: { $0.rawValue }, diff --git a/DivKit/Actions/TimerActionHandler.swift b/DivKit/Actions/TimerActionHandler.swift index 677f6573..a641eacc 100644 --- a/DivKit/Actions/TimerActionHandler.swift +++ b/DivKit/Actions/TimerActionHandler.swift @@ -19,7 +19,7 @@ final class TimerActionHandler { return } - handle(cardId: context.path.cardId, timerId: id, action: command.toDivTimerAction()) + handle(cardId: context.cardId, timerId: id, action: command.toDivTimerAction()) } func handle(cardId: DivCardID, timerId: String, action: DivTimerAction) { diff --git a/DivKit/Actions/VideoActionHandler.swift b/DivKit/Actions/VideoActionHandler.swift index 4e5a0495..dd85a17c 100644 --- a/DivKit/Actions/VideoActionHandler.swift +++ b/DivKit/Actions/VideoActionHandler.swift @@ -8,20 +8,17 @@ final class VideoActionHandler { context: DivActionHandlingContext ) { let expressionResolver = context.expressionResolver - guard let id = action.resolveId(expressionResolver), let command = action.resolveAction(expressionResolver) else { return } - let cardId = context.path.cardId - + let cardId = context.cardId context.blockStateStorage.setState( id: id, cardId: cardId, state: VideoBlockViewState(state: command == .start ? .playing : .paused) ) - context.updateCard(.state(cardId)) } } diff --git a/DivKit/Debug/DebugBlock.swift b/DivKit/Debug/DebugBlock.swift index 100667eb..4de50645 100644 --- a/DivKit/Debug/DebugBlock.swift +++ b/DivKit/Debug/DebugBlock.swift @@ -3,7 +3,6 @@ import LayoutKit import VGSL final class DebugBlock: WrapperBlock, LayoutCachingDefaultImpl { - let child: Block let errorCollector: DebugErrorCollector @@ -26,7 +25,7 @@ final class DebugBlock: WrapperBlock, LayoutCachingDefaultImpl { showDebugInfo: showDebugInfo ) } - + func equals(_ other: any LayoutKit.Block) -> Bool { if self === other { return true } guard let other = other as? DebugBlock else { return false } @@ -38,10 +37,6 @@ final class DebugBlock: WrapperBlock, LayoutCachingDefaultImpl { } func getImageHolders() -> [any VGSLUI.ImageHolder] { - [] - } - - func updated(withStates _: LayoutKit.BlocksState) throws -> Self { - self + child.getImageHolders() } } diff --git a/DivKit/DivKitComponents.swift b/DivKit/DivKitComponents.swift index ad540c87..4f282396 100644 --- a/DivKit/DivKitComponents.swift +++ b/DivKit/DivKitComponents.swift @@ -165,7 +165,7 @@ public final class DivKitComponents { reporter: reporter, idToPath: idToPath, animatorController: animatorController, - flags: .default + flags: flagsInfo ) triggersStorage = DivTriggersStorage( @@ -339,6 +339,7 @@ public final class DivKitComponents { public func setCardData(divData: DivData, cardId: DivCardID) { setTimers(divData: divData, cardId: cardId) setVariablesAndTriggers(divData: divData, cardId: cardId) + functionsStorage.set(cardId: cardId, functions: divData.functions ?? []) } public func setVariablesAndTriggers(divData: DivData, cardId: DivCardID) { diff --git a/DivKit/DivKitInfo.swift b/DivKit/DivKitInfo.swift index 25338c9c..9be30e06 100644 --- a/DivKit/DivKitInfo.swift +++ b/DivKit/DivKitInfo.swift @@ -1,3 +1,3 @@ public enum DivKitInfo { - public static let version = "31.0.0" + public static let version = "31.1.0" } diff --git a/DivKit/Expressions/CustomFunctions/DivFunctionsStorage.swift b/DivKit/Expressions/CustomFunctions/DivFunctionsStorage.swift index 70a2f062..c25fab8a 100644 --- a/DivKit/Expressions/CustomFunctions/DivFunctionsStorage.swift +++ b/DivKit/Expressions/CustomFunctions/DivFunctionsStorage.swift @@ -19,6 +19,11 @@ public final class DivFunctionsStorage { self.reporter = reporter } + func set(cardId: DivCardID, functions: [DivFunction]) { + reset(cardId: cardId) + setIfNeeded(path: cardId.path, functions: functions) + } + func setIfNeeded(path: UIElementPath, functions: [DivFunction]) { lock.withLock { if storages[path] != nil { diff --git a/DivKit/Extensions/DivAction/DivActionBase.swift b/DivKit/Extensions/DivAction/DivActionBase.swift index 9cc6a1c6..ae1068ff 100644 --- a/DivKit/Extensions/DivAction/DivActionBase.swift +++ b/DivKit/Extensions/DivAction/DivActionBase.swift @@ -17,6 +17,22 @@ public protocol DivActionBase: Serializable { } extension DivActionBase { + func resolveInfo( + _ expressionResolver: ExpressionResolver, + path: UIElementPath, + source: UserInterfaceAction.DivActionSource + ) -> DivActionInfo { + DivActionInfo( + path: path, + logId: resolveLogId(expressionResolver) ?? "", + url: resolveUrl(expressionResolver), + logUrl: resolveLogUrl(expressionResolver), + referer: resolveReferer(expressionResolver), + source: source, + payload: payload + ) + } + func makeDivActionPayload( path: UIElementPath, source: UserInterfaceAction.DivActionSource, diff --git a/DivKit/Extensions/DivBase/DivBaseExtensions.swift b/DivKit/Extensions/DivBase/DivBaseExtensions.swift index aad06603..7e195b9a 100644 --- a/DivKit/Extensions/DivBase/DivBaseExtensions.swift +++ b/DivKit/Extensions/DivBase/DivBaseExtensions.swift @@ -404,15 +404,20 @@ extension DivBorder { guard resolveHasShadow(expressionResolver) else { return nil } - - return BlockShadow( - cornerRadii: resolveCornerRadii(expressionResolver), - blurRadius: CGFloat(shadow?.resolveBlur(expressionResolver) ?? 2), - offset: shadow?.offset.resolve(expressionResolver) ?? .zero, - opacity: (shadow?.resolveAlpha(expressionResolver)).map(Float.init) - ?? BlockShadow.Defaults.opacity, - color: shadow?.resolveColor(expressionResolver) ?? BlockShadow.Defaults.color - ) + + let cornerRadii = resolveCornerRadii(expressionResolver) + + guard let shadow else { + return BlockShadow( + cornerRadii: cornerRadii, + blurRadius: 2, + offset: .zero, + opacity: BlockShadow.Defaults.opacity, + color: BlockShadow.Defaults.color + ) + } + + return shadow.resolve(expressionResolver, cornerRadii: cornerRadii) } fileprivate func resolveCornerRadii(_ expressionResolver: ExpressionResolver) -> CornerRadii { diff --git a/DivKit/Extensions/DivData/DivDataPatchExtensions.swift b/DivKit/Extensions/DivData/DivDataPatchExtensions.swift index 54f5610a..1f2be87a 100644 --- a/DivKit/Extensions/DivData/DivDataPatchExtensions.swift +++ b/DivKit/Extensions/DivData/DivDataPatchExtensions.swift @@ -24,6 +24,7 @@ extension DivData { State(div: $0.div.applySingleItemPatch(patch, callbacks: callbacks), stateId: $0.stateId) } return DivData( + functions: functions, logId: logId, states: states, timers: timers, @@ -284,6 +285,7 @@ extension DivPager { background: background, border: border, columnSpan: columnSpan, + crossAxisAlignment: crossAxisAlignment, defaultItem: defaultItem, disappearActions: disappearActions, extensions: extensions, diff --git a/DivKit/Extensions/DivPagerExtensions.swift b/DivKit/Extensions/DivPagerExtensions.swift index ca79daec..51f980f7 100644 --- a/DivKit/Extensions/DivPagerExtensions.swift +++ b/DivKit/Extensions/DivPagerExtensions.swift @@ -27,6 +27,7 @@ extension DivPager: DivBlockModeling, DivGalleryProtocol { let items = nonNilItems let scrollDirection = resolveOrientation(expressionResolver).direction let alignment = resolveScrollAxisAlignment(expressionResolver).system + let crossAlignment = resolveCrossAxisAlignment(expressionResolver).system let layoutMode = layoutMode.resolve(expressionResolver) let gallery = try makeGalleryModel( context: context, @@ -34,7 +35,7 @@ extension DivPager: DivBlockModeling, DivGalleryProtocol { alignment: alignment, spacing: CGFloat(itemSpacing.resolveValue(expressionResolver) ?? 0), crossSpacing: 0, - defaultCrossAlignment: .center, + defaultCrossAlignment: crossAlignment, scrollMode: .autoPaging(inertionEnabled: false), infiniteScroll: resolveInfiniteScroll(expressionResolver), bufferSize: layoutMode.bufferSize(itemsCount: items.count), @@ -112,7 +113,7 @@ extension DivPagerLayoutMode { } } -extension DivPager.ScrollAxisAlignment { +extension DivPager.ItemAlignment { fileprivate var system: Alignment { switch self { case .center: .center diff --git a/DivKit/Extensions/DivShadowExtensions.swift b/DivKit/Extensions/DivShadowExtensions.swift new file mode 100644 index 00000000..b9467832 --- /dev/null +++ b/DivKit/Extensions/DivShadowExtensions.swift @@ -0,0 +1,19 @@ +import LayoutKit +import VGSL + +import Foundation + +extension DivShadow { + func resolve( + _ expressionResolver: ExpressionResolver, + cornerRadii: CornerRadii = .zero + ) -> BlockShadow { + return BlockShadow( + cornerRadii: cornerRadii, + blurRadius: CGFloat(resolveBlur(expressionResolver)), + offset: offset.resolve(expressionResolver), + opacity: Float(resolveAlpha(expressionResolver)), + color: resolveColor(expressionResolver) + ) + } +} diff --git a/DivKit/Extensions/DivTextExtensions.swift b/DivKit/Extensions/DivTextExtensions.swift index eb06ec80..63b5c768 100644 --- a/DivKit/Extensions/DivTextExtensions.swift +++ b/DivKit/Extensions/DivTextExtensions.swift @@ -1,5 +1,4 @@ import CoreFoundation -import CoreGraphics import Foundation import LayoutKit import VGSL @@ -61,6 +60,9 @@ extension DivText: DivBlockModeling { case .none: break case .single: typo = typo.underlined(.single) } + if let textShadow = textShadow?.resolve(expressionResolver) { + typo = typo.shaded(textShadow.typoShadow) + } let attributedString = makeAttributedString( text: text.value as CFString, @@ -198,9 +200,17 @@ extension DivText: DivBlockModeling { let baselineOffsetTypo = Typo( baselineOffset: range.resolveBaselineOffset(expressionResolver) ) + let blockShadow = range.textShadow?.resolve(expressionResolver).typoShadow + let shadowTypo = blockShadow.map { Typo(shadow: $0) } let typos = [ - fontTypo, colorTypo, heightTypo, spacingTypo, - strikethroughTypo, underlineTypo, baselineOffsetTypo + fontTypo, + colorTypo, + heightTypo, + spacingTypo, + strikethroughTypo, + underlineTypo, + baselineOffsetTypo, + shadowTypo, ].compactMap { $0 } let actions = range.actions?.uiActions(context: context) if typos.isEmpty, actions == nil, range.background == nil, range.border == nil { @@ -367,3 +377,13 @@ extension DivLineStyle { } } } + +extension BlockShadow { + fileprivate var typoShadow: Shadow { + Shadow( + offset: CGSize(width: offset.x, height: -offset.y), + blurRadius: blurRadius, + color: color.withAlphaComponent(CGFloat(opacity)) + ) + } +} diff --git a/DivKit/Tooltips/DivTooltipViewFactory.swift b/DivKit/Tooltips/DivTooltipViewFactory.swift index fa7c2fce..d8a0ac0f 100644 --- a/DivKit/Tooltips/DivTooltipViewFactory.swift +++ b/DivKit/Tooltips/DivTooltipViewFactory.swift @@ -17,6 +17,7 @@ public struct DivTooltipViewFactory { func makeView(div: Div, tooltipId: String) async -> VisibleBoundsTrackingView { let view = DivView(divKitComponents: divKitComponents) let divData = DivData( + functions: nil, logId: tooltipId, states: [.init(div: div, stateId: 0)], timers: nil, diff --git a/DivKit/generated_sources/DivActionDownload.swift b/DivKit/generated_sources/DivActionDownload.swift index 43ef04db..5161bcb6 100644 --- a/DivKit/generated_sources/DivActionDownload.swift +++ b/DivKit/generated_sources/DivActionDownload.swift @@ -8,16 +8,16 @@ public final class DivActionDownload: Sendable { public static let type: String = "download" public let onFailActions: [DivAction]? public let onSuccessActions: [DivAction]? - public let url: Expression + public let url: Expression - public func resolveUrl(_ resolver: ExpressionResolver) -> String? { - resolver.resolveString(url) + public func resolveUrl(_ resolver: ExpressionResolver) -> URL? { + resolver.resolveUrl(url) } init( onFailActions: [DivAction]? = nil, onSuccessActions: [DivAction]? = nil, - url: Expression + url: Expression ) { self.onFailActions = onFailActions self.onSuccessActions = onSuccessActions diff --git a/DivKit/generated_sources/DivActionDownloadTemplate.swift b/DivKit/generated_sources/DivActionDownloadTemplate.swift index 2578afdd..6c66215d 100644 --- a/DivKit/generated_sources/DivActionDownloadTemplate.swift +++ b/DivKit/generated_sources/DivActionDownloadTemplate.swift @@ -9,14 +9,14 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { public let parent: String? public let onFailActions: Field<[DivActionTemplate]>? public let onSuccessActions: Field<[DivActionTemplate]>? - public let url: Field>? + public let url: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, onFailActions: dictionary.getOptionalArray("on_fail_actions", templateToType: templateToType), onSuccessActions: dictionary.getOptionalArray("on_success_actions", templateToType: templateToType), - url: dictionary.getOptionalExpressionField("url") + url: dictionary.getOptionalExpressionField("url", transform: URL.init(string:)) ) } @@ -24,7 +24,7 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { parent: String?, onFailActions: Field<[DivActionTemplate]>? = nil, onSuccessActions: Field<[DivActionTemplate]>? = nil, - url: Field>? = nil + url: Field>? = nil ) { self.parent = parent self.onFailActions = onFailActions @@ -35,7 +35,7 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { private static func resolveOnlyLinks(context: TemplatesContext, parent: DivActionDownloadTemplate?) -> DeserializationResult { let onFailActionsValue = { parent?.onFailActions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let onSuccessActionsValue = { parent?.onSuccessActions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() - let urlValue = { parent?.url?.resolveValue(context: context) ?? .noValue }() + let urlValue = { parent?.url?.resolveValue(context: context, transform: URL.init(string:)) ?? .noValue }() var errors = mergeErrors( onFailActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "on_fail_actions", error: $0) }, onSuccessActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "on_success_actions", error: $0) }, @@ -63,7 +63,7 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { } var onFailActionsValue: DeserializationResult<[DivAction]> = .noValue var onSuccessActionsValue: DeserializationResult<[DivAction]> = .noValue - var urlValue: DeserializationResult> = { parent?.url?.value() ?? .noValue }() + var urlValue: DeserializationResult> = { parent?.url?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable @@ -81,7 +81,7 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { }() _ = { if key == "url" { - urlValue = deserialize(__dictValue).merged(with: urlValue) + urlValue = deserialize(__dictValue, transform: URL.init(string:)).merged(with: urlValue) } }() _ = { @@ -96,7 +96,7 @@ public final class DivActionDownloadTemplate: TemplateValue, Sendable { }() _ = { if key == parent?.url?.link { - urlValue = urlValue.merged(with: { deserialize(__dictValue) }) + urlValue = urlValue.merged(with: { deserialize(__dictValue, transform: URL.init(string:)) }) } }() } diff --git a/DivKit/generated_sources/DivData.swift b/DivKit/generated_sources/DivData.swift index 55febe87..9e72194f 100644 --- a/DivKit/generated_sources/DivData.swift +++ b/DivKit/generated_sources/DivData.swift @@ -18,6 +18,7 @@ public final class DivData: Sendable { } } + public let functions: [DivFunction]? public let logId: String public let states: [State] // at least 1 elements public let timers: [DivTimer]? @@ -33,6 +34,7 @@ public final class DivData: Sendable { makeArrayValidator(minItems: 1) init( + functions: [DivFunction]?, logId: String, states: [State], timers: [DivTimer]?, @@ -40,6 +42,7 @@ public final class DivData: Sendable { variableTriggers: [DivTrigger]?, variables: [DivVariable]? ) { + self.functions = functions self.logId = logId self.states = states self.timers = timers @@ -53,15 +56,20 @@ public final class DivData: Sendable { extension DivData: Equatable { public static func ==(lhs: DivData, rhs: DivData) -> Bool { guard + lhs.functions == rhs.functions, lhs.logId == rhs.logId, - lhs.states == rhs.states, - lhs.timers == rhs.timers + lhs.states == rhs.states else { return false } guard + lhs.timers == rhs.timers, lhs.transitionAnimationSelector == rhs.transitionAnimationSelector, - lhs.variableTriggers == rhs.variableTriggers, + lhs.variableTriggers == rhs.variableTriggers + else { + return false + } + guard lhs.variables == rhs.variables else { return false @@ -74,6 +82,7 @@ extension DivData: Equatable { extension DivData: Serializable { public func toDictionary() -> [String: ValidSerializationValue] { var result: [String: ValidSerializationValue] = [:] + result["functions"] = functions?.map { $0.toDictionary() } result["log_id"] = logId result["states"] = states.map { $0.toDictionary() } result["timers"] = timers?.map { $0.toDictionary() } diff --git a/DivKit/generated_sources/DivDataTemplate.swift b/DivKit/generated_sources/DivDataTemplate.swift index 5ba7ba08..f8f80379 100644 --- a/DivKit/generated_sources/DivDataTemplate.swift +++ b/DivKit/generated_sources/DivDataTemplate.swift @@ -123,6 +123,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { } } + public let functions: Field<[DivFunctionTemplate]>? public let logId: Field? public let states: Field<[StateTemplate]>? // at least 1 elements public let timers: Field<[DivTimerTemplate]>? @@ -132,6 +133,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( + functions: dictionary.getOptionalArray("functions", templateToType: templateToType), logId: dictionary.getOptionalField("log_id"), states: dictionary.getOptionalArray("states", templateToType: templateToType), timers: dictionary.getOptionalArray("timers", templateToType: templateToType), @@ -142,6 +144,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { } init( + functions: Field<[DivFunctionTemplate]>? = nil, logId: Field? = nil, states: Field<[StateTemplate]>? = nil, timers: Field<[DivTimerTemplate]>? = nil, @@ -149,6 +152,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { variableTriggers: Field<[DivTriggerTemplate]>? = nil, variables: Field<[DivVariableTemplate]>? = nil ) { + self.functions = functions self.logId = logId self.states = states self.timers = timers @@ -158,6 +162,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { } private static func resolveOnlyLinks(context: TemplatesContext, parent: DivDataTemplate?) -> DeserializationResult { + let functionsValue = { parent?.functions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let logIdValue = { parent?.logId?.resolveValue(context: context) ?? .noValue }() let statesValue = { parent?.states?.resolveValue(context: context, validator: ResolvedValue.statesValidator, useOnlyLinks: true) ?? .noValue }() let timersValue = { parent?.timers?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() @@ -165,6 +170,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { let variableTriggersValue = { parent?.variableTriggers?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let variablesValue = { parent?.variables?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() var errors = mergeErrors( + functionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "functions", error: $0) }, logIdValue.errorsOrWarnings?.map { .nestedObjectError(field: "log_id", error: $0) }, statesValue.errorsOrWarnings?.map { .nestedObjectError(field: "states", error: $0) }, timersValue.errorsOrWarnings?.map { .nestedObjectError(field: "timers", error: $0) }, @@ -185,6 +191,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { return .failure(NonEmptyArray(errors)!) } let result = DivData( + functions: { functionsValue.value }(), logId: { logIdNonNil }(), states: { statesNonNil }(), timers: { timersValue.value }(), @@ -199,6 +206,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { if useOnlyLinks { return resolveOnlyLinks(context: context, parent: parent) } + var functionsValue: DeserializationResult<[DivFunction]> = .noValue var logIdValue: DeserializationResult = { parent?.logId?.value() ?? .noValue }() var statesValue: DeserializationResult<[DivData.State]> = .noValue var timersValue: DeserializationResult<[DivTimer]> = .noValue @@ -210,6 +218,11 @@ public final class DivDataTemplate: TemplateValue, Sendable { // Otherwise the compiler will allocate stack for each intermediate variable // upfront even when we don't actually visit a relevant branch for (key, __dictValue) in context.templateData { + _ = { + if key == "functions" { + functionsValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivFunctionTemplate.self).merged(with: functionsValue) + } + }() _ = { if key == "log_id" { logIdValue = deserialize(__dictValue).merged(with: logIdValue) @@ -240,6 +253,11 @@ public final class DivDataTemplate: TemplateValue, Sendable { variablesValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivVariableTemplate.self).merged(with: variablesValue) } }() + _ = { + if key == parent?.functions?.link { + functionsValue = functionsValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivFunctionTemplate.self) }) + } + }() _ = { if key == parent?.logId?.link { logIdValue = logIdValue.merged(with: { deserialize(__dictValue) }) @@ -273,12 +291,14 @@ public final class DivDataTemplate: TemplateValue, Sendable { } }() if let parent = parent { + _ = { functionsValue = functionsValue.merged(with: { parent.functions?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { statesValue = statesValue.merged(with: { parent.states?.resolveValue(context: context, validator: ResolvedValue.statesValidator, useOnlyLinks: true) }) }() _ = { timersValue = timersValue.merged(with: { parent.timers?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { variableTriggersValue = variableTriggersValue.merged(with: { parent.variableTriggers?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { variablesValue = variablesValue.merged(with: { parent.variables?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() } var errors = mergeErrors( + functionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "functions", error: $0) }, logIdValue.errorsOrWarnings?.map { .nestedObjectError(field: "log_id", error: $0) }, statesValue.errorsOrWarnings?.map { .nestedObjectError(field: "states", error: $0) }, timersValue.errorsOrWarnings?.map { .nestedObjectError(field: "timers", error: $0) }, @@ -299,6 +319,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { return .failure(NonEmptyArray(errors)!) } let result = DivData( + functions: { functionsValue.value }(), logId: { logIdNonNil }(), states: { statesNonNil }(), timers: { timersValue.value }(), @@ -317,6 +338,7 @@ public final class DivDataTemplate: TemplateValue, Sendable { let merged = try mergedWithParent(templates: templates) return DivDataTemplate( + functions: merged.functions?.tryResolveParent(templates: templates), logId: merged.logId, states: try merged.states?.resolveParent(templates: templates), timers: merged.timers?.tryResolveParent(templates: templates), diff --git a/DivKit/generated_sources/DivPager.swift b/DivKit/generated_sources/DivPager.swift index 7f9827e5..8a0d11ff 100644 --- a/DivKit/generated_sources/DivPager.swift +++ b/DivKit/generated_sources/DivPager.swift @@ -6,18 +6,18 @@ import VGSL public final class DivPager: DivBase, Sendable { @frozen - public enum Orientation: String, CaseIterable, Sendable { - case horizontal = "horizontal" - case vertical = "vertical" - } - - @frozen - public enum ScrollAxisAlignment: String, CaseIterable, Sendable { + public enum ItemAlignment: String, CaseIterable, Sendable { case start = "start" case center = "center" case end = "end" } + @frozen + public enum Orientation: String, CaseIterable, Sendable { + case horizontal = "horizontal" + case vertical = "vertical" + } + public static let type: String = "pager" public let accessibility: DivAccessibility? public let alignmentHorizontal: Expression? @@ -27,6 +27,7 @@ public final class DivPager: DivBase, Sendable { public let background: [DivBackground]? public let border: DivBorder? public let columnSpan: Expression? // constraint: number >= 0 + public let crossAxisAlignment: Expression // default value: start public let defaultItem: Expression // constraint: number >= 0; default value: 0 public let disappearActions: [DivDisappearAction]? public let extensions: [DivExtension]? @@ -47,7 +48,7 @@ public final class DivPager: DivBase, Sendable { public let restrictParentScroll: Expression // default value: false public let reuseId: Expression? public let rowSpan: Expression? // constraint: number >= 0 - public let scrollAxisAlignment: Expression // default value: center + public let scrollAxisAlignment: Expression // default value: center public let selectedActions: [DivAction]? public let tooltips: [DivTooltip]? public let transform: DivTransform? @@ -78,6 +79,10 @@ public final class DivPager: DivBase, Sendable { resolver.resolveNumeric(columnSpan) } + public func resolveCrossAxisAlignment(_ resolver: ExpressionResolver) -> ItemAlignment { + resolver.resolveEnum(crossAxisAlignment) ?? ItemAlignment.start + } + public func resolveDefaultItem(_ resolver: ExpressionResolver) -> Int { resolver.resolveNumeric(defaultItem) ?? 0 } @@ -102,8 +107,8 @@ public final class DivPager: DivBase, Sendable { resolver.resolveNumeric(rowSpan) } - public func resolveScrollAxisAlignment(_ resolver: ExpressionResolver) -> ScrollAxisAlignment { - resolver.resolveEnum(scrollAxisAlignment) ?? ScrollAxisAlignment.center + public func resolveScrollAxisAlignment(_ resolver: ExpressionResolver) -> ItemAlignment { + resolver.resolveEnum(scrollAxisAlignment) ?? ItemAlignment.center } public func resolveVisibility(_ resolver: ExpressionResolver) -> DivVisibility { @@ -134,6 +139,7 @@ public final class DivPager: DivBase, Sendable { background: [DivBackground]?, border: DivBorder?, columnSpan: Expression?, + crossAxisAlignment: Expression?, defaultItem: Expression?, disappearActions: [DivDisappearAction]?, extensions: [DivExtension]?, @@ -154,7 +160,7 @@ public final class DivPager: DivBase, Sendable { restrictParentScroll: Expression?, reuseId: Expression?, rowSpan: Expression?, - scrollAxisAlignment: Expression?, + scrollAxisAlignment: Expression?, selectedActions: [DivAction]?, tooltips: [DivTooltip]?, transform: DivTransform?, @@ -177,6 +183,7 @@ public final class DivPager: DivBase, Sendable { self.background = background self.border = border self.columnSpan = columnSpan + self.crossAxisAlignment = crossAxisAlignment ?? .value(.start) self.defaultItem = defaultItem ?? .value(0) self.disappearActions = disappearActions self.extensions = extensions @@ -234,83 +241,88 @@ extension DivPager: Equatable { guard lhs.border == rhs.border, lhs.columnSpan == rhs.columnSpan, - lhs.defaultItem == rhs.defaultItem + lhs.crossAxisAlignment == rhs.crossAxisAlignment else { return false } guard + lhs.defaultItem == rhs.defaultItem, lhs.disappearActions == rhs.disappearActions, - lhs.extensions == rhs.extensions, - lhs.focus == rhs.focus + lhs.extensions == rhs.extensions else { return false } guard + lhs.focus == rhs.focus, lhs.functions == rhs.functions, - lhs.height == rhs.height, - lhs.id == rhs.id + lhs.height == rhs.height else { return false } guard + lhs.id == rhs.id, lhs.infiniteScroll == rhs.infiniteScroll, - lhs.itemBuilder == rhs.itemBuilder, - lhs.itemSpacing == rhs.itemSpacing + lhs.itemBuilder == rhs.itemBuilder else { return false } guard + lhs.itemSpacing == rhs.itemSpacing, lhs.items == rhs.items, - lhs.layoutMode == rhs.layoutMode, - lhs.layoutProvider == rhs.layoutProvider + lhs.layoutMode == rhs.layoutMode else { return false } guard + lhs.layoutProvider == rhs.layoutProvider, lhs.margins == rhs.margins, - lhs.orientation == rhs.orientation, - lhs.paddings == rhs.paddings + lhs.orientation == rhs.orientation else { return false } guard + lhs.paddings == rhs.paddings, lhs.pageTransformation == rhs.pageTransformation, - lhs.restrictParentScroll == rhs.restrictParentScroll, - lhs.reuseId == rhs.reuseId + lhs.restrictParentScroll == rhs.restrictParentScroll else { return false } guard + lhs.reuseId == rhs.reuseId, lhs.rowSpan == rhs.rowSpan, - lhs.scrollAxisAlignment == rhs.scrollAxisAlignment, - lhs.selectedActions == rhs.selectedActions + lhs.scrollAxisAlignment == rhs.scrollAxisAlignment else { return false } guard + lhs.selectedActions == rhs.selectedActions, lhs.tooltips == rhs.tooltips, - lhs.transform == rhs.transform, - lhs.transitionChange == rhs.transitionChange + lhs.transform == rhs.transform else { return false } guard + lhs.transitionChange == rhs.transitionChange, lhs.transitionIn == rhs.transitionIn, - lhs.transitionOut == rhs.transitionOut, - lhs.transitionTriggers == rhs.transitionTriggers + lhs.transitionOut == rhs.transitionOut else { return false } guard + lhs.transitionTriggers == rhs.transitionTriggers, lhs.variableTriggers == rhs.variableTriggers, - lhs.variables == rhs.variables, - lhs.visibility == rhs.visibility + lhs.variables == rhs.variables else { return false } guard + lhs.visibility == rhs.visibility, lhs.visibilityAction == rhs.visibilityAction, - lhs.visibilityActions == rhs.visibilityActions, + lhs.visibilityActions == rhs.visibilityActions + else { + return false + } + guard lhs.width == rhs.width else { return false @@ -332,6 +344,7 @@ extension DivPager: Serializable { result["background"] = background?.map { $0.toDictionary() } result["border"] = border?.toDictionary() result["column_span"] = columnSpan?.toValidSerializationValue() + result["cross_axis_alignment"] = crossAxisAlignment.toValidSerializationValue() result["default_item"] = defaultItem.toValidSerializationValue() result["disappear_actions"] = disappearActions?.map { $0.toDictionary() } result["extensions"] = extensions?.map { $0.toDictionary() } diff --git a/DivKit/generated_sources/DivPagerTemplate.swift b/DivKit/generated_sources/DivPagerTemplate.swift index 4527007f..749fcfb2 100644 --- a/DivKit/generated_sources/DivPagerTemplate.swift +++ b/DivKit/generated_sources/DivPagerTemplate.swift @@ -5,9 +5,9 @@ import Serialization import VGSL public final class DivPagerTemplate: TemplateValue, Sendable { - public typealias Orientation = DivPager.Orientation + public typealias ItemAlignment = DivPager.ItemAlignment - public typealias ScrollAxisAlignment = DivPager.ScrollAxisAlignment + public typealias Orientation = DivPager.Orientation public static let type: String = "pager" public let parent: String? @@ -19,6 +19,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { public let background: Field<[DivBackgroundTemplate]>? public let border: Field? public let columnSpan: Field>? // constraint: number >= 0 + public let crossAxisAlignment: Field>? // default value: start public let defaultItem: Field>? // constraint: number >= 0; default value: 0 public let disappearActions: Field<[DivDisappearActionTemplate]>? public let extensions: Field<[DivExtensionTemplate]>? @@ -39,7 +40,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { public let restrictParentScroll: Field>? // default value: false public let reuseId: Field>? public let rowSpan: Field>? // constraint: number >= 0 - public let scrollAxisAlignment: Field>? // default value: center + public let scrollAxisAlignment: Field>? // default value: center public let selectedActions: Field<[DivActionTemplate]>? public let tooltips: Field<[DivTooltipTemplate]>? public let transform: Field? @@ -65,6 +66,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: dictionary.getOptionalArray("background", templateToType: templateToType), border: dictionary.getOptionalField("border", templateToType: templateToType), columnSpan: dictionary.getOptionalExpressionField("column_span"), + crossAxisAlignment: dictionary.getOptionalExpressionField("cross_axis_alignment"), defaultItem: dictionary.getOptionalExpressionField("default_item"), disappearActions: dictionary.getOptionalArray("disappear_actions", templateToType: templateToType), extensions: dictionary.getOptionalArray("extensions", templateToType: templateToType), @@ -112,6 +114,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: Field<[DivBackgroundTemplate]>? = nil, border: Field? = nil, columnSpan: Field>? = nil, + crossAxisAlignment: Field>? = nil, defaultItem: Field>? = nil, disappearActions: Field<[DivDisappearActionTemplate]>? = nil, extensions: Field<[DivExtensionTemplate]>? = nil, @@ -132,7 +135,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { restrictParentScroll: Field>? = nil, reuseId: Field>? = nil, rowSpan: Field>? = nil, - scrollAxisAlignment: Field>? = nil, + scrollAxisAlignment: Field>? = nil, selectedActions: Field<[DivActionTemplate]>? = nil, tooltips: Field<[DivTooltipTemplate]>? = nil, transform: Field? = nil, @@ -156,6 +159,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { self.background = background self.border = border self.columnSpan = columnSpan + self.crossAxisAlignment = crossAxisAlignment self.defaultItem = defaultItem self.disappearActions = disappearActions self.extensions = extensions @@ -201,6 +205,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { let backgroundValue = { parent?.background?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let borderValue = { parent?.border?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let columnSpanValue = { parent?.columnSpan?.resolveOptionalValue(context: context, validator: ResolvedValue.columnSpanValidator) ?? .noValue }() + let crossAxisAlignmentValue = { parent?.crossAxisAlignment?.resolveOptionalValue(context: context) ?? .noValue }() let defaultItemValue = { parent?.defaultItem?.resolveOptionalValue(context: context, validator: ResolvedValue.defaultItemValidator) ?? .noValue }() let disappearActionsValue = { parent?.disappearActions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let extensionsValue = { parent?.extensions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() @@ -244,6 +249,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { backgroundValue.errorsOrWarnings?.map { .nestedObjectError(field: "background", error: $0) }, borderValue.errorsOrWarnings?.map { .nestedObjectError(field: "border", error: $0) }, columnSpanValue.errorsOrWarnings?.map { .nestedObjectError(field: "column_span", error: $0) }, + crossAxisAlignmentValue.errorsOrWarnings?.map { .nestedObjectError(field: "cross_axis_alignment", error: $0) }, defaultItemValue.errorsOrWarnings?.map { .nestedObjectError(field: "default_item", error: $0) }, disappearActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "disappear_actions", error: $0) }, extensionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "extensions", error: $0) }, @@ -296,6 +302,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: { backgroundValue.value }(), border: { borderValue.value }(), columnSpan: { columnSpanValue.value }(), + crossAxisAlignment: { crossAxisAlignmentValue.value }(), defaultItem: { defaultItemValue.value }(), disappearActions: { disappearActionsValue.value }(), extensions: { extensionsValue.value }(), @@ -346,6 +353,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { var backgroundValue: DeserializationResult<[DivBackground]> = .noValue var borderValue: DeserializationResult = .noValue var columnSpanValue: DeserializationResult> = { parent?.columnSpan?.value() ?? .noValue }() + var crossAxisAlignmentValue: DeserializationResult> = { parent?.crossAxisAlignment?.value() ?? .noValue }() var defaultItemValue: DeserializationResult> = { parent?.defaultItem?.value() ?? .noValue }() var disappearActionsValue: DeserializationResult<[DivDisappearAction]> = .noValue var extensionsValue: DeserializationResult<[DivExtension]> = .noValue @@ -366,7 +374,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { var restrictParentScrollValue: DeserializationResult> = { parent?.restrictParentScroll?.value() ?? .noValue }() var reuseIdValue: DeserializationResult> = { parent?.reuseId?.value() ?? .noValue }() var rowSpanValue: DeserializationResult> = { parent?.rowSpan?.value() ?? .noValue }() - var scrollAxisAlignmentValue: DeserializationResult> = { parent?.scrollAxisAlignment?.value() ?? .noValue }() + var scrollAxisAlignmentValue: DeserializationResult> = { parent?.scrollAxisAlignment?.value() ?? .noValue }() var selectedActionsValue: DeserializationResult<[DivAction]> = .noValue var tooltipsValue: DeserializationResult<[DivTooltip]> = .noValue var transformValue: DeserializationResult = .noValue @@ -425,6 +433,11 @@ public final class DivPagerTemplate: TemplateValue, Sendable { columnSpanValue = deserialize(__dictValue, validator: ResolvedValue.columnSpanValidator).merged(with: columnSpanValue) } }() + _ = { + if key == "cross_axis_alignment" { + crossAxisAlignmentValue = deserialize(__dictValue).merged(with: crossAxisAlignmentValue) + } + }() _ = { if key == "default_item" { defaultItemValue = deserialize(__dictValue, validator: ResolvedValue.defaultItemValidator).merged(with: defaultItemValue) @@ -635,6 +648,11 @@ public final class DivPagerTemplate: TemplateValue, Sendable { columnSpanValue = columnSpanValue.merged(with: { deserialize(__dictValue, validator: ResolvedValue.columnSpanValidator) }) } }() + _ = { + if key == parent?.crossAxisAlignment?.link { + crossAxisAlignmentValue = crossAxisAlignmentValue.merged(with: { deserialize(__dictValue) }) + } + }() _ = { if key == parent?.defaultItem?.link { defaultItemValue = defaultItemValue.merged(with: { deserialize(__dictValue, validator: ResolvedValue.defaultItemValidator) }) @@ -846,6 +864,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { backgroundValue.errorsOrWarnings?.map { .nestedObjectError(field: "background", error: $0) }, borderValue.errorsOrWarnings?.map { .nestedObjectError(field: "border", error: $0) }, columnSpanValue.errorsOrWarnings?.map { .nestedObjectError(field: "column_span", error: $0) }, + crossAxisAlignmentValue.errorsOrWarnings?.map { .nestedObjectError(field: "cross_axis_alignment", error: $0) }, defaultItemValue.errorsOrWarnings?.map { .nestedObjectError(field: "default_item", error: $0) }, disappearActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "disappear_actions", error: $0) }, extensionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "extensions", error: $0) }, @@ -898,6 +917,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: { backgroundValue.value }(), border: { borderValue.value }(), columnSpan: { columnSpanValue.value }(), + crossAxisAlignment: { crossAxisAlignmentValue.value }(), defaultItem: { defaultItemValue.value }(), disappearActions: { disappearActionsValue.value }(), extensions: { extensionsValue.value }(), @@ -953,6 +973,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: background ?? mergedParent.background, border: border ?? mergedParent.border, columnSpan: columnSpan ?? mergedParent.columnSpan, + crossAxisAlignment: crossAxisAlignment ?? mergedParent.crossAxisAlignment, defaultItem: defaultItem ?? mergedParent.defaultItem, disappearActions: disappearActions ?? mergedParent.disappearActions, extensions: extensions ?? mergedParent.extensions, @@ -1003,6 +1024,7 @@ public final class DivPagerTemplate: TemplateValue, Sendable { background: merged.background?.tryResolveParent(templates: templates), border: merged.border?.tryResolveParent(templates: templates), columnSpan: merged.columnSpan, + crossAxisAlignment: merged.crossAxisAlignment, defaultItem: merged.defaultItem, disappearActions: merged.disappearActions?.tryResolveParent(templates: templates), extensions: merged.extensions?.tryResolveParent(templates: templates), diff --git a/DivKit/generated_sources/DivTooltip.swift b/DivKit/generated_sources/DivTooltip.swift index c76a3331..c3352eec 100644 --- a/DivKit/generated_sources/DivTooltip.swift +++ b/DivKit/generated_sources/DivTooltip.swift @@ -20,11 +20,18 @@ public final class DivTooltip: Sendable { public let animationIn: DivAnimation? public let animationOut: DivAnimation? + public let closeByTapOutside: Expression // default value: true public let div: Div public let duration: Expression // constraint: number >= 0; default value: 5000 public let id: String + public let mode: DivTooltipMode // default value: .divTooltipModeModal(DivTooltipModeModal()) public let offset: DivPoint? public let position: Expression + public let tapOutsideActions: [DivAction]? + + public func resolveCloseByTapOutside(_ resolver: ExpressionResolver) -> Bool { + resolver.resolveNumeric(closeByTapOutside) ?? true + } public func resolveDuration(_ resolver: ExpressionResolver) -> Int { resolver.resolveNumeric(duration) ?? 5000 @@ -40,19 +47,25 @@ public final class DivTooltip: Sendable { init( animationIn: DivAnimation? = nil, animationOut: DivAnimation? = nil, + closeByTapOutside: Expression? = nil, div: Div, duration: Expression? = nil, id: String, + mode: DivTooltipMode? = nil, offset: DivPoint? = nil, - position: Expression + position: Expression, + tapOutsideActions: [DivAction]? = nil ) { self.animationIn = animationIn self.animationOut = animationOut + self.closeByTapOutside = closeByTapOutside ?? .value(true) self.div = div self.duration = duration ?? .value(5000) self.id = id + self.mode = mode ?? .divTooltipModeModal(DivTooltipModeModal()) self.offset = offset self.position = position + self.tapOutsideActions = tapOutsideActions } } @@ -62,22 +75,29 @@ extension DivTooltip: Equatable { guard lhs.animationIn == rhs.animationIn, lhs.animationOut == rhs.animationOut, - lhs.div == rhs.div + lhs.closeByTapOutside == rhs.closeByTapOutside else { return false } guard + lhs.div == rhs.div, lhs.duration == rhs.duration, - lhs.id == rhs.id, - lhs.offset == rhs.offset + lhs.id == rhs.id else { return false } guard + lhs.mode == rhs.mode, + lhs.offset == rhs.offset, lhs.position == rhs.position else { return false } + guard + lhs.tapOutsideActions == rhs.tapOutsideActions + else { + return false + } return true } } @@ -88,11 +108,14 @@ extension DivTooltip: Serializable { var result: [String: ValidSerializationValue] = [:] result["animation_in"] = animationIn?.toDictionary() result["animation_out"] = animationOut?.toDictionary() + result["close_by_tap_outside"] = closeByTapOutside.toValidSerializationValue() result["div"] = div.toDictionary() result["duration"] = duration.toValidSerializationValue() result["id"] = id + result["mode"] = mode.toDictionary() result["offset"] = offset?.toDictionary() result["position"] = position.toValidSerializationValue() + result["tap_outside_actions"] = tapOutsideActions?.map { $0.toDictionary() } return result } } diff --git a/DivKit/generated_sources/DivTooltipMode.swift b/DivKit/generated_sources/DivTooltipMode.swift new file mode 100644 index 00000000..b49a4016 --- /dev/null +++ b/DivKit/generated_sources/DivTooltipMode.swift @@ -0,0 +1,41 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +@frozen +public enum DivTooltipMode: Sendable { + case divTooltipModeNonModal(DivTooltipModeNonModal) + case divTooltipModeModal(DivTooltipModeModal) + + public var value: Serializable { + switch self { + case let .divTooltipModeNonModal(value): + return value + case let .divTooltipModeModal(value): + return value + } + } +} + +#if DEBUG +extension DivTooltipMode: Equatable { + public static func ==(lhs: DivTooltipMode, rhs: DivTooltipMode) -> Bool { + switch (lhs, rhs) { + case let (.divTooltipModeNonModal(l), .divTooltipModeNonModal(r)): + return l == r + case let (.divTooltipModeModal(l), .divTooltipModeModal(r)): + return l == r + default: + return false + } + } +} +#endif + +extension DivTooltipMode: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + return value.toDictionary() + } +} diff --git a/DivKit/generated_sources/DivTooltipModeModal.swift b/DivKit/generated_sources/DivTooltipModeModal.swift new file mode 100644 index 00000000..e52e0e37 --- /dev/null +++ b/DivKit/generated_sources/DivTooltipModeModal.swift @@ -0,0 +1,27 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +public final class DivTooltipModeModal: Sendable { + public static let type: String = "modal" + + init() {} +} + +#if DEBUG +extension DivTooltipModeModal: Equatable { + public static func ==(lhs: DivTooltipModeModal, rhs: DivTooltipModeModal) -> Bool { + return true + } +} +#endif + +extension DivTooltipModeModal: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + var result: [String: ValidSerializationValue] = [:] + result["type"] = Self.type + return result + } +} diff --git a/DivKit/generated_sources/DivTooltipModeModalTemplate.swift b/DivKit/generated_sources/DivTooltipModeModalTemplate.swift new file mode 100644 index 00000000..56656d05 --- /dev/null +++ b/DivKit/generated_sources/DivTooltipModeModalTemplate.swift @@ -0,0 +1,38 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +public final class DivTooltipModeModalTemplate: TemplateValue, Sendable { + public static let type: String = "modal" + public let parent: String? + + public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { + self.init( + parent: dictionary["type"] as? String + ) + } + + init( + parent: String? + ) { + self.parent = parent + } + + private static func resolveOnlyLinks(context: TemplatesContext, parent: DivTooltipModeModalTemplate?) -> DeserializationResult { + return .success(DivTooltipModeModal()) + } + + public static func resolveValue(context: TemplatesContext, parent: DivTooltipModeModalTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + return .success(DivTooltipModeModal()) + } + + private func mergedWithParent(templates: [TemplateName: Any]) throws -> DivTooltipModeModalTemplate { + return self + } + + public func resolveParent(templates: [TemplateName: Any]) throws -> DivTooltipModeModalTemplate { + return self + } +} diff --git a/DivKit/generated_sources/DivTooltipModeNonModal.swift b/DivKit/generated_sources/DivTooltipModeNonModal.swift new file mode 100644 index 00000000..969dda1c --- /dev/null +++ b/DivKit/generated_sources/DivTooltipModeNonModal.swift @@ -0,0 +1,27 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +public final class DivTooltipModeNonModal: Sendable { + public static let type: String = "non_modal" + + init() {} +} + +#if DEBUG +extension DivTooltipModeNonModal: Equatable { + public static func ==(lhs: DivTooltipModeNonModal, rhs: DivTooltipModeNonModal) -> Bool { + return true + } +} +#endif + +extension DivTooltipModeNonModal: Serializable { + public func toDictionary() -> [String: ValidSerializationValue] { + var result: [String: ValidSerializationValue] = [:] + result["type"] = Self.type + return result + } +} diff --git a/DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift b/DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift new file mode 100644 index 00000000..c748adf2 --- /dev/null +++ b/DivKit/generated_sources/DivTooltipModeNonModalTemplate.swift @@ -0,0 +1,38 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +public final class DivTooltipModeNonModalTemplate: TemplateValue, Sendable { + public static let type: String = "non_modal" + public let parent: String? + + public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { + self.init( + parent: dictionary["type"] as? String + ) + } + + init( + parent: String? + ) { + self.parent = parent + } + + private static func resolveOnlyLinks(context: TemplatesContext, parent: DivTooltipModeNonModalTemplate?) -> DeserializationResult { + return .success(DivTooltipModeNonModal()) + } + + public static func resolveValue(context: TemplatesContext, parent: DivTooltipModeNonModalTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + return .success(DivTooltipModeNonModal()) + } + + private func mergedWithParent(templates: [TemplateName: Any]) throws -> DivTooltipModeNonModalTemplate { + return self + } + + public func resolveParent(templates: [TemplateName: Any]) throws -> DivTooltipModeNonModalTemplate { + return self + } +} diff --git a/DivKit/generated_sources/DivTooltipModeTemplate.swift b/DivKit/generated_sources/DivTooltipModeTemplate.swift new file mode 100644 index 00000000..d456a8ac --- /dev/null +++ b/DivKit/generated_sources/DivTooltipModeTemplate.swift @@ -0,0 +1,110 @@ +// Generated code. Do not modify. + +import Foundation +import Serialization +import VGSL + +@frozen +public enum DivTooltipModeTemplate: TemplateValue, Sendable { + case divTooltipModeNonModalTemplate(DivTooltipModeNonModalTemplate) + case divTooltipModeModalTemplate(DivTooltipModeModalTemplate) + + public var value: Any { + switch self { + case let .divTooltipModeNonModalTemplate(value): + return value + case let .divTooltipModeModalTemplate(value): + return value + } + } + + public func resolveParent(templates: [TemplateName: Any]) throws -> DivTooltipModeTemplate { + switch self { + case let .divTooltipModeNonModalTemplate(value): + return .divTooltipModeNonModalTemplate(try value.resolveParent(templates: templates)) + case let .divTooltipModeModalTemplate(value): + return .divTooltipModeModalTemplate(try value.resolveParent(templates: templates)) + } + } + + public static func resolveValue(context: TemplatesContext, parent: DivTooltipModeTemplate?, useOnlyLinks: Bool) -> DeserializationResult { + guard let parent = parent else { + if useOnlyLinks { + return .failure(NonEmptyArray(.missingType(representation: context.templateData))) + } else { + return resolveUnknownValue(context: context, useOnlyLinks: useOnlyLinks) + } + } + + return { + var result: DeserializationResult! + result = result ?? { + if case let .divTooltipModeNonModalTemplate(value) = parent { + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divTooltipModeNonModal(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divTooltipModeNonModal(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } else { return nil } + }() + result = result ?? { + if case let .divTooltipModeModalTemplate(value) = parent { + let result = value.resolveValue(context: context, useOnlyLinks: useOnlyLinks) + switch result { + case let .success(value): return .success(.divTooltipModeModal(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divTooltipModeModal(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } else { return nil } + }() + return result + }() + } + + private static func resolveUnknownValue(context: TemplatesContext, useOnlyLinks: Bool) -> DeserializationResult { + guard let type = (context.templateData["type"] as? String).flatMap({ context.templateToType[$0] ?? $0 }) else { + return .failure(NonEmptyArray(.requiredFieldIsMissing(field: "type"))) + } + + return { + var result: DeserializationResult? + result = result ?? { if type == DivTooltipModeNonModal.type { + let result = { DivTooltipModeNonModalTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) }() + switch result { + case let .success(value): return .success(.divTooltipModeNonModal(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divTooltipModeNonModal(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } else { return nil } }() + result = result ?? { if type == DivTooltipModeModal.type { + let result = { DivTooltipModeModalTemplate.resolveValue(context: context, useOnlyLinks: useOnlyLinks) }() + switch result { + case let .success(value): return .success(.divTooltipModeModal(value)) + case let .partialSuccess(value, warnings): return .partialSuccess(.divTooltipModeModal(value), warnings: warnings) + case let .failure(errors): return .failure(errors) + case .noValue: return .noValue + } + } else { return nil } }() + return result ?? .failure(NonEmptyArray(.requiredFieldIsMissing(field: "type"))) + }() + } +} + +extension DivTooltipModeTemplate { + public init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { + let receivedType = try dictionary.getField("type") as String + let blockType = templateToType[receivedType] ?? receivedType + switch blockType { + case DivTooltipModeNonModalTemplate.type: + self = .divTooltipModeNonModalTemplate(try DivTooltipModeNonModalTemplate(dictionary: dictionary, templateToType: templateToType)) + case DivTooltipModeModalTemplate.type: + self = .divTooltipModeModalTemplate(try DivTooltipModeModalTemplate(dictionary: dictionary, templateToType: templateToType)) + default: + throw DeserializationError.invalidFieldRepresentation(field: "div-tooltip-mode_template", representation: dictionary) + } + } +} diff --git a/DivKit/generated_sources/DivTooltipTemplate.swift b/DivKit/generated_sources/DivTooltipTemplate.swift index 83f32391..3a625289 100644 --- a/DivKit/generated_sources/DivTooltipTemplate.swift +++ b/DivKit/generated_sources/DivTooltipTemplate.swift @@ -9,58 +9,76 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { public let animationIn: Field? public let animationOut: Field? + public let closeByTapOutside: Field>? // default value: true public let div: Field? public let duration: Field>? // constraint: number >= 0; default value: 5000 public let id: Field? + public let mode: Field? // default value: .divTooltipModeModal(DivTooltipModeModal()) public let offset: Field? public let position: Field>? + public let tapOutsideActions: Field<[DivActionTemplate]>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( animationIn: dictionary.getOptionalField("animation_in", templateToType: templateToType), animationOut: dictionary.getOptionalField("animation_out", templateToType: templateToType), + closeByTapOutside: dictionary.getOptionalExpressionField("close_by_tap_outside"), div: dictionary.getOptionalField("div", templateToType: templateToType), duration: dictionary.getOptionalExpressionField("duration"), id: dictionary.getOptionalField("id"), + mode: dictionary.getOptionalField("mode", templateToType: templateToType), offset: dictionary.getOptionalField("offset", templateToType: templateToType), - position: dictionary.getOptionalExpressionField("position") + position: dictionary.getOptionalExpressionField("position"), + tapOutsideActions: dictionary.getOptionalArray("tap_outside_actions", templateToType: templateToType) ) } init( animationIn: Field? = nil, animationOut: Field? = nil, + closeByTapOutside: Field>? = nil, div: Field? = nil, duration: Field>? = nil, id: Field? = nil, + mode: Field? = nil, offset: Field? = nil, - position: Field>? = nil + position: Field>? = nil, + tapOutsideActions: Field<[DivActionTemplate]>? = nil ) { self.animationIn = animationIn self.animationOut = animationOut + self.closeByTapOutside = closeByTapOutside self.div = div self.duration = duration self.id = id + self.mode = mode self.offset = offset self.position = position + self.tapOutsideActions = tapOutsideActions } private static func resolveOnlyLinks(context: TemplatesContext, parent: DivTooltipTemplate?) -> DeserializationResult { let animationInValue = { parent?.animationIn?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let animationOutValue = { parent?.animationOut?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() + let closeByTapOutsideValue = { parent?.closeByTapOutside?.resolveOptionalValue(context: context) ?? .noValue }() let divValue = { parent?.div?.resolveValue(context: context, useOnlyLinks: true) ?? .noValue }() let durationValue = { parent?.duration?.resolveOptionalValue(context: context, validator: ResolvedValue.durationValidator) ?? .noValue }() let idValue = { parent?.id?.resolveValue(context: context) ?? .noValue }() + let modeValue = { parent?.mode?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let offsetValue = { parent?.offset?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() let positionValue = { parent?.position?.resolveValue(context: context) ?? .noValue }() + let tapOutsideActionsValue = { parent?.tapOutsideActions?.resolveOptionalValue(context: context, useOnlyLinks: true) ?? .noValue }() var errors = mergeErrors( animationInValue.errorsOrWarnings?.map { .nestedObjectError(field: "animation_in", error: $0) }, animationOutValue.errorsOrWarnings?.map { .nestedObjectError(field: "animation_out", error: $0) }, + closeByTapOutsideValue.errorsOrWarnings?.map { .nestedObjectError(field: "close_by_tap_outside", error: $0) }, divValue.errorsOrWarnings?.map { .nestedObjectError(field: "div", error: $0) }, durationValue.errorsOrWarnings?.map { .nestedObjectError(field: "duration", error: $0) }, idValue.errorsOrWarnings?.map { .nestedObjectError(field: "id", error: $0) }, + modeValue.errorsOrWarnings?.map { .nestedObjectError(field: "mode", error: $0) }, offsetValue.errorsOrWarnings?.map { .nestedObjectError(field: "offset", error: $0) }, - positionValue.errorsOrWarnings?.map { .nestedObjectError(field: "position", error: $0) } + positionValue.errorsOrWarnings?.map { .nestedObjectError(field: "position", error: $0) }, + tapOutsideActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "tap_outside_actions", error: $0) } ) if case .noValue = divValue { errors.append(.requiredFieldIsMissing(field: "div")) @@ -81,11 +99,14 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { let result = DivTooltip( animationIn: { animationInValue.value }(), animationOut: { animationOutValue.value }(), + closeByTapOutside: { closeByTapOutsideValue.value }(), div: { divNonNil }(), duration: { durationValue.value }(), id: { idNonNil }(), + mode: { modeValue.value }(), offset: { offsetValue.value }(), - position: { positionNonNil }() + position: { positionNonNil }(), + tapOutsideActions: { tapOutsideActionsValue.value }() ) return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) } @@ -96,11 +117,14 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { } var animationInValue: DeserializationResult = .noValue var animationOutValue: DeserializationResult = .noValue + var closeByTapOutsideValue: DeserializationResult> = { parent?.closeByTapOutside?.value() ?? .noValue }() var divValue: DeserializationResult
= .noValue var durationValue: DeserializationResult> = { parent?.duration?.value() ?? .noValue }() var idValue: DeserializationResult = { parent?.id?.value() ?? .noValue }() + var modeValue: DeserializationResult = .noValue var offsetValue: DeserializationResult = .noValue var positionValue: DeserializationResult> = { parent?.position?.value() ?? .noValue }() + var tapOutsideActionsValue: DeserializationResult<[DivAction]> = .noValue _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable @@ -116,6 +140,11 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { animationOutValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivAnimationTemplate.self).merged(with: animationOutValue) } }() + _ = { + if key == "close_by_tap_outside" { + closeByTapOutsideValue = deserialize(__dictValue).merged(with: closeByTapOutsideValue) + } + }() _ = { if key == "div" { divValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivTemplate.self).merged(with: divValue) @@ -131,6 +160,11 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { idValue = deserialize(__dictValue).merged(with: idValue) } }() + _ = { + if key == "mode" { + modeValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivTooltipModeTemplate.self).merged(with: modeValue) + } + }() _ = { if key == "offset" { offsetValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivPointTemplate.self).merged(with: offsetValue) @@ -141,6 +175,11 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { positionValue = deserialize(__dictValue).merged(with: positionValue) } }() + _ = { + if key == "tap_outside_actions" { + tapOutsideActionsValue = deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivActionTemplate.self).merged(with: tapOutsideActionsValue) + } + }() _ = { if key == parent?.animationIn?.link { animationInValue = animationInValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivAnimationTemplate.self) }) @@ -151,6 +190,11 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { animationOutValue = animationOutValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivAnimationTemplate.self) }) } }() + _ = { + if key == parent?.closeByTapOutside?.link { + closeByTapOutsideValue = closeByTapOutsideValue.merged(with: { deserialize(__dictValue) }) + } + }() _ = { if key == parent?.div?.link { divValue = divValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivTemplate.self) }) @@ -166,6 +210,11 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { idValue = idValue.merged(with: { deserialize(__dictValue) }) } }() + _ = { + if key == parent?.mode?.link { + modeValue = modeValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivTooltipModeTemplate.self) }) + } + }() _ = { if key == parent?.offset?.link { offsetValue = offsetValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivPointTemplate.self) }) @@ -176,22 +225,32 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { positionValue = positionValue.merged(with: { deserialize(__dictValue) }) } }() + _ = { + if key == parent?.tapOutsideActions?.link { + tapOutsideActionsValue = tapOutsideActionsValue.merged(with: { deserialize(__dictValue, templates: context.templates, templateToType: context.templateToType, type: DivActionTemplate.self) }) + } + }() } }() if let parent = parent { _ = { animationInValue = animationInValue.merged(with: { parent.animationIn?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { animationOutValue = animationOutValue.merged(with: { parent.animationOut?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { divValue = divValue.merged(with: { parent.div?.resolveValue(context: context, useOnlyLinks: true) }) }() + _ = { modeValue = modeValue.merged(with: { parent.mode?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() _ = { offsetValue = offsetValue.merged(with: { parent.offset?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() + _ = { tapOutsideActionsValue = tapOutsideActionsValue.merged(with: { parent.tapOutsideActions?.resolveOptionalValue(context: context, useOnlyLinks: true) }) }() } var errors = mergeErrors( animationInValue.errorsOrWarnings?.map { .nestedObjectError(field: "animation_in", error: $0) }, animationOutValue.errorsOrWarnings?.map { .nestedObjectError(field: "animation_out", error: $0) }, + closeByTapOutsideValue.errorsOrWarnings?.map { .nestedObjectError(field: "close_by_tap_outside", error: $0) }, divValue.errorsOrWarnings?.map { .nestedObjectError(field: "div", error: $0) }, durationValue.errorsOrWarnings?.map { .nestedObjectError(field: "duration", error: $0) }, idValue.errorsOrWarnings?.map { .nestedObjectError(field: "id", error: $0) }, + modeValue.errorsOrWarnings?.map { .nestedObjectError(field: "mode", error: $0) }, offsetValue.errorsOrWarnings?.map { .nestedObjectError(field: "offset", error: $0) }, - positionValue.errorsOrWarnings?.map { .nestedObjectError(field: "position", error: $0) } + positionValue.errorsOrWarnings?.map { .nestedObjectError(field: "position", error: $0) }, + tapOutsideActionsValue.errorsOrWarnings?.map { .nestedObjectError(field: "tap_outside_actions", error: $0) } ) if case .noValue = divValue { errors.append(.requiredFieldIsMissing(field: "div")) @@ -212,11 +271,14 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { let result = DivTooltip( animationIn: { animationInValue.value }(), animationOut: { animationOutValue.value }(), + closeByTapOutside: { closeByTapOutsideValue.value }(), div: { divNonNil }(), duration: { durationValue.value }(), id: { idNonNil }(), + mode: { modeValue.value }(), offset: { offsetValue.value }(), - position: { positionNonNil }() + position: { positionNonNil }(), + tapOutsideActions: { tapOutsideActionsValue.value }() ) return errors.isEmpty ? .success(result) : .partialSuccess(result, warnings: NonEmptyArray(errors)!) } @@ -231,11 +293,14 @@ public final class DivTooltipTemplate: TemplateValue, Sendable { return DivTooltipTemplate( animationIn: merged.animationIn?.tryResolveParent(templates: templates), animationOut: merged.animationOut?.tryResolveParent(templates: templates), + closeByTapOutside: merged.closeByTapOutside, div: try merged.div?.resolveParent(templates: templates), duration: merged.duration, id: merged.id, + mode: merged.mode?.tryResolveParent(templates: templates), offset: merged.offset?.tryResolveParent(templates: templates), - position: merged.position + position: merged.position, + tapOutsideActions: merged.tapOutsideActions?.tryResolveParent(templates: templates) ) } } diff --git a/LayoutKit/LayoutKit/Blocks/CornerRadii.swift b/LayoutKit/LayoutKit/Blocks/CornerRadii.swift index 83f5fa43..323422ef 100644 --- a/LayoutKit/LayoutKit/Blocks/CornerRadii.swift +++ b/LayoutKit/LayoutKit/Blocks/CornerRadii.swift @@ -33,3 +33,7 @@ extension CornerRadii: ExpressibleByFloatLiteral { ) } } + +extension CornerRadii { + public static var zero: CornerRadii = .init(0) +} diff --git a/LayoutKit/LayoutKit/Blocks/Grid/GridBlock.swift b/LayoutKit/LayoutKit/Blocks/Grid/GridBlock.swift index 55a6ade2..aff6c25b 100644 --- a/LayoutKit/LayoutKit/Blocks/Grid/GridBlock.swift +++ b/LayoutKit/LayoutKit/Blocks/Grid/GridBlock.swift @@ -176,7 +176,7 @@ public final class GridBlock: BlockWithTraits, BlockWithLayout { var result = makeLayout(constrainedTo: CGSize(width: width, height: .infinity)).size.height - if case let .intrinsic(_, minSize, maxSize) = widthTrait { + if case let .intrinsic(_, minSize, maxSize) = heightTrait { result = clamp(result, min: minSize, max: maxSize) } diff --git a/LayoutKit/LayoutKit/UI/Blocks/DecoratingBlock+UIViewRenderableBlock.swift b/LayoutKit/LayoutKit/UI/Blocks/DecoratingBlock+UIViewRenderableBlock.swift index 72484b60..38b15931 100644 --- a/LayoutKit/LayoutKit/UI/Blocks/DecoratingBlock+UIViewRenderableBlock.swift +++ b/LayoutKit/LayoutKit/UI/Blocks/DecoratingBlock+UIViewRenderableBlock.swift @@ -242,6 +242,7 @@ private final class DecoratingView: UIControl, BlockViewProtocol, VisibleBoundsT if tapRecognizer == nil { tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) } + tapRecognizer?.cancelsTouchesInView = false if model.shouldHandleDoubleTap, doubleTapRecognizer == nil { let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) diff --git a/LayoutKit/LayoutKit/UI/Views/GalleryView.swift b/LayoutKit/LayoutKit/UI/Views/GalleryView.swift index c219f7ea..e551a6cc 100644 --- a/LayoutKit/LayoutKit/UI/Views/GalleryView.swift +++ b/LayoutKit/LayoutKit/UI/Views/GalleryView.swift @@ -285,17 +285,21 @@ extension GalleryView: ScrollDelegate { break case let .autoPaging(inertionEnabled): guard !inertionEnabled else { return } + let startPage = layout.pageIndex(forContentOffset: scrollStartOffset) let isHorizontal = model.direction.isHorizontal + let resultOffset: CGPoint if isHorizontal { let delta = CGPoint(x: targetContentOffset.pointee.x - scrollStartOffset, y: 0) - let absoluteDelta = CGPoint(x: min(bounds.width, abs(delta.x)), y: 0) let sign = CGPoint(x: delta.x == 0 ? 0 : delta.x / abs(delta.x), y: 1) + let maxDelta = layout.contentOffset(pageIndex: startPage + 1 * sign.x) - scrollStartOffset + let absoluteDelta = CGPoint(x: min(abs(maxDelta), abs(delta.x)), y: 0) resultOffset = CGPoint(x: scrollStartOffset + absoluteDelta.x * sign.x, y: 0) } else { let delta = CGPoint(x: 0, y: targetContentOffset.pointee.y - scrollStartOffset) - let absoluteDelta = CGPoint(x: 0, y: min(bounds.height, abs(delta.y))) let sign = CGPoint(x: 1, y: delta.y == 0 ? 0 : delta.y / abs(delta.y)) + let maxDelta = layout.contentOffset(pageIndex: startPage + 1 * sign.y) - scrollStartOffset + let absoluteDelta = CGPoint(x: 0, y: min(abs(maxDelta), abs(delta.y))) resultOffset = CGPoint(x: 0, y: scrollStartOffset + absoluteDelta.y * sign.y) } targetContentOffset.pointee = resultOffset diff --git a/LayoutKit/LayoutKit/ViewModels/PagerViewLayout.swift b/LayoutKit/LayoutKit/ViewModels/PagerViewLayout.swift index 102bf4c6..9dfd4fcb 100644 --- a/LayoutKit/LayoutKit/ViewModels/PagerViewLayout.swift +++ b/LayoutKit/LayoutKit/ViewModels/PagerViewLayout.swift @@ -121,6 +121,10 @@ extension GalleryViewModel { fitting size: CGSize?, alignment: Alignment ) -> [PagerViewLayout.Page] { + guard let firstFrame = frames.first, let lastFrame = frames.last else { + return [] + } + let bound = (size ?? .zero).dimension(in: direction) let contentSize = contentSize( for: frames, @@ -128,21 +132,27 @@ extension GalleryViewModel { pageIndex: 0 ).dimension(in: direction) - var lastOrigin = 0.0 + let firstFrameOrigin = firstFrame.origin.dimension(in: direction) + let lastFrameOffset = lastGap( + forSize: size, + elementMainAxisSize: lastFrame.size.dimension(in: direction) + ) + var frameOrigin = 0.0 let origins = frames.enumerated().map { index, frame -> CGFloat in guard index > 0 else { return 0.0 } if index == frames.count - 1, transformation?.style != .overlap { - return max(contentSize - bound, lastOrigin) + return max(contentSize - bound, frameOrigin) } let alignmentOffset = alignment.offset( - forAvailableSpace: bound, - contentSize: frame.size.dimension(in: direction) + availableSpace: bound, + contentSize: frame.size.dimension(in: direction), + firstFrameOffset: firstFrameOrigin, + lastFrameOffset: lastFrameOffset ) - - lastOrigin = frame.origin.dimension(in: direction) - alignmentOffset - return max(0, lastOrigin) + frameOrigin = frame.origin.dimension(in: direction) - alignmentOffset + return max(0, frameOrigin) } return (0.. CGFloat { + switch self { + case .leading: + firstFrameOffset + case .center: + ((availableSpace - contentSize) * 0.5).roundedToScreenScale + case .trailing: + availableSpace - contentSize - lastFrameOffset + } + } } extension CGPoint { diff --git a/Specs/DivKit/31.1.0/DivKit.podspec b/Specs/DivKit/31.1.0/DivKit.podspec new file mode 100644 index 00000000..608d8e52 --- /dev/null +++ b/Specs/DivKit/31.1.0/DivKit.podspec @@ -0,0 +1,24 @@ +Pod::Spec.new do |s| + s.name = 'DivKit' + s.version = '31.1.0' + s.summary = 'DivKit framework' + s.description = 'DivKit is a backend-driven UI framework' + s.homepage = 'https://divkit.tech' + + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.author = { 'divkit' => 'divkit@yandex-team.ru' } + s.source = { :git => 'https://github.com/divkit/divkit-ios.git', :tag => s.version.to_s } + + s.swift_version = '5.9' + s.requires_arc = true + s.prefix_header_file = false + s.platforms = { :ios => '13.0' } + + s.dependency 'DivKit_LayoutKit', s.version.to_s + s.dependency 'DivKit_Serialization', s.version.to_s + s.dependency 'VGSL', '~> 6.0' + + s.source_files = [ + 'DivKit/**/*' + ] +end diff --git a/Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec b/Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec new file mode 100644 index 00000000..be409072 --- /dev/null +++ b/Specs/DivKitExtensions/31.1.0/DivKitExtensions.podspec @@ -0,0 +1,22 @@ +Pod::Spec.new do |s| + s.name = 'DivKitExtensions' + s.version = '31.1.0' + s.summary = 'DivKit framework extensions' + s.description = 'Part of DivKit framework' + s.homepage = 'https://divkit.tech' + + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.author = { 'divkit' => 'divkit@yandex-team.ru' } + s.source = { :git => 'https://github.com/divkit/divkit-ios.git', :tag => s.version.to_s } + + s.swift_version = '5.9' + s.requires_arc = true + s.prefix_header_file = false + s.platforms = { :ios => '13.0' } + + s.dependency 'DivKit', s.version.to_s + + s.source_files = [ + 'DivKitExtensions/**/*' + ] +end diff --git a/Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec b/Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec new file mode 100644 index 00000000..a0a893d0 --- /dev/null +++ b/Specs/DivKit_LayoutKit/31.1.0/DivKit_LayoutKit.podspec @@ -0,0 +1,24 @@ +Pod::Spec.new do |s| + s.name = 'DivKit_LayoutKit' + s.module_name = 'LayoutKit' + s.version = '31.1.0' + s.summary = 'Part of DivKit framework' + s.description = 'Part of DivKit framework' + s.homepage = 'https://divkit.tech' + + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.author = { 'divkit' => 'divkit@yandex-team.ru' } + s.source = { :git => 'https://github.com/divkit/divkit-ios.git', :tag => s.version.to_s } + + s.swift_version = '5.9' + s.requires_arc = true + s.prefix_header_file = false + s.platforms = { :ios => '13.0' } + + s.dependency 'DivKit_LayoutKitInterface', s.version.to_s + s.dependency 'VGSL', '~> 6.0' + + s.source_files = [ + 'LayoutKit/LayoutKit/**/*' + ] +end diff --git a/Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec b/Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec new file mode 100644 index 00000000..82352613 --- /dev/null +++ b/Specs/DivKit_LayoutKitInterface/31.1.0/DivKit_LayoutKitInterface.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = 'DivKit_LayoutKitInterface' + s.module_name = 'LayoutKitInterface' + s.version = '31.1.0' + s.summary = 'Part of DivKit framework' + s.description = 'Part of DivKit framework' + s.homepage = 'https://divkit.tech' + + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.author = { 'divkit' => 'divkit@yandex-team.ru' } + s.source = { :git => 'https://github.com/divkit/divkit-ios.git', :tag => s.version.to_s } + + s.swift_version = '5.9' + s.requires_arc = true + s.prefix_header_file = false + s.platforms = { :ios => '13.0' } + + s.dependency 'VGSL', '~> 6.0' + + s.source_files = [ + 'LayoutKit/Interface/**/*' + ] +end diff --git a/Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec b/Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec new file mode 100644 index 00000000..2f68e24e --- /dev/null +++ b/Specs/DivKit_Serialization/31.1.0/DivKit_Serialization.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = 'DivKit_Serialization' + s.module_name = 'Serialization' + s.version = '31.1.0' + s.summary = 'Part of DivKit framework' + s.description = 'Part of DivKit framework' + s.homepage = 'https://divkit.tech' + + s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' } + s.author = { 'divkit' => 'divkit@yandex-team.ru' } + s.source = { :git => 'https://github.com/divkit/divkit-ios.git', :tag => s.version.to_s } + + s.swift_version = '5.9' + s.requires_arc = true + s.prefix_header_file = false + s.platforms = { :ios => '13.0' } + + s.dependency 'VGSL', '~> 6.0' + + s.source_files = [ + 'Serialization/**/*' + ] +end