mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-06-20 18:24:43 +00:00
Merge branches 'master' and 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
@@ -613,7 +613,7 @@ private final class NotificationServiceHandler {
|
||||
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
self.accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false)
|
||||
self.accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false)
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
self.encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
@@ -121,7 +121,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
|
||||
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
|
||||
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false)
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false)
|
||||
self.accountManager = accountManager
|
||||
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
|
||||
@@ -9,5 +9,11 @@ DSYM_PATH="build/artifacts/Telegram.DSYMs.zip"
|
||||
APPCENTER="/usr/local/bin/appcenter"
|
||||
|
||||
$APPCENTER login --token "$API_TOKEN"
|
||||
$APPCENTER distribute release --app "$API_USER_NAME/$API_APP_NAME" -f "$IPA_PATH" -g Internal
|
||||
|
||||
NEXT_WAIT_TIME=0
|
||||
until [ $NEXT_WAIT_TIME -eq 5 ] || $APPCENTER distribute release --app "$API_USER_NAME/$API_APP_NAME" -f "$IPA_PATH" -g Internal; do
|
||||
sleep $(( NEXT_WAIT_TIME++ ))
|
||||
done
|
||||
[ $NEXT_WAIT_TIME -lt 10 ]
|
||||
|
||||
$APPCENTER crashes upload-symbols --app "$API_USER_NAME/$API_APP_NAME" --symbol "$DSYM_PATH"
|
||||
|
||||
+4
-4
@@ -153,7 +153,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
}
|
||||
|
||||
var counterComponents: [String] = []
|
||||
for character in "\(spec.component.count)" {
|
||||
for character in countString(Int64(spec.component.count)) {
|
||||
counterComponents.append(String(character))
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceNode {
|
||||
var previousDisplayCounter: String?
|
||||
if let currentLayout = currentLayout {
|
||||
if currentLayout.spec.component.avatarPeers.isEmpty {
|
||||
previousDisplayCounter = "\(spec.component.count)"
|
||||
previousDisplayCounter = countString(Int64(spec.component.count))
|
||||
}
|
||||
}
|
||||
var currentDisplayCounter: String?
|
||||
if spec.component.avatarPeers.isEmpty {
|
||||
currentDisplayCounter = "\(spec.component.count)"
|
||||
currentDisplayCounter = countString(Int64(spec.component.count))
|
||||
}
|
||||
|
||||
let backgroundImage: UIImage
|
||||
@@ -669,7 +669,7 @@ public final class ReactionButtonComponent: Component {
|
||||
|
||||
self.iconView.frame = CGRect(origin: CGPoint(x: sideInsets, y: floorToScreenPixels((height - imageSize.height) / 2.0)), size: imageSize)
|
||||
|
||||
let text = "\(component.count)"
|
||||
let text = countString(Int64(component.count))
|
||||
var measureText = ""
|
||||
for _ in 0 ..< text.count {
|
||||
measureText.append("0")
|
||||
|
||||
+1
-1
@@ -741,7 +741,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
reactions.insert((nil, totalCount), at: 0)
|
||||
}
|
||||
|
||||
if reactions.count > 2 {
|
||||
if reactions.count > 2 && totalCount > 10 {
|
||||
self.tabListNode = ReactionTabListNode(context: context, availableReactions: availableReactions, reactions: reactions, message: message)
|
||||
}
|
||||
|
||||
|
||||
@@ -1229,12 +1229,21 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
if let presentationNode = self.presentationNode {
|
||||
presentationNode.addRelativeContentOffset(offset, transition: transition)
|
||||
}
|
||||
if self.reactionContextNodeIsAnimatingOut, let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.bounds = reactionContextNode.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
||||
transition.animateOffsetAdditive(node: reactionContextNode, offset: -offset.y)
|
||||
}
|
||||
}
|
||||
|
||||
func cancelReactionAnimation() {
|
||||
if let presentationNode = self.presentationNode {
|
||||
presentationNode.cancelReactionAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutToReaction(value: String, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
if let presentationNode = self.presentationNode {
|
||||
presentationNode.animateOutToReaction(value: value, targetView: targetView, hideNode: hideNode, completion: completion)
|
||||
@@ -2372,6 +2381,10 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
}
|
||||
}
|
||||
|
||||
public func cancelReactionAnimation() {
|
||||
self.controllerNode.cancelReactionAnimation()
|
||||
}
|
||||
|
||||
public func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
self.controllerNode.addRelativeContentOffset(offset, transition: transition)
|
||||
}
|
||||
|
||||
@@ -93,9 +93,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
if highlighted {
|
||||
strongSelf.highlightBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
let previousAlpha = strongSelf.highlightBackgroundNode.alpha
|
||||
strongSelf.highlightBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,14 +244,14 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin
|
||||
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalInset), size: titleSize)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: sideInset, y: titleFrame.maxY + titleSubtitleSpacing), size: subtitleSize)
|
||||
|
||||
transition.updateFrame(node: self.highlightBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: self.highlightBackgroundNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateFrameAdditive(node: self.titleLabelNode, frame: titleFrame)
|
||||
transition.updateFrameAdditive(node: self.subtitleNode, frame: subtitleFrame)
|
||||
|
||||
if let iconSize = iconSize {
|
||||
let iconWidth = max(standardIconWidth, iconSize.width)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: size.width - iconSideInset - iconWidth + floor((iconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame)
|
||||
transition.updateFrame(node: self.iconNode, frame: iconFrame, beginWithCurrentState: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -344,7 +342,7 @@ private final class ContextControllerActionsListCustomItemNode: ASDisplayNode, C
|
||||
let itemLayoutAndApply = itemNode.updateLayout(constrainedWidth: constrainedSize.width, constrainedHeight: constrainedSize.height)
|
||||
|
||||
return (minSize: itemLayoutAndApply.0, apply: { size, transition in
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
itemLayoutAndApply.1(size, transition)
|
||||
})
|
||||
}
|
||||
@@ -493,10 +491,10 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
|
||||
|
||||
let itemSize = CGSize(width: combinedSize.width, height: itemNodeLayout.minSize.height)
|
||||
let itemFrame = CGRect(origin: nextItemOrigin, size: itemSize)
|
||||
itemTransition.updateFrame(node: item.node, frame: itemFrame)
|
||||
itemTransition.updateFrame(node: item.node, frame: itemFrame, beginWithCurrentState: true)
|
||||
|
||||
if let separatorNode = item.separatorNode {
|
||||
itemTransition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: itemFrame.minX, y: itemFrame.maxY), size: CGSize(width: itemFrame.width, height: UIScreenPixel)))
|
||||
itemTransition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: itemFrame.minX, y: itemFrame.maxY), size: CGSize(width: itemFrame.width, height: UIScreenPixel)), beginWithCurrentState: true)
|
||||
if i != self.itemNodes.count - 1 {
|
||||
switch self.items[i + 1] {
|
||||
case .separator:
|
||||
@@ -543,10 +541,9 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack
|
||||
func highlightGestureFinished(performAction: Bool) {
|
||||
if let highlightedItemNode = self.highlightedItemNode {
|
||||
self.highlightedItemNode = nil
|
||||
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
|
||||
if performAction {
|
||||
highlightedItemNode.node.performAction()
|
||||
} else {
|
||||
highlightedItemNode.node.updateIsHighlighted(isHighlighted: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,7 +611,7 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta
|
||||
bottomInset: 0.0,
|
||||
transition: transition
|
||||
)
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: contentLayout.cleanSize))
|
||||
transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: contentLayout.cleanSize), beginWithCurrentState: true)
|
||||
|
||||
return (contentLayout.cleanSize, contentLayout.apparentHeight)
|
||||
}
|
||||
@@ -779,9 +776,9 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
let scale: CGFloat = (size.width - scaleOffset) / size.width
|
||||
let yOffset: CGFloat = size.height * (1.0 - scale)
|
||||
let transitionOffset = (1.0 - transitionFraction) * size.width / 2.0
|
||||
transition.updatePosition(node: self.node, position: CGPoint(x: size.width / 2.0 + scaleOffset / 2.0 + transitionOffset, y: size.height / 2.0 - yOffset / 2.0))
|
||||
transition.updateBounds(node: self.node, bounds: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateTransformScale(node: self.node, scale: scale)
|
||||
transition.updatePosition(node: self.node, position: CGPoint(x: size.width / 2.0 + scaleOffset / 2.0 + transitionOffset, y: size.height / 2.0 - yOffset / 2.0), beginWithCurrentState: true)
|
||||
transition.updateBounds(node: self.node, bounds: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateTransformScale(node: self.node, scale: scale, beginWithCurrentState: true)
|
||||
|
||||
return (size, apparentHeight)
|
||||
}
|
||||
@@ -789,8 +786,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
func updateDimNode(presentationData: PresentationData, size: CGSize, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.dimNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor
|
||||
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction)
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: size), beginWithCurrentState: true)
|
||||
transition.updateAlpha(node: self.dimNode, alpha: 1.0 - transitionFraction, beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
func highlightGestureMoved(location: CGPoint) {
|
||||
@@ -810,6 +807,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
private var itemContainers: [ItemContainer] = []
|
||||
private var dismissingItemContainers: [(container: ItemContainer, isPopped: Bool)] = []
|
||||
|
||||
private var selectionPanGesture: UIPanGestureRecognizer?
|
||||
|
||||
var topReactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? {
|
||||
return self.itemContainers.last?.reactionItems
|
||||
}
|
||||
@@ -850,6 +849,25 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
strongSelf.pop()
|
||||
}
|
||||
|
||||
let selectionPanGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
|
||||
self.selectionPanGesture = selectionPanGesture
|
||||
self.view.addGestureRecognizer(selectionPanGesture)
|
||||
selectionPanGesture.isEnabled = false
|
||||
}
|
||||
|
||||
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .changed:
|
||||
let location = recognizer.location(in: self.view)
|
||||
self.highlightGestureMoved(location: location)
|
||||
case .ended:
|
||||
self.highlightGestureFinished(performAction: true)
|
||||
case .cancelled:
|
||||
self.highlightGestureFinished(performAction: false)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func replace(item: ContextControllerActionsStackItem, animated: Bool) {
|
||||
@@ -1000,7 +1018,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let navigationContainerFrame = CGRect(origin: CGPoint(), size: CGSize(width: topItemWidth, height: max(14 * 2.0, topItemApparentHeight)))
|
||||
transition.updateFrame(node: self.navigationContainer, frame: navigationContainerFrame)
|
||||
transition.updateFrame(node: self.navigationContainer, frame: navigationContainerFrame, beginWithCurrentState: true)
|
||||
self.navigationContainer.update(presentationData: presentationData, size: navigationContainerFrame.size, transition: transition)
|
||||
|
||||
for i in 0 ..< self.itemContainers.count {
|
||||
@@ -1016,7 +1034,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
let itemFrame = CGRect(origin: CGPoint(x: xOffset, y: 0.0), size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.height))
|
||||
|
||||
itemLayouts[i].itemTransition.updateFrame(node: self.itemContainers[i], frame: itemFrame)
|
||||
itemLayouts[i].itemTransition.updateFrame(node: self.itemContainers[i], frame: itemFrame, beginWithCurrentState: true)
|
||||
if itemLayouts[i].animateAppearingContainer {
|
||||
transition.animatePositionAdditive(node: self.itemContainers[i], offset: CGPoint(x: itemFrame.width, y: 0.0))
|
||||
}
|
||||
@@ -1051,4 +1069,10 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
topItemContainer.highlightGestureFinished(performAction: performAction)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePanSelection(isEnabled: Bool) {
|
||||
if let selectionPanGesture = self.selectionPanGesture {
|
||||
selectionPanGesture.isEnabled = isEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.offsetContainerNode)
|
||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
||||
}
|
||||
|
||||
func update(presentationData: PresentationData, size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
func takeContainingNode() {
|
||||
if self.containingNode.contentNode.supernode !== self.offsetContainerNode {
|
||||
self.offsetContainerNode.addSubnode(self.containingNode.contentNode)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
@@ -217,12 +222,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
self.backgroundNode.update(size: layout.size, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
if self.scrollNode.frame != CGRect(origin: CGPoint(), size: layout.size) {
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
}
|
||||
|
||||
if let current = self.contentNode {
|
||||
@@ -328,8 +333,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
if reactionContextNode.frame.isEmpty {
|
||||
reactionContextNodeTransition = .immediate
|
||||
}
|
||||
reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: contentRect, transition: reactionContextNodeTransition)
|
||||
reactionContextNodeTransition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true)
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: UIEdgeInsets(top: topInset, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: contentRect.offsetBy(dx: contentParentGlobalFrame.minX, dy: 0.0), transition: reactionContextNodeTransition)
|
||||
}
|
||||
if let removedReactionContextNode = removedReactionContextNode {
|
||||
removedReactionContextNode.animateOut(to: contentRect, animatingOutToReaction: false)
|
||||
@@ -338,7 +343,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
})
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.contentRectDebugNode, frame: contentRect)
|
||||
transition.updateFrame(node: self.contentRectDebugNode, frame: contentRect, beginWithCurrentState: true)
|
||||
|
||||
var actionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: contentRect.maxY + contentActionsSpacing), size: actionsSize)
|
||||
if self.source.keepInPlace {
|
||||
@@ -365,9 +370,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
actionsFrame.origin.x = actionsEdgeInset
|
||||
}
|
||||
}
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame)
|
||||
transition.updateFrame(node: self.actionsStackNode, frame: actionsFrame, beginWithCurrentState: true)
|
||||
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingNode.contentRect.minX, y: contentRect.minY - contentNode.containingNode.contentRect.minY), size: contentNode.containingNode.bounds.size))
|
||||
contentTransition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentParentGlobalFrame.minX + contentRect.minX - contentNode.containingNode.contentRect.minX, y: contentRect.minY - contentNode.containingNode.contentRect.minY), size: contentNode.containingNode.bounds.size), beginWithCurrentState: true)
|
||||
|
||||
let contentHeight: CGFloat
|
||||
if self.actionsStackNode.topPositionLock != nil {
|
||||
@@ -391,6 +396,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
}
|
||||
|
||||
self.actionsStackNode.updatePanSelection(isEnabled: contentSize.height <= layout.size.height)
|
||||
|
||||
defaultScrollY = contentSize.height - layout.size.height
|
||||
if defaultScrollY < 0.0 {
|
||||
defaultScrollY = 0.0
|
||||
@@ -401,6 +408,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
switch stateTransition {
|
||||
case .animateIn:
|
||||
contentNode.takeContainingNode()
|
||||
|
||||
let duration: Double = 0.42
|
||||
let springDamping: CGFloat = 104.0
|
||||
|
||||
@@ -409,7 +418,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let animateClippingFromContentAreaInScreenSpace = contentNode.animateClippingFromContentAreaInScreenSpace {
|
||||
self.clippingNode.layer.animateFrame(from: animateClippingFromContentAreaInScreenSpace, to: CGRect(origin: CGPoint(), size: layout.size), duration: 0.2)
|
||||
self.clippingNode.layer.animateFrame(from: CGRect(origin: CGPoint(x: 0.0, y: animateClippingFromContentAreaInScreenSpace.minY), size: CGSize(width: layout.size.width, height: animateClippingFromContentAreaInScreenSpace.height)), to: CGRect(origin: CGPoint(), size: layout.size), duration: 0.2)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: animateClippingFromContentAreaInScreenSpace.minY, to: 0.0, duration: 0.2)
|
||||
}
|
||||
|
||||
@@ -519,7 +528,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let putBackInfo = self.source.putBack()
|
||||
|
||||
if let putBackInfo = putBackInfo {
|
||||
self.clippingNode.layer.animateFrame(from: CGRect(origin: CGPoint(), size: layout.size), to: putBackInfo.contentAreaInScreenSpace, duration: duration, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateFrame(from: CGRect(origin: CGPoint(), size: layout.size), to: CGRect(origin: CGPoint(x: 0.0, y: putBackInfo.contentAreaInScreenSpace.minY), size: CGSize(width: layout.size.width, height: putBackInfo.contentAreaInScreenSpace.height)), duration: duration, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: putBackInfo.contentAreaInScreenSpace.minY, duration: duration, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
@@ -644,4 +653,15 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
|
||||
func cancelReactionAnimation() {
|
||||
self.reactionContextNode?.cancelReactionAnimation()
|
||||
}
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition) {
|
||||
if self.reactionContextNodeIsAnimatingOut, let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.bounds = reactionContextNode.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
||||
transition.animateOffsetAdditive(node: reactionContextNode, offset: -offset.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ protocol ContextControllerPresentationNode: ASDisplayNode {
|
||||
)
|
||||
|
||||
func animateOutToReaction(value: String, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void)
|
||||
func cancelReactionAnimation()
|
||||
|
||||
func highlightGestureMoved(location: CGPoint)
|
||||
func highlightGestureFinished(performAction: Bool)
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public extension CGRect {
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransition {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) {
|
||||
if frame.origin.x.isNaN {
|
||||
return
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrameAsPositionAndBounds(node: ASDisplayNode, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -190,7 +190,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateFrameAsPositionAndBounds(layer: CALayer, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateFrameAsPositionAndBounds(layer: CALayer, frame: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if layer.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -261,7 +261,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.bounds.equalTo(bounds) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
@@ -272,7 +272,12 @@ public extension ContainedViewLayoutTransition {
|
||||
completion(true)
|
||||
}
|
||||
case let .animated(duration, curve):
|
||||
let previousBounds = node.bounds
|
||||
let previousBounds: CGRect
|
||||
if beginWithCurrentState, let presentation = node.layer.presentation() {
|
||||
previousBounds = presentation.bounds
|
||||
} else {
|
||||
previousBounds = node.bounds
|
||||
}
|
||||
node.bounds = bounds
|
||||
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, mediaTimingFunction: curve.mediaTimingFunction, force: force, completion: { result in
|
||||
if let completion = completion {
|
||||
@@ -305,7 +310,7 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func updatePosition(node: ASDisplayNode, position: CGPoint, beginWithCurrentState: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
func updatePosition(node: ASDisplayNode, position: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.position.equalTo(position) {
|
||||
completion?(true)
|
||||
} else {
|
||||
|
||||
@@ -1170,7 +1170,7 @@ func debugRestoreState(basePath:String, name: String) {
|
||||
|
||||
private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox")
|
||||
|
||||
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool, useCopy: Bool, useCaches: Bool) -> Signal<PostboxResult, NoError> {
|
||||
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32, isTemporary: Bool, isReadOnly: Bool, useCopy: Bool, useCaches: Bool, removeDatabaseOnError: Bool) -> Signal<PostboxResult, NoError> {
|
||||
let queue = sharedQueue
|
||||
return Signal { subscriber in
|
||||
queue.async {
|
||||
@@ -1214,7 +1214,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
|
||||
postboxLog("openPostbox, initialize SqliteValueBox")
|
||||
|
||||
guard var valueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
guard var valueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
postboxLog("openPostbox, SqliteValueBox upgrading progress \(progress)")
|
||||
subscriber.putNext(.upgrading(progress))
|
||||
}) else {
|
||||
@@ -1242,7 +1242,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
postboxLog("Version \(userVersion) is newer than supported")
|
||||
assertionFailure("Version \(userVersion) is newer than supported")
|
||||
valueBox.drop()
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
subscriber.putNext(.upgrading(progress))
|
||||
}) else {
|
||||
subscriber.putNext(.error)
|
||||
@@ -1266,7 +1266,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
valueBox.internalClose()
|
||||
let _ = try? FileManager.default.removeItem(atPath: dbBasePath)
|
||||
let _ = try? FileManager.default.moveItem(atPath: updatedPath, toPath: dbBasePath)
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
subscriber.putNext(.upgrading(progress))
|
||||
}) else {
|
||||
subscriber.putNext(.error)
|
||||
@@ -1280,7 +1280,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
|
||||
assertionFailure("Couldn't find any upgrade for \(userVersion)")
|
||||
postboxLog("Couldn't find any upgrade for \(userVersion)")
|
||||
valueBox.drop()
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
guard let updatedValueBox = SqliteValueBox(basePath: dbBasePath, queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: encryptionParameters, upgradeProgress: { progress in
|
||||
subscriber.putNext(.upgrading(progress))
|
||||
}) else {
|
||||
subscriber.putNext(.error)
|
||||
|
||||
@@ -46,7 +46,7 @@ struct SqlitePreparedStatement {
|
||||
sqlite3_clear_bindings(statement)
|
||||
}
|
||||
|
||||
func step(handle: OpaquePointer?, _ initial: Bool = false, path: String?) -> Bool {
|
||||
func step(handle: OpaquePointer?, _ initial: Bool = false, pathToRemoveOnError: String?) -> Bool {
|
||||
let res = sqlite3_step(statement)
|
||||
if res != SQLITE_ROW && res != SQLITE_DONE {
|
||||
if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) {
|
||||
@@ -56,7 +56,7 @@ struct SqlitePreparedStatement {
|
||||
}
|
||||
|
||||
if res == SQLITE_CORRUPT {
|
||||
if let path = path {
|
||||
if let path = pathToRemoveOnError {
|
||||
postboxLog("Corrupted DB at step, dropping")
|
||||
try? FileManager.default.removeItem(atPath: path)
|
||||
preconditionFailure()
|
||||
@@ -70,7 +70,7 @@ struct SqlitePreparedStatement {
|
||||
var code: Int32
|
||||
}
|
||||
|
||||
func tryStep(handle: OpaquePointer?, _ initial: Bool = false, path: String?) -> Result<Void, SqlError> {
|
||||
func tryStep(handle: OpaquePointer?, _ initial: Bool = false, pathToRemoveOnError: String?) -> Result<Void, SqlError> {
|
||||
let res = sqlite3_step(statement)
|
||||
if res != SQLITE_ROW && res != SQLITE_DONE {
|
||||
if let error = sqlite3_errmsg(handle), let str = NSString(utf8String: error) {
|
||||
@@ -80,7 +80,7 @@ struct SqlitePreparedStatement {
|
||||
}
|
||||
|
||||
if res == SQLITE_CORRUPT {
|
||||
if let path = path {
|
||||
if let path = pathToRemoveOnError {
|
||||
postboxLog("Corrupted DB at step, dropping")
|
||||
try? FileManager.default.removeItem(atPath: path)
|
||||
preconditionFailure()
|
||||
@@ -167,6 +167,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
private let inMemory: Bool
|
||||
private let encryptionParameters: ValueBoxEncryptionParameters?
|
||||
private let databasePath: String
|
||||
private let removeDatabaseOnError: Bool
|
||||
private var database: Database!
|
||||
private var tables: [Int32: SqliteValueBoxTable] = [:]
|
||||
private var fullTextTables: [Int32: ValueBoxFullTextTable] = [:]
|
||||
@@ -202,11 +203,12 @@ public final class SqliteValueBox: ValueBox {
|
||||
|
||||
private let queue: Queue
|
||||
|
||||
public init?(basePath: String, queue: Queue, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void, inMemory: Bool = false) {
|
||||
public init?(basePath: String, queue: Queue, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool, removeDatabaseOnError: Bool, encryptionParameters: ValueBoxEncryptionParameters?, upgradeProgress: (Float) -> Void, inMemory: Bool = false) {
|
||||
self.basePath = basePath
|
||||
self.isTemporary = isTemporary
|
||||
self.isReadOnly = isReadOnly
|
||||
self.useCaches = useCaches
|
||||
self.removeDatabaseOnError = removeDatabaseOnError
|
||||
self.inMemory = inMemory
|
||||
self.encryptionParameters = encryptionParameters
|
||||
self.databasePath = basePath + "/db_sqlite"
|
||||
@@ -294,7 +296,9 @@ public final class SqliteValueBox: ValueBox {
|
||||
preconditionFailure("Don't have write access to database folder")
|
||||
}
|
||||
|
||||
let _ = try? FileManager.default.removeItem(atPath: path)
|
||||
if self.removeDatabaseOnError {
|
||||
let _ = try? FileManager.default.removeItem(atPath: path)
|
||||
}
|
||||
preconditionFailure("Couldn't open database")
|
||||
}
|
||||
|
||||
@@ -328,7 +332,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if self.isEncrypted(database) {
|
||||
postboxLog("Encryption key is invalid")
|
||||
|
||||
if isTemporary || isReadOnly {
|
||||
if isTemporary || isReadOnly || !self.removeDatabaseOnError {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -347,7 +351,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
} else {
|
||||
postboxLog("Encryption key is required")
|
||||
if isReadOnly {
|
||||
if isReadOnly || !self.removeDatabaseOnError {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -548,13 +552,16 @@ public final class SqliteValueBox: ValueBox {
|
||||
postboxLog("isEncrypted prepare...")
|
||||
|
||||
let allIsOk = Atomic<Bool>(value: false)
|
||||
let removeDatabaseOnError = self.removeDatabaseOnError
|
||||
let databasePath = self.databasePath
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 5.0, execute: {
|
||||
if allIsOk.with({ $0 }) == false {
|
||||
postboxLog("Timeout reached, discarding database")
|
||||
try? FileManager.default.removeItem(atPath: databasePath)
|
||||
if removeDatabaseOnError {
|
||||
try? FileManager.default.removeItem(atPath: databasePath)
|
||||
}
|
||||
|
||||
exit(0)
|
||||
preconditionFailure()
|
||||
}
|
||||
})
|
||||
let status = sqlite3_prepare_v2(database.handle, "SELECT * FROM sqlite_master LIMIT 1", -1, &statement, nil)
|
||||
@@ -569,7 +576,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
return true
|
||||
}
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
switch preparedStatement.tryStep(handle: database.handle, path: self.databasePath) {
|
||||
switch preparedStatement.tryStep(handle: database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
case .success:
|
||||
break
|
||||
case let .failure(error):
|
||||
@@ -588,7 +595,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
let status = sqlite3_prepare_v2(database.handle, "PRAGMA user_version", -1, &statement, nil)
|
||||
precondition(status == SQLITE_OK)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
let _ = preparedStatement.step(handle: database.handle, path: self.databasePath)
|
||||
let _ = preparedStatement.step(handle: database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil)
|
||||
let value = preparedStatement.int64At(0)
|
||||
preparedStatement.destroy()
|
||||
return value
|
||||
@@ -601,7 +608,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
precondition(status == SQLITE_OK)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
var result: String?
|
||||
if preparedStatement.step(handle: database.handle, path: self.databasePath) {
|
||||
if preparedStatement.step(handle: database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
result = preparedStatement.stringAt(0)
|
||||
}
|
||||
preparedStatement.destroy()
|
||||
@@ -616,7 +623,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
var tables: [SqliteValueBoxTable] = []
|
||||
|
||||
while preparedStatement.step(handle: database.handle, true, path: self.databasePath) {
|
||||
while preparedStatement.step(handle: database.handle, true, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
guard let name = preparedStatement.stringAt(0) else {
|
||||
assertionFailure()
|
||||
continue
|
||||
@@ -663,7 +670,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
var tables: [ValueBoxFullTextTable] = []
|
||||
|
||||
while preparedStatement.step(handle: database.handle, true, path: self.databasePath) {
|
||||
while preparedStatement.step(handle: database.handle, true, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let value = preparedStatement.int64At(0)
|
||||
tables.append(ValueBoxFullTextTable(id: Int32(value)))
|
||||
}
|
||||
@@ -1475,7 +1482,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
|
||||
var buffer: ReadBuffer?
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
buffer = statement.valueAt(0)
|
||||
break
|
||||
}
|
||||
@@ -1495,7 +1502,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement = self.getRowIdStatement(table, key: key)
|
||||
|
||||
if statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
if statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let rowId = statement.int64At(0)
|
||||
var blobHandle: OpaquePointer?
|
||||
sqlite3_blob_open(database.handle, "main", "t\(table.id)", "value", rowId, 0, &blobHandle)
|
||||
@@ -1515,7 +1522,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement = self.getRowIdStatement(table, key: key)
|
||||
|
||||
if statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
if statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let rowId = statement.int64At(0)
|
||||
var blobHandle: OpaquePointer?
|
||||
sqlite3_blob_open(database.handle, "main", "t\(table.id)", "value", rowId, 1, &blobHandle)
|
||||
@@ -1566,7 +1573,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.keyAt(0)
|
||||
let value = statement.valueAt(1)
|
||||
|
||||
@@ -1591,7 +1598,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.int64KeyAt(0)
|
||||
let value = statement.valueAt(1)
|
||||
|
||||
@@ -1702,7 +1709,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.keyAt(0)
|
||||
|
||||
if !keys(key) {
|
||||
@@ -1726,7 +1733,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.int64KeyAt(0)
|
||||
|
||||
if !keys(key) {
|
||||
@@ -1748,7 +1755,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement: SqlitePreparedStatement = self.scanStatement(table)
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.keyAt(0)
|
||||
let value = statement.valueAt(1)
|
||||
|
||||
@@ -1767,7 +1774,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement: SqlitePreparedStatement = self.scanKeysStatement(table)
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.keyAt(0)
|
||||
|
||||
if !keys(key) {
|
||||
@@ -1785,7 +1792,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement: SqlitePreparedStatement = self.scanStatement(table)
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.int64KeyValueAt(0)
|
||||
let value = statement.valueAt(1)
|
||||
|
||||
@@ -1804,7 +1811,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement: SqlitePreparedStatement = self.scanKeysStatement(table)
|
||||
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let key = statement.int64KeyValueAt(0)
|
||||
|
||||
if !keys(key) {
|
||||
@@ -1822,18 +1829,18 @@ public final class SqliteValueBox: ValueBox {
|
||||
|
||||
if sqliteTable.hasPrimaryKey {
|
||||
let statement = self.insertOrReplaceStatement(sqliteTable, key: key, value: value)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
} else {
|
||||
if self.exists(table, key: key) {
|
||||
let statement = self.updateStatement(table, key: key, value: value)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
} else {
|
||||
let statement = self.insertOrReplaceStatement(sqliteTable, key: key, value: value)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1850,7 +1857,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
|
||||
let statement = self.deleteStatement(table, key: key)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1860,7 +1867,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
precondition(self.queue.isCurrent())
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement = self.rangeDeleteStatement(table, start: min(start, end), end: max(start, end))
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1870,7 +1877,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
precondition(self.queue.isCurrent())
|
||||
if let _ = self.tables[table.id] {
|
||||
let statement = self.moveStatement(table, from: previousKey, to: updatedKey)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1880,7 +1887,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
precondition(self.queue.isCurrent())
|
||||
if let _ = self.tables[fromTable.id] {
|
||||
let statement = self.copyStatement(fromTable: fromTable, fromKey: fromKey, toTable: toTable, toKey: toKey)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1916,7 +1923,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
|
||||
if let statement = statement {
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let resultCollectionId = statement.stringAt(0)
|
||||
let resultItemId = statement.stringAt(1)
|
||||
|
||||
@@ -1942,7 +1949,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
|
||||
let statement = self.fullTextInsertStatement(table, collectionId: collectionIdData, itemId: itemIdData, contents: contentsData, tags: tagsData)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1960,7 +1967,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
|
||||
let statement = self.fullTextDeleteStatement(table, itemId: itemIdData)
|
||||
while statement.step(handle: self.database.handle, path: self.databasePath) {
|
||||
while statement.step(handle: self.database.handle, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
@@ -1987,7 +1994,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
}
|
||||
|
||||
var result = 0
|
||||
while statement.step(handle: database.handle, true, path: self.databasePath) {
|
||||
while statement.step(handle: database.handle, true, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let value = statement.int32At(0)
|
||||
result = Int(value)
|
||||
}
|
||||
@@ -2005,7 +2012,7 @@ public final class SqliteValueBox: ValueBox {
|
||||
let statement = SqlitePreparedStatement(statement: statementImpl)
|
||||
|
||||
var result = 0
|
||||
while statement.step(handle: database.handle, true, path: self.databasePath) {
|
||||
while statement.step(handle: database.handle, true, pathToRemoveOnError: self.removeDatabaseOnError ? self.databasePath : nil) {
|
||||
let value = statement.int32At(0)
|
||||
result = Int(value)
|
||||
}
|
||||
|
||||
@@ -7,11 +7,6 @@ import TelegramPresentationData
|
||||
import AccountContext
|
||||
import TelegramAnimatedStickerNode
|
||||
|
||||
public enum ReactionGestureItem {
|
||||
case like
|
||||
case unlike
|
||||
}
|
||||
|
||||
public final class ReactionContextItem {
|
||||
public struct Reaction: Equatable {
|
||||
public var rawValue: String
|
||||
@@ -118,6 +113,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
public var reactionSelected: ((ReactionContextItem) -> Void)?
|
||||
|
||||
private var hapticFeedback: HapticFeedback?
|
||||
private var standaloneReactionAnimation: StandaloneReactionAnimation?
|
||||
|
||||
private weak var animationTargetView: UIView?
|
||||
private var animationHideNode: Bool = false
|
||||
|
||||
public init(context: AccountContext, theme: PresentationTheme, items: [ReactionContextItem]) {
|
||||
self.theme = theme
|
||||
@@ -489,10 +488,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
itemNode.layer.animatePosition(from: itemNode.layer.position, to: targetPosition, duration: duration, removeOnCompletion: false)
|
||||
targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.8)
|
||||
targetSnapshotView.layer.animatePosition(from: sourceFrame.center, to: targetPosition, duration: duration, removeOnCompletion: false)
|
||||
targetSnapshotView.layer.animateScale(from: itemNode.bounds.width / targetSnapshotView.bounds.width, to: 1.0, duration: duration, removeOnCompletion: false, completion: { [weak self, weak targetSnapshotView] _ in
|
||||
if let _ = self {
|
||||
//strongSelf.hapticFeedback.tap()
|
||||
}
|
||||
targetSnapshotView.layer.animateScale(from: itemNode.bounds.width / targetSnapshotView.bounds.width, to: 1.0, duration: duration, removeOnCompletion: false, completion: { [weak targetSnapshotView] _ in
|
||||
completedTarget = true
|
||||
intermediateCompletion()
|
||||
|
||||
@@ -500,11 +496,9 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
if hideNode {
|
||||
targetView.isHidden = false
|
||||
/*targetView.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0, completion: { _ in*/
|
||||
targetSnapshotView?.isHidden = true
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
//})
|
||||
targetSnapshotView?.isHidden = true
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
} else {
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
@@ -528,6 +522,18 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if itemNode.item.reaction.rawValue != value {
|
||||
continue
|
||||
}
|
||||
|
||||
self.animationTargetView = targetView
|
||||
self.animationHideNode = hideNode
|
||||
|
||||
/*let standaloneReactionAnimation = StandaloneReactionAnimation()
|
||||
self.standaloneReactionAnimation = standaloneReactionAnimation
|
||||
standaloneReactionAnimation.frame = self.bounds
|
||||
self.addSubnode(standaloneReactionAnimation)
|
||||
standaloneReactionAnimation.animateReactionSelection(context: itemNode.context, theme: self.theme, reaction: itemNode.item, targetView: targetView, currentItemNode: itemNode, hideNode: hideNode, completion: completion)
|
||||
|
||||
return*/
|
||||
|
||||
if hideNode {
|
||||
targetView.isHidden = true
|
||||
}
|
||||
@@ -662,6 +668,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func cancelReactionAnimation() {
|
||||
self.standaloneReactionAnimation?.cancel()
|
||||
|
||||
if let animationTargetView = self.animationTargetView, self.animationHideNode {
|
||||
animationTargetView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
public func setHighlightedReaction(_ value: ReactionContextItem.Reaction?) {
|
||||
self.highlightedReaction = value
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
@@ -678,29 +692,47 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
private let itemNode: ReactionNode
|
||||
private var itemNode: ReactionNode? = nil
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
private var isCancelled: Bool = false
|
||||
|
||||
public init(context: AccountContext, theme: PresentationTheme, reaction: ReactionContextItem) {
|
||||
self.itemNode = ReactionNode(context: context, theme: theme, item: reaction)
|
||||
|
||||
private weak var targetView: UIView?
|
||||
private var hideNode: Bool = false
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
self.addSubnode(self.itemNode)
|
||||
}
|
||||
|
||||
public func animateReactionSelection(targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, reaction: ReactionContextItem, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
self.animateReactionSelection(context: context, theme: theme, reaction: reaction, targetView: targetView, currentItemNode: nil, hideNode: hideNode, completion: completion)
|
||||
}
|
||||
|
||||
func animateReactionSelection(context: AccountContext, theme: PresentationTheme, reaction: ReactionContextItem, targetView: UIView, currentItemNode: ReactionNode?, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
guard let sourceSnapshotView = targetView.snapshotContentTree() else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
self.targetView = targetView
|
||||
self.hideNode = hideNode
|
||||
|
||||
let itemNode: ReactionNode
|
||||
if let currentItemNode = currentItemNode {
|
||||
itemNode = currentItemNode
|
||||
} else {
|
||||
itemNode = ReactionNode(context: context, theme: theme, item: reaction)
|
||||
}
|
||||
self.itemNode = itemNode
|
||||
|
||||
self.addSubnode(itemNode)
|
||||
|
||||
if hideNode {
|
||||
targetView.isHidden = true
|
||||
}
|
||||
|
||||
self.itemNode.isExtracted = true
|
||||
itemNode.isExtracted = true
|
||||
let sourceItemSize: CGFloat = 40.0
|
||||
let selfTargetRect = self.view.convert(targetView.bounds, from: targetView)
|
||||
|
||||
@@ -732,7 +764,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
let animationFrame = expandedFrame.insetBy(dx: -expandedFrame.width * 0.5, dy: -expandedFrame.height * 0.5)
|
||||
.offsetBy(dx: incomingMessage ? (expandedFrame.width - 50.0) : (-expandedFrame.width + 50.0), dy: 0.0)
|
||||
|
||||
additionalAnimationNode.setup(source: AnimatedStickerResourceSource(account: self.itemNode.context.account, resource: self.itemNode.item.applicationAnimation.resource), width: Int(animationFrame.width * 2.0), height: Int(animationFrame.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: self.itemNode.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(self.itemNode.item.applicationAnimation.resource.id)))
|
||||
additionalAnimationNode.setup(source: AnimatedStickerResourceSource(account: itemNode.context.account, resource: itemNode.item.applicationAnimation.resource), width: Int(animationFrame.width * 2.0), height: Int(animationFrame.height * 2.0), playbackMode: .once, mode: .direct(cachePathPrefix: itemNode.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(itemNode.item.applicationAnimation.resource.id)))
|
||||
additionalAnimationNode.frame = animationFrame
|
||||
if incomingMessage {
|
||||
additionalAnimationNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
@@ -750,9 +782,28 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var didBeginDismissAnimation = false
|
||||
let beginDismissAnimation: () -> Void = { [weak self] in
|
||||
if !didBeginDismissAnimation {
|
||||
didBeginDismissAnimation = true
|
||||
|
||||
guard let strongSelf = self else {
|
||||
mainAnimationCompleted = true
|
||||
intermediateCompletion()
|
||||
return
|
||||
}
|
||||
strongSelf.animateFromItemNodeToReaction(itemNode: itemNode, targetView: targetView, hideNode: hideNode, completion: {
|
||||
mainAnimationCompleted = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
additionalAnimationNode.completed = { _ in
|
||||
additionalAnimationCompleted = true
|
||||
intermediateCompletion()
|
||||
|
||||
beginDismissAnimation()
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1 * UIView.animationDurationFactor(), execute: {
|
||||
@@ -760,11 +811,9 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
})
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0, execute: {
|
||||
self.animateFromItemNodeToReaction(itemNode: self.itemNode, targetView: targetView, hideNode: hideNode, completion: {
|
||||
mainAnimationCompleted = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
beginDismissAnimation()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private func animateFromItemNodeToReaction(itemNode: ReactionNode, targetView: UIView, hideNode: Bool, completion: @escaping () -> Void) {
|
||||
@@ -795,9 +844,6 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.8)
|
||||
targetSnapshotView.layer.animatePosition(from: sourceFrame.center, to: targetPosition, duration: duration, removeOnCompletion: false)
|
||||
targetSnapshotView.layer.animateScale(from: itemNode.bounds.width / targetSnapshotView.bounds.width, to: 1.0, duration: duration, removeOnCompletion: false, completion: { [weak targetSnapshotView] _ in
|
||||
/*if let strongSelf = self {
|
||||
strongSelf.hapticFeedback.tap()
|
||||
}*/
|
||||
completedTarget = true
|
||||
intermediateCompletion()
|
||||
|
||||
@@ -805,11 +851,9 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
|
||||
if hideNode {
|
||||
targetView.isHidden = false
|
||||
/*targetView.layer.animateSpring(from: 0.5 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: duration, initialVelocity: 0.0, damping: 90.0, completion: { _ in*/
|
||||
targetSnapshotView?.isHidden = true
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
//})
|
||||
targetSnapshotView?.isHidden = true
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
} else {
|
||||
targetScaleCompleted = true
|
||||
intermediateCompletion()
|
||||
@@ -823,6 +867,14 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
self.bounds = self.bounds.offsetBy(dx: 0.0, dy: offset.y)
|
||||
transition.animateOffsetAdditive(node: self, offset: -offset.y)
|
||||
}
|
||||
|
||||
public func cancel() {
|
||||
self.isCancelled = true
|
||||
|
||||
if let targetView = self.targetView, self.hideNode {
|
||||
targetView.isHidden = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class StandaloneDismissReactionAnimation: ASDisplayNode {
|
||||
|
||||
@@ -132,6 +132,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
for reaction in availableReactions.reactions {
|
||||
if reaction.value == updatedReaction {
|
||||
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
|
||||
standaloneReactionAnimation.cancel()
|
||||
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
|
||||
standaloneReactionAnimation?.removeFromSupernode()
|
||||
})
|
||||
@@ -139,19 +140,24 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
if let supernode = self.supernode {
|
||||
let standaloneReactionAnimation = StandaloneReactionAnimation(context: item.context, theme: item.theme, reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
))
|
||||
let standaloneReactionAnimation = StandaloneReactionAnimation()
|
||||
self.standaloneReactionAnimation = standaloneReactionAnimation
|
||||
|
||||
supernode.addSubnode(standaloneReactionAnimation)
|
||||
standaloneReactionAnimation.frame = supernode.bounds
|
||||
standaloneReactionAnimation.animateReactionSelection(targetView: targetView, hideNode: true, completion: { [weak standaloneReactionAnimation] in
|
||||
standaloneReactionAnimation.animateReactionSelection(
|
||||
context: item.context, theme: item.theme, reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
),
|
||||
targetView: targetView,
|
||||
hideNode: true,
|
||||
completion: { [weak standaloneReactionAnimation] in
|
||||
standaloneReactionAnimation?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
@@ -173,7 +173,8 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
|
||||
isTemporary: false,
|
||||
isReadOnly: false,
|
||||
useCopy: false,
|
||||
useCaches: !supplementary
|
||||
useCaches: !supplementary,
|
||||
removeDatabaseOnError: !supplementary
|
||||
)
|
||||
|
||||
return postbox
|
||||
@@ -1295,7 +1296,8 @@ public func standaloneStateManager(
|
||||
isTemporary: false,
|
||||
isReadOnly: false,
|
||||
useCopy: false,
|
||||
useCaches: false
|
||||
useCaches: false,
|
||||
removeDatabaseOnError: false
|
||||
)
|
||||
|
||||
return postbox
|
||||
|
||||
@@ -73,7 +73,7 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate init?(queue: Queue, basePath: String, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool, temporarySessionId: Int64) {
|
||||
fileprivate init?(queue: Queue, basePath: String, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool, removeDatabaseOnError: Bool, temporarySessionId: Int64) {
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
self.queue = queue
|
||||
@@ -82,11 +82,11 @@ final class AccountManagerImpl<Types: AccountManagerTypes> {
|
||||
self.loginTokensPath = "\(basePath)/login-tokens"
|
||||
self.temporarySessionId = temporarySessionId
|
||||
let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil)
|
||||
guard let guardValueBox = SqliteValueBox(basePath: basePath + "/guard_db", queue: queue, isTemporary: isTemporary, isReadOnly: false, useCaches: useCaches, encryptionParameters: nil, upgradeProgress: { _ in }) else {
|
||||
guard let guardValueBox = SqliteValueBox(basePath: basePath + "/guard_db", queue: queue, isTemporary: isTemporary, isReadOnly: false, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: nil, upgradeProgress: { _ in }) else {
|
||||
return nil
|
||||
}
|
||||
self.guardValueBox = guardValueBox
|
||||
guard let valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, encryptionParameters: nil, upgradeProgress: { _ in }) else {
|
||||
guard let valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, encryptionParameters: nil, upgradeProgress: { _ in }) else {
|
||||
return nil
|
||||
}
|
||||
self.valueBox = valueBox
|
||||
@@ -510,7 +510,7 @@ public final class AccountManager<Types: AccountManagerTypes> {
|
||||
return AccountManagerImpl<Types>.getCurrentRecords(basePath: basePath)
|
||||
}
|
||||
|
||||
public init(basePath: String, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool) {
|
||||
public init(basePath: String, isTemporary: Bool, isReadOnly: Bool, useCaches: Bool, removeDatabaseOnError: Bool) {
|
||||
self.queue = sharedQueue
|
||||
self.basePath = basePath
|
||||
var temporarySessionId: Int64 = 0
|
||||
@@ -518,7 +518,7 @@ public final class AccountManager<Types: AccountManagerTypes> {
|
||||
self.temporarySessionId = temporarySessionId
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
if let value = AccountManagerImpl<Types>(queue: queue, basePath: basePath, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, temporarySessionId: temporarySessionId) {
|
||||
if let value = AccountManagerImpl<Types>(queue: queue, basePath: basePath, isTemporary: isTemporary, isReadOnly: isReadOnly, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError, temporarySessionId: temporarySessionId) {
|
||||
return value
|
||||
} else {
|
||||
preconditionFailure()
|
||||
|
||||
+2
-2
@@ -97,9 +97,9 @@ public enum AccountTransactionError {
|
||||
case couldNotOpen
|
||||
}
|
||||
|
||||
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, useCopy: Bool = false, useCaches: Bool = true, transaction: @escaping (Postbox, Transaction) -> T) -> Signal<T, AccountTransactionError> {
|
||||
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, isReadOnly: Bool, useCopy: Bool = false, useCaches: Bool = true, removeDatabaseOnError: Bool = true, transaction: @escaping (Postbox, Transaction) -> T) -> Signal<T, AccountTransactionError> {
|
||||
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
|
||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly, useCopy: useCopy, useCaches: useCaches)
|
||||
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970), isTemporary: true, isReadOnly: isReadOnly, useCopy: useCopy, useCaches: useCaches, removeDatabaseOnError: removeDatabaseOnError)
|
||||
return postbox
|
||||
|> castError(AccountTransactionError.self)
|
||||
|> mapToSignal { value -> Signal<T, AccountTransactionError> in
|
||||
|
||||
@@ -95,3 +95,31 @@ public func dataSizeString(_ size: Int64, forceDecimal: Bool = false, formatting
|
||||
return formatting.byte("\(size)").string
|
||||
}
|
||||
}
|
||||
|
||||
public func countString(_ count: Int64, forceDecimal: Bool = false) -> String {
|
||||
let decimalSeparator = "."
|
||||
if count >= 1000 * 1000 * 1000 {
|
||||
let remainder = Int64((Double(count % (1000 * 1000 * 1000)) / (1000 * 1000 * 100.0)).rounded(.down))
|
||||
if remainder != 0 || forceDecimal {
|
||||
return "\(count / (1000 * 1000 * 1000))\(decimalSeparator)\(remainder)T"
|
||||
} else {
|
||||
return "\(count / (1000 * 1000 * 1000))T"
|
||||
}
|
||||
} else if count >= 1000 * 1000 {
|
||||
let remainder = Int64((Double(count % (1000 * 1000)) / (1000.0 * 100.0)).rounded(.down))
|
||||
if remainder != 0 || forceDecimal {
|
||||
return "\(count / (1000 * 1000))\(decimalSeparator)\(remainder)M"
|
||||
} else {
|
||||
return "\(count / (1000 * 1000))M"
|
||||
}
|
||||
} else if count >= 1000 {
|
||||
let remainder = (count % (1000)) / (102)
|
||||
if remainder != 0 || forceDecimal {
|
||||
return "\(count / 1000)\(decimalSeparator)\(remainder)K"
|
||||
} else {
|
||||
return "\(count / 1000)K"
|
||||
}
|
||||
} else {
|
||||
return "\(count)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +705,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
UINavigationController.attemptRotationToDeviceOrientation()
|
||||
})
|
||||
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true)
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: false, isReadOnly: false, useCaches: true, removeDatabaseOnError: true)
|
||||
self.accountManager = accountManager
|
||||
|
||||
telegramUIDeclareEncodables()
|
||||
|
||||
@@ -429,12 +429,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private weak var currentPinchController: PinchController?
|
||||
private weak var currentPinchSourceItemNode: ListViewItemNode?
|
||||
|
||||
private weak var currentReactionContextController: ContextController?
|
||||
private weak var currentReactionContextItemNode: ListViewItemNode?
|
||||
|
||||
private weak var currentStandaloneReactionAnimation: StandaloneReactionAnimation?
|
||||
private weak var currentStandaloneReactionItemNode: ListViewItemNode?
|
||||
|
||||
private var screenCaptureManager: ScreenCaptureDetectionManager?
|
||||
private let chatAdditionalDataDisposable = MetaDisposable()
|
||||
|
||||
@@ -594,6 +588,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self else {
|
||||
return true
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
if strongSelf.presentVoiceMessageDiscardAlert(action: action, performAction: false) {
|
||||
return false
|
||||
}
|
||||
@@ -1025,6 +1022,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), recognizer: recognizer, gesture: gesture)
|
||||
strongSelf.currentContextController = controller
|
||||
|
||||
@@ -1059,8 +1058,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
if let itemNode = itemNode, let targetView = itemNode.targetReactionView(value: updatedReaction) {
|
||||
strongSelf.currentReactionContextController = controller
|
||||
strongSelf.currentReactionContextItemNode = itemNode
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageContextController(messageId: item.message.id, contextController: controller)
|
||||
|
||||
controller.dismissWithReaction(value: updatedReaction, targetView: targetView, hideNode: true, completion: { [weak itemNode, weak targetView] in
|
||||
guard let strongSelf = self, let itemNode = itemNode, let targetView = targetView else {
|
||||
@@ -1116,6 +1114,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
})))
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageReactionContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, contentNode: sourceNode)), items: .single(items), recognizer: nil, gesture: gesture)
|
||||
|
||||
dismissController = { [weak controller] completion in
|
||||
@@ -1238,44 +1238,54 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let itemNode = itemNode, let item = itemNode.item, let availableReactions = item.associatedData.availableReactions, let targetView = itemNode.targetReactionView(value: updatedReaction) {
|
||||
for reaction in availableReactions.reactions {
|
||||
if reaction.value == updatedReaction {
|
||||
let standaloneReactionAnimation = StandaloneReactionAnimation(context: strongSelf.context, theme: strongSelf.presentationData.theme, reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
))
|
||||
let standaloneReactionAnimation = StandaloneReactionAnimation()
|
||||
|
||||
strongSelf.currentStandaloneReactionAnimation = standaloneReactionAnimation
|
||||
strongSelf.currentStandaloneReactionItemNode = itemNode
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addMessageStandaloneReactionAnimation(messageId: item.message.id, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||
|
||||
strongSelf.chatDisplayNode.addSubnode(standaloneReactionAnimation)
|
||||
standaloneReactionAnimation.frame = strongSelf.chatDisplayNode.bounds
|
||||
standaloneReactionAnimation.animateReactionSelection(targetView: targetView, hideNode: true, completion: { [weak standaloneReactionAnimation] in
|
||||
standaloneReactionAnimation?.removeFromSupernode()
|
||||
})
|
||||
standaloneReactionAnimation.animateReactionSelection(
|
||||
context: strongSelf.context,
|
||||
theme: strongSelf.presentationData.theme,
|
||||
reaction: ReactionContextItem(
|
||||
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
|
||||
stillAnimation: reaction.selectAnimation,
|
||||
listAnimation: reaction.activateAnimation,
|
||||
applicationAnimation: reaction.effectAnimation
|
||||
),
|
||||
targetView: targetView,
|
||||
hideNode: true,
|
||||
completion: { [weak standaloneReactionAnimation] in
|
||||
standaloneReactionAnimation?.removeFromSupernode()
|
||||
}
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if let removedReaction = removedReaction, let targetView = itemNode.targetReactionView(value: removedReaction), shouldDisplayInlineDateReactions(message: message) {
|
||||
var hideRemovedReaction: Bool = false
|
||||
if let reactions = mergedMessageReactions(attributes: message.attributes) {
|
||||
for reaction in reactions.reactions {
|
||||
if reaction.value == removedReaction {
|
||||
hideRemovedReaction = reaction.count == 1
|
||||
break
|
||||
} else {
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts(itemNode: itemNode)
|
||||
|
||||
if let removedReaction = removedReaction, let targetView = itemNode.targetReactionView(value: removedReaction), shouldDisplayInlineDateReactions(message: message) {
|
||||
var hideRemovedReaction: Bool = false
|
||||
if let reactions = mergedMessageReactions(attributes: message.attributes) {
|
||||
for reaction in reactions.reactions {
|
||||
if reaction.value == removedReaction {
|
||||
hideRemovedReaction = reaction.count == 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let standaloneDismissAnimation = StandaloneDismissReactionAnimation()
|
||||
standaloneDismissAnimation.frame = strongSelf.chatDisplayNode.bounds
|
||||
strongSelf.chatDisplayNode.addSubnode(standaloneDismissAnimation)
|
||||
standaloneDismissAnimation.animateReactionDismiss(sourceView: targetView, hideNode: hideRemovedReaction, completion: { [weak standaloneDismissAnimation] in
|
||||
standaloneDismissAnimation?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
let standaloneDismissAnimation = StandaloneDismissReactionAnimation()
|
||||
standaloneDismissAnimation.frame = strongSelf.chatDisplayNode.bounds
|
||||
strongSelf.chatDisplayNode.addSubnode(standaloneDismissAnimation)
|
||||
standaloneDismissAnimation.animateReactionDismiss(sourceView: targetView, hideNode: hideRemovedReaction, completion: { [weak standaloneDismissAnimation] in
|
||||
standaloneDismissAnimation?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
let _ = updateMessageReactionsInteractively(account: strongSelf.context.account, messageId: message.id, reaction: updatedReaction).start()
|
||||
@@ -2471,6 +2481,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})))
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil)
|
||||
strongSelf.currentContextController = controller
|
||||
strongSelf.forEachController({ controller in
|
||||
@@ -2548,6 +2560,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
f(.dismissWithoutContent)
|
||||
})))
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: topMessage, selectAll: true)), items: .single(ContextController.Items(content: .list(actions))), recognizer: nil)
|
||||
strongSelf.currentContextController = controller
|
||||
strongSelf.forEachController({ controller in
|
||||
@@ -2981,6 +2995,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return items
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
})
|
||||
@@ -3287,6 +3303,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return items
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: galleryController, sourceNode: node, passthroughTouches: false)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
@@ -4888,26 +4906,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
if let reactionItemNode = strongSelf.currentReactionContextItemNode, let currentReactionContextController = strongSelf.currentReactionContextController {
|
||||
if let itemNode = itemNode {
|
||||
if itemNode === reactionItemNode {
|
||||
currentReactionContextController.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
} else {
|
||||
currentReactionContextController.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
if let standaloneReactionItemNode = strongSelf.currentStandaloneReactionItemNode, let currentStandaloneReactionAnimation = strongSelf.currentStandaloneReactionAnimation {
|
||||
if let itemNode = itemNode {
|
||||
if itemNode === standaloneReactionItemNode {
|
||||
currentStandaloneReactionAnimation.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
} else {
|
||||
currentStandaloneReactionAnimation.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode)
|
||||
}
|
||||
|
||||
@@ -6271,6 +6269,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, passthroughTouches: true)), items: items |> map { ContextController.Items(content: .list($0)) })
|
||||
contextController.dismissedForCancel = { [weak chatController] in
|
||||
@@ -7750,6 +7750,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .pinnedMessages(id: pinnedMessage.message.id), botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}, joinGroupCall: { [weak self] activeCall in
|
||||
@@ -7812,6 +7815,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
items.append(.custom(ChatSendAsPeerTitleContextItem(text: strongSelf.presentationInterfaceState.strings.Conversation_SendMesageAs.uppercased()), false))
|
||||
items.append(.custom(ChatSendAsPeerListContextItem(context: strongSelf.context, chatPeerId: peerId, peers: peers, selectedPeerId: myPeerId), false))
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .reference(ChatControllerContextReferenceContentSource(controller: strongSelf, sourceNode: node, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0))), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
contextController.dismissed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
@@ -11998,6 +12003,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peerId), subject: .message(id: .timestamp(timestamp), highlight: false, timecode: nil), botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
|
||||
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: sourceNode, sourceRect: sourceRect, passthroughTouches: true)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
@@ -1144,7 +1144,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
layoutSize.height += actionButtonsSizeAndApply.0.height
|
||||
}
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
layoutSize.height += reactionButtonsSizeAndApply.0.height
|
||||
layoutSize.height += 4.0 + reactionButtonsSizeAndApply.0.height
|
||||
}
|
||||
|
||||
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _, _ in
|
||||
@@ -1411,7 +1411,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
|
||||
let reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0)
|
||||
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY), size: reactionButtonsSizeAndApply.0)
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height
|
||||
}
|
||||
if reactionButtonsNode !== strongSelf.reactionButtonsNode {
|
||||
strongSelf.reactionButtonsNode = reactionButtonsNode
|
||||
reactionButtonsNode.reactionSelected = { value in
|
||||
|
||||
@@ -558,7 +558,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
if !reactions.reactions.isEmpty {
|
||||
let totalInset = params.leftInset + layoutConstants.bubble.edgeInset * 2.0 + avatarInset + layoutConstants.bubble.contentInsets.left + params.rightInset + layoutConstants.bubble.contentInsets.right
|
||||
|
||||
let maxReactionsWidth = params.width - totalInset
|
||||
let maxReactionsWidth = params.width - totalInset - 8.0
|
||||
let (minWidth, buttonsLayout) = reactionButtonsLayout(ChatMessageReactionButtonsNode.Arguments(
|
||||
context: item.context,
|
||||
presentationData: item.presentationData,
|
||||
@@ -787,7 +787,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
|
||||
let reactionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY + 6.0), size: reactionButtonsSizeAndApply.0)
|
||||
var reactionButtonsFrame = CGRect(origin: CGPoint(x: videoFrame.minX, y: videoFrame.maxY + 6.0), size: reactionButtonsSizeAndApply.0)
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height
|
||||
}
|
||||
if reactionButtonsNode !== strongSelf.reactionButtonsNode {
|
||||
strongSelf.reactionButtonsNode = reactionButtonsNode
|
||||
reactionButtonsNode.reactionSelected = { value in
|
||||
@@ -805,6 +808,16 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
|
||||
}
|
||||
reactionButtonsNode.frame = reactionButtonsFrame
|
||||
if let (rect, containerSize) = strongSelf.absoluteRect {
|
||||
var rect = rect
|
||||
rect.origin.y = containerSize.height - rect.maxY + strongSelf.insets.top
|
||||
|
||||
var reactionButtonsNodeFrame = reactionButtonsFrame
|
||||
reactionButtonsNodeFrame.origin.x += rect.minX
|
||||
reactionButtonsNodeFrame.origin.y += rect.minY
|
||||
|
||||
reactionButtonsNode.update(rect: rect, within: containerSize, transition: .immediate)
|
||||
}
|
||||
strongSelf.addSubnode(reactionButtonsNode)
|
||||
if animation.isAnimated {
|
||||
reactionButtonsNode.animateIn(animation: animation)
|
||||
@@ -1338,6 +1351,28 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.interactiveVideoNode.frame, nil)
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
override func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
var rect = rect
|
||||
rect.origin.y = containerSize.height - rect.maxY + self.insets.top
|
||||
|
||||
if let reactionButtonsNode = self.reactionButtonsNode {
|
||||
var reactionButtonsNodeFrame = reactionButtonsNode.frame
|
||||
reactionButtonsNodeFrame.origin.x += rect.minX
|
||||
reactionButtonsNodeFrame.origin.y += rect.minY
|
||||
|
||||
reactionButtonsNode.update(rect: rect, within: containerSize, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) {
|
||||
if let reactionButtonsNode = self.reactionButtonsNode {
|
||||
reactionButtonsNode.offset(value: value, animationCurve: animationCurve, duration: duration)
|
||||
}
|
||||
}
|
||||
|
||||
override func targetReactionView(value: String) -> UIView? {
|
||||
if let result = self.reactionButtonsNode?.reactionTargetView(value: value) {
|
||||
return result
|
||||
|
||||
@@ -689,7 +689,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
layoutSize.height += dateAndStatusSize.height
|
||||
}
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
layoutSize.height += reactionButtonsSizeAndApply.0.height
|
||||
layoutSize.height += 4.0 + reactionButtonsSizeAndApply.0.height
|
||||
}
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
layoutSize.height += actionButtonsSizeAndApply.0.height
|
||||
@@ -986,7 +986,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
|
||||
if let reactionButtonsSizeAndApply = reactionButtonsSizeAndApply {
|
||||
let reactionButtonsNode = reactionButtonsSizeAndApply.1(animation)
|
||||
let reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY - 10.0), size: reactionButtonsSizeAndApply.0)
|
||||
var reactionButtonsFrame = CGRect(origin: CGPoint(x: imageFrame.minX, y: imageFrame.maxY - 10.0), size: reactionButtonsSizeAndApply.0)
|
||||
if let actionButtonsSizeAndApply = actionButtonsSizeAndApply {
|
||||
reactionButtonsFrame.origin.y += 4.0 + actionButtonsSizeAndApply.0.height
|
||||
}
|
||||
if reactionButtonsNode !== strongSelf.reactionButtonsNode {
|
||||
strongSelf.reactionButtonsNode = reactionButtonsNode
|
||||
reactionButtonsNode.reactionSelected = { value in
|
||||
|
||||
@@ -5,6 +5,10 @@ import Display
|
||||
import ContextUI
|
||||
import AnimatedStickerNode
|
||||
import SwiftSignalKit
|
||||
import ContextUI
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import ReactionSelectionNode
|
||||
|
||||
private final class OverlayTransitionContainerNode: ViewControllerTracingNode {
|
||||
override init() {
|
||||
@@ -595,6 +599,53 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class MessageReactionContext {
|
||||
private(set) weak var itemNode: ListViewItemNode?
|
||||
private(set) weak var contextController: ContextController?
|
||||
private(set) weak var standaloneReactionAnimation: StandaloneReactionAnimation?
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.contextController == nil && self.standaloneReactionAnimation == nil
|
||||
}
|
||||
|
||||
init(itemNode: ListViewItemNode, contextController: ContextController?, standaloneReactionAnimation: StandaloneReactionAnimation?) {
|
||||
self.itemNode = itemNode
|
||||
self.contextController = contextController
|
||||
self.standaloneReactionAnimation = standaloneReactionAnimation
|
||||
}
|
||||
|
||||
func addExternalOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, itemNode: ListViewItemNode?) {
|
||||
guard let currentItemNode = self.itemNode else {
|
||||
return
|
||||
}
|
||||
if itemNode == nil || itemNode === currentItemNode {
|
||||
if let contextController = self.contextController {
|
||||
contextController.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
|
||||
standaloneReactionAnimation.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addContentOffset(offset: CGFloat, itemNode: ListViewItemNode?) {
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
if let contextController = self.contextController {
|
||||
contextController.cancelReactionAnimation()
|
||||
contextController.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
})
|
||||
}
|
||||
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
|
||||
standaloneReactionAnimation.cancel()
|
||||
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
|
||||
standaloneReactionAnimation?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let listNode: ChatHistoryListNode
|
||||
private let getContentAreaInScreenSpace: () -> CGRect
|
||||
@@ -604,6 +655,7 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
|
||||
private var animatingItemNodes: [AnimatingItemNode] = []
|
||||
private var decorationItemNodes: [DecorationItemNode] = []
|
||||
private var messageReactionContexts: [MessageReactionContext] = []
|
||||
|
||||
var hasScheduledTransitions: Bool {
|
||||
return self.currentPendingItem != nil
|
||||
@@ -714,6 +766,61 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func removeEmptyMessageReactionContexts() {
|
||||
for i in (0 ..< self.messageReactionContexts.count).reversed() {
|
||||
if self.messageReactionContexts[i].isEmpty {
|
||||
self.messageReactionContexts.remove(at: i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dismissMessageReactionContexts(itemNode: ListViewItemNode? = nil) {
|
||||
for i in (0 ..< self.messageReactionContexts.count).reversed() {
|
||||
let messageReactionContext = self.messageReactionContexts[i]
|
||||
if itemNode == nil || messageReactionContext.itemNode === itemNode {
|
||||
self.messageReactionContexts.remove(at: i)
|
||||
messageReactionContext.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addMessageContextController(messageId: MessageId, contextController: ContextController) {
|
||||
self.addMessageReactionContextContext(messageId: messageId, contextController: contextController, standaloneReactionAnimation: nil)
|
||||
}
|
||||
|
||||
func addMessageStandaloneReactionAnimation(messageId: MessageId, standaloneReactionAnimation: StandaloneReactionAnimation) {
|
||||
self.addMessageReactionContextContext(messageId: messageId, contextController: nil, standaloneReactionAnimation: standaloneReactionAnimation)
|
||||
}
|
||||
|
||||
private func addMessageReactionContextContext(messageId: MessageId, contextController: ContextController?, standaloneReactionAnimation: StandaloneReactionAnimation?) {
|
||||
self.removeEmptyMessageReactionContexts()
|
||||
|
||||
var messageItemNode: ListViewItemNode?
|
||||
self.listNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let item = itemNode.item {
|
||||
for (message, _) in item.content {
|
||||
if message.id == messageId {
|
||||
messageItemNode = itemNode
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let messageItemNode = messageItemNode {
|
||||
for i in 0 ..< self.messageReactionContexts.count {
|
||||
if self.messageReactionContexts[i].itemNode === messageItemNode {
|
||||
self.messageReactionContexts[i].dismiss()
|
||||
self.messageReactionContexts.remove(at: i)
|
||||
break
|
||||
}
|
||||
}
|
||||
self.messageReactionContexts.append(MessageReactionContext(itemNode: messageItemNode, contextController: contextController, standaloneReactionAnimation: standaloneReactionAnimation))
|
||||
}
|
||||
}
|
||||
|
||||
func addExternalOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, itemNode: ListViewItemNode?) {
|
||||
for animatingItemNode in self.animatingItemNodes {
|
||||
@@ -724,6 +831,9 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
decorationItemNode.addExternalOffset(offset: offset, transition: transition)
|
||||
}
|
||||
}
|
||||
for messageReactionContext in self.messageReactionContexts {
|
||||
messageReactionContext.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode)
|
||||
}
|
||||
}
|
||||
|
||||
func addContentOffset(offset: CGFloat, itemNode: ListViewItemNode?) {
|
||||
@@ -735,6 +845,9 @@ public final class ChatMessageTransitionNode: ASDisplayNode {
|
||||
decorationItemNode.addContentOffset(offset: offset)
|
||||
}
|
||||
}
|
||||
for messageReactionContext in self.messageReactionContexts {
|
||||
messageReactionContext.addContentOffset(offset: offset, itemNode: itemNode)
|
||||
}
|
||||
}
|
||||
|
||||
func isAnimatingMessage(stableId: UInt32) -> Bool {
|
||||
|
||||
@@ -94,7 +94,7 @@ public final class NotificationViewControllerImpl {
|
||||
|
||||
if sharedAccountContext == nil {
|
||||
initializeAccountManagement()
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false)
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false)
|
||||
|
||||
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
|
||||
@@ -205,7 +205,7 @@ public class ShareRootControllerImpl {
|
||||
|
||||
let internalContext: InternalContext
|
||||
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false)
|
||||
let accountManager = AccountManager<TelegramAccountManagerTypes>(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false)
|
||||
|
||||
if let globalInternalContext = globalInternalContext {
|
||||
internalContext = globalInternalContext
|
||||
|
||||
Reference in New Issue
Block a user