diff --git a/.bazelrc b/.bazelrc
index 1ce9174344..9127415f6a 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -22,14 +22,36 @@ build --strategy=SwiftCompile=worker
#common --registry=https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/
-# SourceKit BSP: Swift indexing features
-common --features=swift.index_while_building
-common --features=swift.use_global_index_store
-common --features=swift.use_global_module_cache
-common --features=oso_prefix_is_pwd
+# rules_swift flags migration
+# --swiftcopt and --host_swiftcopt are deprecated
+common --flag_alias=swiftcopt=@build_bazel_rules_swift//swift:copt
+common --flag_alias=host_swiftcopt=@build_bazel_rules_swift//swift:exec_copt
+#common --swiftcopt=-whole-module-optimization
+#common --host_swiftcopt=-whole-module-optimization
-# SourceKit BSP: Index build config (used for background indexing)
+common --check_visibility=false
+
+# All of the following are Debug/Index setup configs inspired by the default rules_xcodeproj template
+
+common --verbose_failures
+common --cache_computed_file_digests=500000
+common --action_cache_store_output_metadata
+common --experimental_use_cpp_compile_action_args_params_file
+common --define=apple.experimental.tree_artifact_outputs=1
+common --features=apple.swizzle_absolute_xcttestsourcelocation
+common --features=oso_prefix_is_pwd
+common --features=relative_ast_path
+common --features=swift.cacheable_swiftmodules
+common --features=swift.index_while_building
+common --features=swift.use_global_module_cache
+common --features=swift.emit_swiftsourceinfo
+common --nolegacy_important_outputs
+build --noworker_sandboxing
+build --spawn_strategy=remote,worker,local
+
+# Only for BSP builds
common:index_build --experimental_convenience_symlinks=ignore
+common:index_build --bes_backend= --bes_results_url=
common:index_build --show_result=0
common:index_build --noshow_loading_progress
common:index_build --noshow_progress
@@ -37,3 +59,4 @@ common:index_build --noannounce_rc
common:index_build --noshow_timestamps
common:index_build --curses=no
common:index_build --color=no
+
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d1e808574b..ba34a33e56 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -23,5 +23,7 @@
]
}
},
- "swift.sourcekit-lsp.serverPath": "${workspaceFolder}/build-input/sourcekit-lsp"
+ "swift.sourcekit-lsp.serverPath": "${workspaceFolder}/build-input/sourcekit-lsp",
+ "sourcekit-bazel-bsp.rulesAppleName": "build_bazel_rules_apple",
+ "cmake.sourceDirectory": "/Users/ali/build/telegram/telegram-ios/submodules/rlottie/rlottie/test"
}
diff --git a/BUILD.bazel b/BUILD.bazel
index 05e5d1c325..4292181ff1 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,5 +1,7 @@
load("@sourcekit_bazel_bsp//rules:setup_sourcekit_bsp.bzl", "setup_sourcekit_bsp")
+exports_files(["versions.json"])
+
setup_sourcekit_bsp(
name = "setup_sourcekit_bsp",
bazel_wrapper = "./build-input/bazel-8.4.2-darwin-arm64",
@@ -18,17 +20,12 @@ setup_sourcekit_bsp(
"submodules/**/*.cpp",
],
aquery_flags = [
- "define=telegramVersion=12.5",
- "define=buildNumber=100000",
],
index_flags = [
"config=index_build",
- "define=telegramVersion=12.5",
- "define=buildNumber=100000",
],
index_build_batch_size = 10,
targets = [
"//Telegram:Telegram",
],
- merge_lsp_config = False,
)
diff --git a/Telegram/BUILD b/Telegram/BUILD
index 15f6fe2c5f..a1cf7d9524 100644
--- a/Telegram/BUILD
+++ b/Telegram/BUILD
@@ -385,7 +385,8 @@ plist_fragment(
"""
CFBundleVersion
{buildNumber}
- """
+ """,
+ defaults = {"buildNumber": "1"},
)
plist_fragment(
@@ -574,14 +575,23 @@ plist_fragment(
])
)
-plist_fragment(
+genrule(
name = "VersionInfoPlist",
- extension = "plist",
- template =
- """
+ srcs = ["//:versions.json"],
+ outs = ["VersionInfoPlist.plist"],
+ cmd = """
+ version=$$(python3 -c "import json; print(json.load(open('$(location //:versions.json)'))['app'])")
+ cat > $@ <
+
+
+
CFBundleShortVersionString
- {telegramVersion}
- """
+ $$version
+
+
+PLIST
+ """,
)
plist_fragment(
diff --git a/Tests/TelegramCoreBuildTest/BUILD b/Tests/TelegramCoreBuildTest/BUILD
index 8b4227a100..e5038bea6f 100644
--- a/Tests/TelegramCoreBuildTest/BUILD
+++ b/Tests/TelegramCoreBuildTest/BUILD
@@ -43,14 +43,23 @@ plist_fragment(
"""
)
-plist_fragment(
+genrule(
name = "VersionInfoPlist",
- extension = "plist",
- template =
- """
+ srcs = ["//:versions.json"],
+ outs = ["VersionInfoPlist.plist"],
+ cmd = """
+ version=$$(python3 -c "import json; print(json.load(open('$(location //:versions.json)'))['app'])")
+ cat > $@ <
+
+
+
CFBundleShortVersionString
- {telegramVersion}
- """
+ $$version
+
+
+PLIST
+ """,
)
plist_fragment(
diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py
index ebff8097e5..457e5d9517 100644
--- a/build-system/Make/Make.py
+++ b/build-system/Make/Make.py
@@ -212,7 +212,6 @@ class BazelCommandLine:
def get_define_arguments(self):
return [
'--define=buildNumber={}'.format(self.build_number),
- '--define=telegramVersion={}'.format(self.build_environment.app_version)
]
def get_project_generation_arguments(self):
diff --git a/build-system/bazel-rules/rules_apple b/build-system/bazel-rules/rules_apple
index 39cc252924..f6aef7db5b 160000
--- a/build-system/bazel-rules/rules_apple
+++ b/build-system/bazel-rules/rules_apple
@@ -1 +1 @@
-Subproject commit 39cc2529241335d20afcb6700c587d064e7a2870
+Subproject commit f6aef7db5b649ffc3b7497ecc83a08ad3ab19559
diff --git a/build-system/bazel-utils/plist_fragment.bzl b/build-system/bazel-utils/plist_fragment.bzl
index ac40575332..64ef8d4076 100644
--- a/build-system/bazel-utils/plist_fragment.bzl
+++ b/build-system/bazel-utils/plist_fragment.bzl
@@ -18,6 +18,8 @@ def _plist_fragment(ctx):
resolved_values = dict()
for key in found_keys:
value = ctx.var.get(key, None)
+ if value == None:
+ value = ctx.attr.defaults.get(key, None)
if value == None:
fail("Expected value for --define={} was not found".format(key))
resolved_values[key] = value
@@ -38,6 +40,7 @@ plist_fragment = rule(
attrs = {
"extension": attr.string(mandatory = True),
"template": attr.string(mandatory = True),
+ "defaults": attr.string_dict(),
},
outputs = {
"out": "%{name}.%{extension}"
diff --git a/scripts/lldb_build_common.sh b/scripts/lldb_build_common.sh
index 8633902f40..223417bc5b 100755
--- a/scripts/lldb_build_common.sh
+++ b/scripts/lldb_build_common.sh
@@ -10,12 +10,9 @@ WORKSPACE_ROOT=$(pwd)
BAZEL_CMD="./build-input/bazel-8.4.2-darwin-arm64"
export ADDITIONAL_FLAGS=()
-TELEGRAM_VERSION=$(python3 -c "import json; print(json.load(open('${WORKSPACE_ROOT}/versions.json'))['app'])")
ADDITIONAL_FLAGS+=("--keep_going")
ADDITIONAL_FLAGS+=("--color=yes")
-ADDITIONAL_FLAGS+=("--define=telegramVersion=${TELEGRAM_VERSION}")
-ADDITIONAL_FLAGS+=("--define=buildNumber=100000")
if [ -n "${BAZEL_EXTRA_BUILD_FLAGS:-}" ]; then
ADDITIONAL_FLAGS+=("${BAZEL_EXTRA_BUILD_FLAGS[@]}")
diff --git a/submodules/ContextUI/Sources/PinchSourceContainerNode.swift b/submodules/ContextUI/Sources/PinchSourceContainerNode.swift
index bc2cc63ac6..e04b59d7fd 100644
--- a/submodules/ContextUI/Sources/PinchSourceContainerNode.swift
+++ b/submodules/ContextUI/Sources/PinchSourceContainerNode.swift
@@ -146,7 +146,7 @@ public final class PinchSourceContainerNode: ASDisplayNode, ASGestureRecognizerD
public var maxPinchScale: CGFloat = 10.0
- private var isActive: Bool = false
+ public private(set) var isActive: Bool = false
public var activate: ((PinchSourceContainerNode) -> Void)?
public var scaleUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
@@ -223,4 +223,4 @@ public final class PinchSourceContainerNode: ASDisplayNode, ASGestureRecognizerD
}
self.contentNode.frame = naturalContentFrame
}
-}
\ No newline at end of file
+}
diff --git a/submodules/Display/Source/ListViewProtocol.swift b/submodules/Display/Source/ListViewProtocol.swift
index d66509ef95..51a838d7e4 100644
--- a/submodules/Display/Source/ListViewProtocol.swift
+++ b/submodules/Display/Source/ListViewProtocol.swift
@@ -30,6 +30,7 @@ public protocol ListView: ASDisplayNode {
// MARK: - Read-only State Properties
var insets: UIEdgeInsets { get }
+ var visibleSize: CGSize { get }
var isTracking: Bool { get }
var trackingOffset: CGFloat { get }
var beganTrackingAtTopOrigin: Bool { get }
diff --git a/submodules/GalleryUI/Sources/GalleryFooterNode.swift b/submodules/GalleryUI/Sources/GalleryFooterNode.swift
index a2571990d0..af4656ee40 100644
--- a/submodules/GalleryUI/Sources/GalleryFooterNode.swift
+++ b/submodules/GalleryUI/Sources/GalleryFooterNode.swift
@@ -78,7 +78,7 @@ public final class GalleryFooterNode: ASDisplayNode {
self.currentThumbnailPanelHeight = thumbnailPanelHeight
self.currentFooterContentNode = footerContentNode
if let footerContentNode = footerContentNode {
- footerContentNode.setVisibilityAlpha(self.visibilityAlpha, animated: transition.isAnimated)
+ footerContentNode.setVisibilityAlpha(self.visibilityAlpha, animated: false)
footerContentNode.controllerInteraction = self.controllerInteraction
footerContentNode.requestLayout = { [weak self] transition in
if let strongSelf = self, let (currentLayout, navigationBarHeight, currentThumbnailPanelHeight, isHidden) = strongSelf.currentLayout {
diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
index 7de25df73e..8c4bc2ac28 100644
--- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
+++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift
@@ -928,6 +928,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
private var scrubbingFrameDisposable: Disposable?
private var isPlaying = false
+ private var hasStartedOnce = false
private let isPlayingPromise = ValuePromise(false, ignoreRepeated: true)
private let isInteractingPromise = ValuePromise(false, ignoreRepeated: true)
private var areControlsVisible: Bool = true
@@ -1679,10 +1680,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
var isPlaying = false
var isPaused = true
var seekable = hintSeekable
- var hasStarted = false
+ var hasStarted = strongSelf.hasStartedOnce
var displayProgress = true
if let value = value {
- hasStarted = value.timestamp > 0
+ if value.timestamp > 0 {
+ hasStarted = true
+ }
if let zoomableContent = strongSelf.zoomableContent, !value.dimensions.width.isZero && !value.dimensions.height.isZero {
let videoSize = CGSize(width: value.dimensions.width * 2.0, height: value.dimensions.height * 2.0)
@@ -1750,6 +1753,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
}
}
+ strongSelf.hasStartedOnce = hasStarted
+
if !disablePlayerControls && strongSelf.isCentral == true && isPlaying {
strongSelf.isPlayingPromise.set(true)
strongSelf.isPlaying = true
diff --git a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
index d5ee06bab1..98029012c8 100644
--- a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
+++ b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift
@@ -268,7 +268,11 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
self.foregroundNode.isLayerBacked = true
self.foregroundNode.displayWithoutProcessing = true
self.foregroundNode.displaysAsynchronously = false
- self.foregroundNode.image = generateStretchableFilledCircleImage(diameter: lineHeight, color: color)
+ if case .round = lineCap {
+ self.foregroundNode.image = generateStretchableFilledCircleImage(diameter: lineHeight, color: color)
+ } else {
+ self.foregroundNode.backgroundColor = color
+ }
super.init()
@@ -278,6 +282,9 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
func updateStatus(_ ranges: RangeSet, _ size: Int64) {
self.ranges = (ranges, size)
+ /*#if DEBUG
+ self.ranges = (RangeSet(0 ..< size / 2), size)
+ #endif*/
if !self.bounds.width.isZero {
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.15, curve: .easeInOut))
}
@@ -424,7 +431,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
backgroundNode.displaysAsynchronously = false
backgroundNode.displayWithoutProcessing = true
- let bufferingNode = MediaPlayerScrubbingBufferingNode(color: bufferingColor, lineCap: lineCap, lineHeight: lineHeight)
+ let bufferingNode = MediaPlayerScrubbingBufferingNode(color: bufferingColor, lineCap: scrubberHandle == .none ? .square : lineCap, lineHeight: lineHeight)
let foregroundContentNode = ASImageNode()
foregroundContentNode.isLayerBacked = true
@@ -434,7 +441,11 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
switch lineCap {
case .round:
backgroundNode.image = generateStretchableFilledCircleImage(diameter: lineHeight, color: backgroundColor)
- foregroundContentNode.image = generateStretchableFilledCircleImage(diameter: lineHeight, color: foregroundColor)
+ if case .none = scrubberHandle {
+ foregroundContentNode.backgroundColor = foregroundColor
+ } else {
+ foregroundContentNode.image = generateStretchableFilledCircleImage(diameter: lineHeight, color: foregroundColor)
+ }
case .square:
backgroundNode.backgroundColor = backgroundColor
foregroundContentNode.backgroundColor = foregroundColor
@@ -443,13 +454,16 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
let foregroundNode = MediaPlayerScrubbingForegroundNode()
foregroundNode.isLayerBacked = true
foregroundNode.clipsToBounds = true
+ if case .round = lineCap {
+ foregroundNode.layer.cornerRadius = lineHeight * 0.5
+ }
var handleNodeImpl: ASImageNode?
var highlightedHandleNodeImpl: ASImageNode?
var handleNodeContainerImpl: MediaPlayerScrubbingNodeButton?
switch scrubberHandle {
- case .none:
+ case .none:
let handleNode = ASImageNode()
handleNode.isLayerBacked = true
handleNodeImpl = handleNode
@@ -750,7 +764,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
switch self.contentNodes {
case let .standard(node):
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
- node.foregroundContentNode.backgroundColor = collapsed ? .white : nil
+ node.foregroundContentNode.backgroundColor = collapsed ? .white : (node.handle == .none ? .white : nil)
if let handleNode = node.handleNodeContainer {
transition.updateAlpha(node: node.foregroundContentNode, alpha: collapsed ? 0.45 : 1.0)
diff --git a/submodules/Postbox/Sources/TimeBasedCleanup.swift b/submodules/Postbox/Sources/TimeBasedCleanup.swift
index 978ec8e094..12b4e864ba 100644
--- a/submodules/Postbox/Sources/TimeBasedCleanup.swift
+++ b/submodules/Postbox/Sources/TimeBasedCleanup.swift
@@ -198,87 +198,6 @@ private func scanFiles(at path: String, olderThan minTimestamp: Int32, includeSu
return result
}
-/*private func mapFiles(paths: [String], inodes: inout [InodeInfo], removeSize: UInt64, mainStoragePath: String, storageBox: StorageBox) {
- var removedSize: UInt64 = 0
- inodes.sort(by: { lhs, rhs in
- return lhs.timestamp < rhs.timestamp
- })
-
- var inodesToDelete = Set<__darwin_ino64_t>()
-
- for inode in inodes {
- inodesToDelete.insert(inode.inode)
- removedSize += UInt64(inode.size)
- if removedSize >= removeSize {
- break
- }
- }
-
- if inodesToDelete.isEmpty {
- return
- }
-
- let pathBuffer = malloc(2048).assumingMemoryBound(to: Int8.self)
- defer {
- free(pathBuffer)
- }
-
- var unlinkedResourceIds: [Data] = []
-
- for path in paths {
- let isMainPath = path == mainStoragePath
- if let dp = opendir(path) {
- while true {
- guard let dirp = readdir(dp) else {
- break
- }
-
- if strncmp(&dirp.pointee.d_name.0, ".", 1024) == 0 {
- continue
- }
- if strncmp(&dirp.pointee.d_name.0, "..", 1024) == 0 {
- continue
- }
- strncpy(pathBuffer, path, 1024)
- strncat(pathBuffer, "/", 1024)
- strncat(pathBuffer, &dirp.pointee.d_name.0, 1024)
-
- //puts(pathBuffer)
- //puts("\n")
-
- var value = stat()
- if stat(pathBuffer, &value) == 0 {
- if (((value.st_mode) & S_IFMT) == S_IFDIR) {
- if let subPath = String(data: Data(bytes: pathBuffer, count: strnlen(pathBuffer, 1024)), encoding: .utf8) {
- mapFiles(paths: <#T##[String]#>, inodes: &<#T##[InodeInfo]#>, removeSize: remov, mainStoragePath: mainStoragePath, storageBox: storageBox)
- }
- } else {
- if inodesToDelete.contains(value.st_ino) {
- if isMainPath {
- let nameLength = strnlen(&dirp.pointee.d_name.0, 1024)
- let nameData = Data(bytesNoCopy: &dirp.pointee.d_name.0, count: Int(nameLength), deallocator: .none)
- withExtendedLifetime(nameData, {
- if let fileName = String(data: nameData, encoding: .utf8) {
- if let idData = MediaBox.idForFileName(name: fileName).data(using: .utf8) {
- unlinkedResourceIds.append(idData)
- }
- }
- })
- }
- unlink(pathBuffer)
- }
- }
- }
- }
- closedir(dp)
- }
- }
-
- if !unlinkedResourceIds.isEmpty {
- storageBox.remove(ids: unlinkedResourceIds)
- }
-}*/
-
private func statForDirectory(path: String) -> Int64 {
if #available(macOS 10.13, *) {
var s = darwin_dirstat()
@@ -482,10 +401,10 @@ private final class TimeBasedCleanupImpl {
}
//let fileName = filePath.lastPathComponent
- if remainingSize >= bytesLimit {
+ if remainingSize <= Int64(bytesLimit) {
return false
}
-
+
return true
}
}
diff --git a/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift
index a46563c51a..4c6ef24fcc 100644
--- a/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift
+++ b/submodules/TelegramCallsUI/Sources/VideoChatParticipantVideoComponent.swift
@@ -213,6 +213,10 @@ final class VideoChatParticipantVideoComponent: Component {
private var referenceLocation: ReferenceLocation?
private var loadingEffectView: VideoChatVideoLoadingEffectView?
+ public var isPinchToZoomActive: Bool {
+ return self.pinchContainerNode.isActive
+ }
+
override init(frame: CGRect) {
self.backgroundGradientView = UIImageView()
self.pinchContainerNode = PinchSourceContainerNode()
diff --git a/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift
index 11529cf236..5aa0a630c3 100644
--- a/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift
+++ b/submodules/TelegramCallsUI/Sources/VideoChatParticipantsComponent.swift
@@ -693,7 +693,7 @@ final class VideoChatParticipantsComponent: Component {
private var appliedGridIsEmpty: Bool = true
- private var isPinchToZoomActive: Bool = false
+ private(set) var isPinchToZoomActive: Bool = false
private var stopRequestingNonCentralVideo: Bool = false
private var stopRequestingNonCentralVideoTimer: Foundation.Timer?
diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift
index 5449297515..def60adbb4 100644
--- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift
+++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift
@@ -337,6 +337,13 @@ final class VideoChatScreenComponent: Component {
var maxVideoQuality: Int = Int.max
+ private var isPinchToZoomActive: Bool {
+ if let participantsView = self.participants.view as? VideoChatParticipantsComponent.View {
+ return participantsView.isPinchToZoomActive
+ }
+ return false
+ }
+
override init(frame: CGRect) {
self.containerView = UIView()
self.containerView.clipsToBounds = true
@@ -1609,7 +1616,7 @@ final class VideoChatScreenComponent: Component {
}
if let expandedParticipantsVideoState = self.expandedParticipantsVideoState, let members {
- if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, let participant = members.participants.first(where: { participant in
+ if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, !self.isPinchToZoomActive, let participant = members.participants.first(where: { participant in
if let callState = self.callState, participant.id == .peer(callState.myPeerId) {
return false
}
@@ -1966,7 +1973,7 @@ final class VideoChatScreenComponent: Component {
}
if let expandedParticipantsVideoState = self.expandedParticipantsVideoState {
- if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, let participant = members.participants.first(where: { participant in
+ if CFAbsoluteTimeGetCurrent() > self.focusedSpeakerAutoSwitchDeadline, !expandedParticipantsVideoState.isMainParticipantPinned, !self.isPinchToZoomActive, let participant = members.participants.first(where: { participant in
if let callState = self.callState, participant.id == .peer(callState.myPeerId) {
return false
}
diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift
index b47e6d164a..dbfabab361 100644
--- a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift
+++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift
@@ -316,8 +316,8 @@ extension VideoChatScreenComponent.View {
]
let videoQualityTitle = qualityList.first(where: { $0.0 == self.maxVideoQuality })?.1 ?? ""
- items.append(.action(ContextMenuActionItem(text: environment.strings.VideoChat_IncomingVideoQuality_Title, textColor: .primary, textLayout: .secondLineWithValue(videoQualityTitle), icon: { _ in
- return nil
+ items.append(.action(ContextMenuActionItem(text: environment.strings.VideoChat_IncomingVideoQuality_Title, textColor: .primary, textLayout: .secondLineWithValue(videoQualityTitle), icon: { theme in
+ return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Settings"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] c, _ in
guard let self else {
c?.dismiss(completion: nil)
diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift
index 70360e3234..d84df8a4c2 100644
--- a/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift
+++ b/submodules/TelegramCallsUI/Sources/VoiceChatShareScreenContextItem.swift
@@ -121,11 +121,10 @@ private final class VoiceChatShareScreenContextItemNode: ASDisplayNode, ContextM
let standardIconWidth: CGFloat = 32.0
var rightTextInset: CGFloat = sideInset
if !iconSize.width.isZero {
- rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset
+ rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset
}
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
-
let verticalSpacing: CGFloat = 2.0
let combinedTextHeight = textSize.height + verticalSpacing
diff --git a/submodules/TelegramCore/Sources/State/PaidMessages.swift b/submodules/TelegramCore/Sources/State/PaidMessages.swift
index fc41251860..d576b283a7 100644
--- a/submodules/TelegramCore/Sources/State/PaidMessages.swift
+++ b/submodules/TelegramCore/Sources/State/PaidMessages.swift
@@ -96,23 +96,29 @@ func _internal_reinstateNoPaidMessagesException(account: Account, scopePeerId: P
return (transaction.getPeer(scopePeerId).flatMap(apiInputPeer), transaction.getPeer(peerId).flatMap(apiInputUser))
}
|> mapToSignal { scopeInputPeer, inputUser -> Signal in
+ var scopeInputPeer = scopeInputPeer
if scopePeerId != account.peerId {
if scopeInputPeer == nil {
return .never()
}
} else {
- return .never()
+ scopeInputPeer = nil
}
guard let inputUser else {
return .never()
}
var flags: Int32 = 0
flags |= (1 << 2)
- flags |= (1 << 1)
+ if scopeInputPeer != nil {
+ flags |= (1 << 1)
+ }
return account.network.request(Api.functions.account.toggleNoPaidMessagesException(flags: flags, parentPeer: scopeInputPeer, userId: inputUser))
|> `catch` { _ -> Signal in
return .single(.boolFalse)
} |> mapToSignal { _ in
+ if scopePeerId == account.peerId {
+ account.viewTracker.forceUpdateCachedPeerData(peerId: peerId)
+ }
return account.postbox.transaction { transaction -> Void in
if scopePeerId != account.peerId {
guard var data = transaction.getMessageHistoryThreadInfo(peerId: scopePeerId, threadId: peerId.toInt64())?.data.get(MessageHistoryThreadData.self) else {
diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift
index c7c38cb20b..e51b9a840e 100644
--- a/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift
+++ b/submodules/TelegramCore/Sources/TelegramEngine/Resources/CollectCacheUsageStats.swift
@@ -325,6 +325,31 @@ func _internal_renderStorageUsageStatsMessages(account: Account, stats: StorageU
}
func _internal_clearStorage(account: Account, peerId: EnginePeer.Id?, categories: [StorageUsageStats.CategoryKey], includeMessages: [Message], excludeMessages: [Message]) -> Signal {
+ #if DEBUG
+ if "".isEmpty {
+ return Signal { subscriber in
+ var value: Float = 0.0
+ subscriber.putNext(value)
+ let timer = SwiftSignalKit.Timer(timeout: 0.1, repeat: true, completion: {
+ if value != 1.0 {
+ value += 0.1
+ if value >= 1.0 {
+ value = 1.0
+ subscriber.putNext(value)
+ subscriber.putCompletion()
+ } else {
+ subscriber.putNext(value)
+ }
+ }
+ }, queue: .mainQueue())
+ timer.start()
+ return ActionDisposable {
+ timer.invalidate()
+ }
+ }
+ }
+ #endif
+
let mediaBox = account.postbox.mediaBox
return Signal { subscriber in
var includeResourceIds = Set()
diff --git a/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift b/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift
index e4773b42e8..fe0cae68e7 100644
--- a/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift
+++ b/submodules/TelegramCore/Sources/Utils/AutomaticCacheEviction.swift
@@ -134,115 +134,119 @@ final class AutomaticCacheEvictionContext {
}
}
- if timeout == Int32.max {
- return .complete()
- }
+ let storyTimeout: Int32 = settings.categoryStorageTimeout[.stories] ?? (2 * 24 * 60 * 60)
- let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - timeout
- //let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
-
- let allSignal = mediaBox.storageBox.all(peerId: peerId, excludeType: MediaResourceUserContentType.story.rawValue)
- |> mapToSignal { peerResourceIds -> Signal in
- return Signal { subscriber in
- var isCancelled = false
-
- processingQueue.justDispatch {
- var removeIds: [MediaResourceId] = []
- var removeRawIds: [Data] = []
- var localCounter = 0
- for resourceId in peerResourceIds {
- localCounter += 1
- if localCounter % 100 == 0 {
- if isCancelled {
- subscriber.putCompletion()
- return
- }
- }
-
- removeRawIds.append(resourceId)
- let id = MediaResourceId(String(data: resourceId, encoding: .utf8)!)
- let resourceTimestamp = mediaBox.resourceUsageWithInfo(id: id)
- if resourceTimestamp != 0 && resourceTimestamp < minPeerTimestamp {
- removeIds.append(id)
- }
- }
+ let allSignal: Signal
+ if timeout != Int32.max {
+ let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - timeout
+
+ allSignal = mediaBox.storageBox.all(peerId: peerId, excludeType: MediaResourceUserContentType.story.rawValue)
+ |> mapToSignal { peerResourceIds -> Signal in
+ return Signal { subscriber in
+ var isCancelled = false
- if !removeIds.isEmpty {
- Logger.shared.log("AutomaticCacheEviction", "peer \(peerId): cleaning \(removeIds.count) resources")
-
- let _ = mediaBox.removeCachedResourcesWithResult(removeIds).start(next: { actualIds in
- var actualRawIds: [Data] = []
- for id in actualIds {
- if let data = id.stringRepresentation.data(using: .utf8) {
- actualRawIds.append(data)
+ processingQueue.justDispatch {
+ var removeIds: [MediaResourceId] = []
+ var localCounter = 0
+ for resourceId in peerResourceIds {
+ localCounter += 1
+ if localCounter % 100 == 0 {
+ if isCancelled {
+ subscriber.putCompletion()
+ return
}
}
- mediaBox.storageBox.remove(ids: actualRawIds)
-
- subscriber.putCompletion()
- })
- } else {
- subscriber.putCompletion()
- }
- }
-
- return ActionDisposable {
- isCancelled = true
- }
- }
- }
-
- let storySignal = mediaBox.storageBox.all(peerId: peerId, onlyType: MediaResourceUserContentType.story.rawValue)
- |> mapToSignal { peerResourceIds -> Signal in
- return Signal { subscriber in
- var isCancelled = false
-
- processingQueue.justDispatch {
- var removeIds: [MediaResourceId] = []
- var removeRawIds: [Data] = []
- var localCounter = 0
- for resourceId in peerResourceIds {
- localCounter += 1
- if localCounter % 100 == 0 {
- if isCancelled {
- subscriber.putCompletion()
- return
+ let id = MediaResourceId(String(data: resourceId, encoding: .utf8)!)
+ let resourceTimestamp = mediaBox.resourceUsageWithInfo(id: id)
+ if resourceTimestamp != 0 && resourceTimestamp < minPeerTimestamp {
+ removeIds.append(id)
}
}
- removeRawIds.append(resourceId)
- let id = MediaResourceId(String(data: resourceId, encoding: .utf8)!)
- let resourceTimestamp = mediaBox.resourceUsageWithInfo(id: id)
- if resourceTimestamp != 0 && resourceTimestamp < minPeerTimestamp {
- removeIds.append(id)
+ if !removeIds.isEmpty {
+ Logger.shared.log("AutomaticCacheEviction", "peer \(peerId): cleaning \(removeIds.count) resources")
+
+ let _ = mediaBox.removeCachedResourcesWithResult(removeIds).start(next: { actualIds in
+ var actualRawIds: [Data] = []
+ for id in actualIds {
+ if let data = id.stringRepresentation.data(using: .utf8) {
+ actualRawIds.append(data)
+ }
+ }
+
+ mediaBox.storageBox.remove(ids: actualRawIds)
+
+ subscriber.putCompletion()
+ })
+ } else {
+ subscriber.putCompletion()
}
}
- if !removeIds.isEmpty {
- Logger.shared.log("AutomaticCacheEviction", "peer \(peerId): cleaning \(removeIds.count) resources")
-
- let _ = mediaBox.removeCachedResourcesWithResult(removeIds).start(next: { actualIds in
- var actualRawIds: [Data] = []
- for id in actualIds {
- if let data = id.stringRepresentation.data(using: .utf8) {
- actualRawIds.append(data)
+ return ActionDisposable {
+ isCancelled = true
+ }
+ }
+ }
+ } else {
+ allSignal = .complete()
+ }
+
+ let storySignal: Signal
+ if storyTimeout != Int32.max {
+ let minPeerTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - storyTimeout
+ storySignal = mediaBox.storageBox.all(peerId: peerId, onlyType: MediaResourceUserContentType.story.rawValue)
+ |> mapToSignal { peerResourceIds -> Signal in
+ return Signal { subscriber in
+ var isCancelled = false
+
+ processingQueue.justDispatch {
+ var removeIds: [MediaResourceId] = []
+ var localCounter = 0
+ for resourceId in peerResourceIds {
+ localCounter += 1
+ if localCounter % 100 == 0 {
+ if isCancelled {
+ subscriber.putCompletion()
+ return
}
}
- mediaBox.storageBox.remove(ids: actualRawIds)
+ let id = MediaResourceId(String(data: resourceId, encoding: .utf8)!)
+ let resourceTimestamp = mediaBox.resourceUsageWithInfo(id: id)
+ if resourceTimestamp != 0 && resourceTimestamp < minPeerTimestamp {
+ removeIds.append(id)
+ }
+ }
+
+ if !removeIds.isEmpty {
+ Logger.shared.log("AutomaticCacheEviction", "peer \(peerId): cleaning \(removeIds.count) resources")
+ let _ = mediaBox.removeCachedResourcesWithResult(removeIds).start(next: { actualIds in
+ var actualRawIds: [Data] = []
+ for id in actualIds {
+ if let data = id.stringRepresentation.data(using: .utf8) {
+ actualRawIds.append(data)
+ }
+ }
+
+ mediaBox.storageBox.remove(ids: actualRawIds)
+
+ subscriber.putCompletion()
+ })
+ } else {
subscriber.putCompletion()
- })
- } else {
- subscriber.putCompletion()
+ }
+ }
+
+ return ActionDisposable {
+ isCancelled = true
}
}
-
- return ActionDisposable {
- isCancelled = true
- }
}
+ } else {
+ storySignal = .complete()
}
return allSignal |> then(storySignal)
diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift
index c4f433f965..4c75039caa 100644
--- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift
+++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift
@@ -2404,7 +2404,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if additionalSideInsets.right > 0.0 {
textFieldInsets.right += additionalSideInsets.right / 3.0
}
- if inputHasText || self.extendedSearchLayout || hasMediaDraft || hasForward || hasSlowmodeButton {
+ if inputHasText || self.extendedSearchLayout || hasMediaDraft || hasForward || hasSlowmodeButton || isEditingMedia {
} else {
if let customRightAction = self.customRightAction, case .empty = customRightAction {
textFieldInsets.right = 8.0
@@ -3033,7 +3033,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if self.extendedSearchLayout {
nextButtonTopRight.x -= 46.0
} else if hasSlowmodeButton {
- } else if inputHasText || hasMediaDraft || hasForward {
+ } else if inputHasText || hasMediaDraft || hasForward || isEditingMedia {
nextButtonTopRight.x -= sendActionButtonsSize.width
}
for (item, button) in self.accessoryItemButtons.reversed() {
@@ -3182,7 +3182,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
}
var mediaActionButtonsFrame = CGRect(origin: CGPoint(x: textInputContainerBackgroundFrame.maxX + 6.0, y: textInputContainerBackgroundFrame.maxY - mediaActionButtonsSize.height), size: mediaActionButtonsSize)
- if inputHasText || self.extendedSearchLayout || hasMediaDraft || interfaceState.interfaceState.forwardMessageIds != nil || hasSlowmodeButton {
+ if inputHasText || self.extendedSearchLayout || hasMediaDraft || interfaceState.interfaceState.forwardMessageIds != nil || hasSlowmodeButton || isEditingMedia {
mediaActionButtonsFrame.origin.x = width + 8.0
}
transition.updateFrame(node: self.mediaActionButtons, frame: mediaActionButtonsFrame)
@@ -3230,7 +3230,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
var sendActionButtonsFrame = CGRect(origin: CGPoint(x: textInputContainerBackgroundFrame.maxX - sendActionButtonsSize.width, y: textInputContainerBackgroundFrame.maxY - sendActionButtonsSize.height), size: sendActionButtonsSize)
let sendActionsScale: CGFloat
- if inputHasText || hasMediaDraft || hasForward {
+ if inputHasText || hasMediaDraft || hasForward || isEditingMedia {
sendActionsScale = 1.0
} else {
sendActionsScale = 0.001
@@ -4369,6 +4369,11 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
if presentationInterfaceState.interfaceState.mediaDraftState != nil {
keepSendButtonEnabled = true
}
+ if let editMessageState = presentationInterfaceState.editMessageState {
+ if case let .media(value) = editMessageState.content, !value.isEmpty {
+ keepSendButtonEnabled = true
+ }
+ }
hasForward = presentationInterfaceState.interfaceState.forwardMessageIds != nil
hideMicButtonBackground = presentationInterfaceState.inputTextPanelState.mediaRecordingState != nil
diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift
index ab60255310..9d00e1054b 100644
--- a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift
+++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift
@@ -1010,7 +1010,7 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
if (leftTitleInset == leftInset) != (rightTitleInset == rightInset) {
if rightTitleInset == rightInset {
- rightTitleInset = leftTitleInset
+ rightTitleInset = max(rightInset, 16.0)
} else if leftTitleInset == leftInset {
leftTitleInset = rightTitleInset
}
@@ -1041,6 +1041,9 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - leftTitleInset - rightTitleInset, height: nominalHeight), transition: titleViewTransition)
var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
+ if titleFrame.origin.x + titleFrame.width > size.width - rightTitleInset {
+ titleFrame.origin.x = size.width - rightTitleInset - titleFrame.width
+ }
if titleFrame.origin.x < leftTitleInset {
titleFrame.origin.x = leftTitleInset + floorToScreenPixels((size.width - leftTitleInset - rightTitleInset - titleFrame.width) * 0.5)
}
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift
index 1f69cbc65a..b1600c7312 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGroupsInCommonPaneNode.swift
@@ -69,14 +69,18 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
private let groupsInCommonContext: GroupsInCommonContext
weak var parentController: ViewController?
-
+
+ private let listBackgroundView: UIImageView
+ private let listMaskView: UIImageView
private let listNode: ListView
private var state: GroupsInCommonState?
private var currentEntries: [GroupsInCommonListEntry] = []
private var enqueuedTransactions: [GroupsInCommonListTransaction] = []
private var currentParams: (size: CGSize, isScrollingLockedAtTop: Bool, presentationData: PresentationData)?
-
+
+ private var ignoreListBackgroundUpdates: Bool = false
+
private let ready = Promise()
private var didSetReady: Bool = false
var isReady: Signal {
@@ -114,11 +118,25 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.listNode.accessibilityPageScrolledString = { row, count in
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
}
-
+
+ self.listBackgroundView = UIImageView()
+ self.listBackgroundView.image = generateStretchableFilledCircleImage(diameter: 26.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
+ self.listMaskView = UIImageView()
+ self.listMaskView.image = generateImage(CGSize(width: 16.0 + 26.0 * 2.0 + 16.0, height: 2.0 * 2.0 + 26.0 * 2.0), rotatedContext: { size, context in
+ context.clear(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.white.cgColor)
+ context.fill(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.clear.cgColor)
+ context.setBlendMode(.copy)
+ context.fillEllipse(in: CGRect(origin: CGPoint(x: 16.0, y: 2.0), size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0)))
+ })?.stretchableImage(withLeftCapWidth: 16 + 26, topCapHeight: 2 + 26).withRenderingMode(.alwaysTemplate)
+
super.init()
-
+
self.listNode.preloadPages = true
+ self.view.addSubview(self.listBackgroundView)
self.addSubnode(self.listNode)
+ self.view.addSubview(self.listMaskView)
self.disposable = (groupsInCommonContext.state
|> deliverOnMainQueue).startStrict(next: { [weak self] state in
@@ -139,6 +157,23 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
strongSelf.groupsInCommonContext.loadMore()
}
}
+
+ self.listNode.visibleContentOffsetChanged = { [weak self] _, transition in
+ guard let self else {
+ return
+ }
+ if !self.ignoreListBackgroundUpdates {
+ self.updateListBackground(transition: transition)
+ }
+ }
+ self.listNode.displayedItemRangeChanged = { [weak self] _, _ in
+ guard let self else {
+ return
+ }
+ if !self.ignoreListBackgroundUpdates {
+ self.updateListBackground(transition: .immediate)
+ }
+ }
}
deinit {
@@ -161,9 +196,10 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
let isFirstLayout = self.currentParams == nil
self.currentParams = (size, isScrollingLockedAtTop, presentationData)
+ self.ignoreListBackgroundUpdates = true
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
-
+
var scrollToItem: ListViewScrollToItem?
if isScrollingLockedAtTop {
switch self.listNode.visibleContentOffset() {
@@ -174,10 +210,16 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
}
- self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
-
+ self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset + 16.0, bottom: bottomInset, right: sideInset + 16.0), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
+
self.listNode.scrollEnabled = !isScrollingLockedAtTop
-
+
+ self.ignoreListBackgroundUpdates = false
+ self.updateListBackground(transition: transition)
+
+ self.listBackgroundView.tintColor = presentationData.theme.list.itemBlocksBackgroundColor
+ self.listMaskView.tintColor = presentationData.theme.list.blocksBackgroundColor
+
if isFirstLayout, let state = self.state {
self.updatePeers(state: state, presentationData: presentationData)
}
@@ -221,6 +263,35 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
})
}
+ private func updateListBackground(transition: ContainedViewLayoutTransition) {
+ guard self.listNode.visibleSize.width != 0.0 else {
+ return
+ }
+
+ var distanceToTop: CGFloat = -100.0
+ var distanceToBottom: CGFloat = -100.0
+ switch self.listNode.visibleContentOffset() {
+ case let .known(topOffset):
+ distanceToTop = -topOffset + self.listNode.insets.top
+ default:
+ break
+ }
+ switch self.listNode.visibleBottomContentOffset() {
+ case let .known(bottomOffset):
+ distanceToBottom = -bottomOffset + self.listNode.insets.bottom
+ default:
+ break
+ }
+
+ distanceToTop = max(-100.0, distanceToTop)
+ distanceToBottom = max(-100.0, distanceToBottom)
+
+ let listBackgroundFrame = CGRect(origin: CGPoint(x: 16.0, y: distanceToTop), size: CGSize(width: max(1.0, self.listNode.visibleSize.width - 16.0 * 2.0), height: max(1.0, self.listNode.visibleSize.height - distanceToBottom - distanceToTop)))
+ let listMaskFrame = CGRect(origin: CGPoint(x: 0.0, y: listBackgroundFrame.minY - 2.0), size: CGSize(width: listBackgroundFrame.width + 16.0 * 2.0, height: listBackgroundFrame.height + 2.0 * 2.0))
+ transition.updateFrame(view: self.listBackgroundView, frame: listBackgroundFrame)
+ transition.updateFrame(view: self.listMaskView, frame: listMaskFrame)
+ }
+
func findLoadedMessage(id: MessageId) -> Message? {
return nil
}
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift
index 284992c656..7ba0773cb5 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift
@@ -286,6 +286,8 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
weak var parentController: ViewController?
+ private let listBackgroundView: UIImageView
+ private let listMaskView: UIImageView
private let listNode: ListView
private var currentEntries: [PeerMembersListEntry] = []
private var enclosingPeer: Peer?
@@ -296,6 +298,8 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
private var currentParams: (size: CGSize, isScrollingLockedAtTop: Bool)?
private let presentationDataPromise = Promise()
+ private var ignoreListBackgroundUpdates: Bool = false
+
private let ready = Promise()
private var didSetReady: Bool = false
var isReady: Signal {
@@ -325,10 +329,24 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
}
+ self.listBackgroundView = UIImageView()
+ self.listBackgroundView.image = generateStretchableFilledCircleImage(diameter: 26.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
+ self.listMaskView = UIImageView()
+ self.listMaskView.image = generateImage(CGSize(width: 16.0 + 26.0 * 2.0 + 16.0, height: 26.0 * 2.0), rotatedContext: { size, context in
+ context.clear(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.white.cgColor)
+ context.fill(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.clear.cgColor)
+ context.setBlendMode(.copy)
+ context.fillEllipse(in: CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0)))
+ })?.stretchableImage(withLeftCapWidth: 16 + 26, topCapHeight: 26).withRenderingMode(.alwaysTemplate)
+
super.init()
self.listNode.preloadPages = true
+ self.view.addSubview(self.listBackgroundView)
self.addSubnode(self.listNode)
+ self.view.addSubview(self.listMaskView)
self.disposable = (combineLatest(queue: .mainQueue(),
membersContext.state,
@@ -353,6 +371,23 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
strongSelf.membersContext.loadMore()
}
}
+
+ self.listNode.visibleContentOffsetChanged = { [weak self] _, transition in
+ guard let self else {
+ return
+ }
+ if !self.ignoreListBackgroundUpdates {
+ self.updateListBackground(transition: transition)
+ }
+ }
+ self.listNode.displayedItemRangeChanged = { [weak self] _, _ in
+ guard let self else {
+ return
+ }
+ if !self.ignoreListBackgroundUpdates {
+ self.updateListBackground(transition: .immediate)
+ }
+ }
}
deinit {
@@ -376,6 +411,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
self.currentParams = (size, isScrollingLockedAtTop)
self.presentationDataPromise.set(.single(presentationData))
+ self.ignoreListBackgroundUpdates = true
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
@@ -388,10 +424,16 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Spring(duration: duration), directionHint: .Up)
}
}
- self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
+ self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: scrollToItem, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset + 16.0, bottom: bottomInset, right: sideInset + 16.0), duration: duration, curve: curve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
self.listNode.scrollEnabled = !isScrollingLockedAtTop
+ self.ignoreListBackgroundUpdates = false
+ self.updateListBackground(transition: transition)
+
+ self.listBackgroundView.tintColor = presentationData.theme.list.itemBlocksBackgroundColor
+ self.listMaskView.tintColor = presentationData.theme.list.blocksBackgroundColor
+
if isFirstLayout, let enclosingPeer = self.enclosingPeer, let state = self.currentState {
self.updateState(enclosingPeer: enclosingPeer, state: state, presentationData: presentationData)
}
@@ -471,6 +513,35 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
})
}
+ private func updateListBackground(transition: ContainedViewLayoutTransition) {
+ guard self.listNode.visibleSize.width != 0.0 else {
+ return
+ }
+
+ var distanceToTop: CGFloat = -100.0
+ var distanceToBottom: CGFloat = -100.0
+ switch self.listNode.visibleContentOffset() {
+ case let .known(topOffset):
+ distanceToTop = -topOffset + self.listNode.insets.top
+ default:
+ break
+ }
+ switch self.listNode.visibleBottomContentOffset() {
+ case let .known(bottomOffset):
+ distanceToBottom = -bottomOffset + self.listNode.insets.bottom
+ default:
+ break
+ }
+
+ distanceToTop = max(-100.0, distanceToTop)
+ distanceToBottom = max(-100.0, distanceToBottom)
+
+ let listBackgroundFrame = CGRect(origin: CGPoint(x: 16.0, y: distanceToTop), size: CGSize(width: max(1.0, self.listNode.visibleSize.width - 16.0 * 2.0), height: max(1.0, self.listNode.visibleSize.height - distanceToBottom - distanceToTop)))
+ let listMaskFrame = CGRect(origin: CGPoint(x: 0.0, y: listBackgroundFrame.minY), size: CGSize(width: listBackgroundFrame.width + 16.0 * 2.0, height: listBackgroundFrame.height))
+ transition.updateFrame(view: self.listBackgroundView, frame: listBackgroundFrame)
+ transition.updateFrame(view: self.listMaskView, frame: listMaskFrame)
+ }
+
func findLoadedMessage(id: MessageId) -> Message? {
return nil
}
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift
index fbfeabc8f3..c88a28f342 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift
@@ -962,7 +962,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
if self.currentPaneKey == .gifts {
backgroundColor = presentationData.theme.list.blocksBackgroundColor
} else {
- backgroundColor = presentationData.theme.list.blocksBackgroundColor.mixedWith(presentationData.theme.list.plainBackgroundColor, alpha: expansionFraction)
+ if self.currentPaneKey == .stories || self.currentPaneKey == .storyArchive {
+ backgroundColor = presentationData.theme.list.blocksBackgroundColor.mixedWith(presentationData.theme.list.plainBackgroundColor, alpha: expansionFraction)
+ } else {
+ backgroundColor = presentationData.theme.list.blocksBackgroundColor
+ }
}
self.backgroundColor = backgroundColor
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift
index 2ea49ad1ed..cf9db2dc9c 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift
@@ -682,6 +682,27 @@ extension PeerInfoScreenNode {
}
let itemsCount = items.count
+
+ /*if let cachedData = data.cachedData as? CachedUserData {
+ var hasPaidFee = false
+ if let paidMessageStars = cachedData.peerStatusSettings?.paidMessageStars, paidMessageStars != .zero {
+ hasPaidFee = true
+ }
+ if !hasPaidFee {
+ //TODO:localize
+ items.append(.action(ContextMenuActionItem(text: "Return Fee", icon: { theme in
+ generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Paid"), color: theme.contextMenu.primaryColor)
+ }, action: { [weak self] _, f in
+ f(.default)
+
+ guard let self, let peer = self.data?.peer else {
+ return
+ }
+
+ let _ = self.context.engine.peers.reinstateNoPaidMessagesException(scopePeerId: self.context.account.peerId, peerId: peer.id).startStandalone()
+ })))
+ }
+ }*/
if canSetupAutoremoveTimeout {
let strings = strongSelf.presentationData.strings
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift
index 330bba5457..48aa418161 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift
@@ -187,8 +187,8 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
super.init()
- self.addSubnode(self.backgroundNode)
- self.addSubnode(self.separatorNode)
+ //self.addSubnode(self.backgroundNode)
+ //self.addSubnode(self.separatorNode)
self.addSubnode(self.selectionPanel)
}
diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift
index 688647ec89..bd7343013d 100644
--- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift
+++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift
@@ -680,6 +680,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
var onTagTapImpl: (() -> Void)?
var didScrollImpl: (() -> Void)?
var coveringInsetOffsetUpdatedImpl: ((ContainedViewLayoutTransition) -> Void)?
+ var scrollingOffsetUpdatedImpl: ((ContainedViewLayoutTransition) -> Void)?
var onBeginFastScrollingImpl: (() -> Void)?
var getShimmerColorsImpl: (() -> SparseItemGrid.ShimmerColors)?
var updateShimmerLayersImpl: ((SparseItemGridDisplayItem) -> Void)?
@@ -1054,6 +1055,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
}
func scrollingOffsetUpdated(transition: ContainedViewLayoutTransition) {
+ self.scrollingOffsetUpdatedImpl?(transition)
}
func onBeginFastScrolling() {
@@ -1132,6 +1134,8 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
private let contextGestureContainerNode: ContextControllerSourceNode
private let itemGrid: SparseItemGrid
private let itemGridBinding: SparseItemGridBindingImpl
+ private let listBackgroundView: UIImageView
+ private let listMaskView: UIImageView
private let directMediaImageCache: DirectMediaImageCache
private var items: SparseItemGrid.Items?
private var didUpdateItemsOnce: Bool = false
@@ -1271,8 +1275,23 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
self.calendarSource = nil
}
+ self.listBackgroundView = UIImageView()
+ self.listBackgroundView.image = generateStretchableFilledCircleImage(diameter: 26.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
+ self.listMaskView = UIImageView()
+ self.listMaskView.image = generateImage(CGSize(width: 16.0 + 26.0 * 2.0 + 16.0, height: 26.0 * 2.0), rotatedContext: { size, context in
+ context.clear(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.white.cgColor)
+ context.fill(CGRect(origin: CGPoint(), size: size))
+ context.setFillColor(UIColor.clear.cgColor)
+ context.setBlendMode(.copy)
+ context.fillEllipse(in: CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0)))
+ })?.stretchableImage(withLeftCapWidth: 16 + 26, topCapHeight: 26).withRenderingMode(.alwaysTemplate)
+ self.listMaskView.image = nil
+ self.listBackgroundView.isHidden = true
+ self.listMaskView.isHidden = true
+
super.init()
-
+
if self.initialMessageIndex != nil {
self.didSetReady = true
self.ready.set(.single(true))
@@ -1342,6 +1361,13 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
strongSelf.tabBarOffsetUpdated?(transition)
}
+ self.itemGridBinding.scrollingOffsetUpdatedImpl = { [weak self] transition in
+ guard let strongSelf = self else {
+ return
+ }
+ strongSelf.updateListBackground(transition: transition)
+ }
+
var processedOnBeginFastScrolling = false
self.itemGridBinding.onBeginFastScrollingImpl = { [weak self] in
guard let strongSelf = self else {
@@ -1421,6 +1447,8 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
self.contextGestureContainerNode.isGestureEnabled = !useListItems
self.contextGestureContainerNode.addSubnode(self.itemGrid)
self.addSubnode(self.contextGestureContainerNode)
+ self.view.insertSubview(self.listBackgroundView, at: 0)
+ self.view.addSubview(self.listMaskView)
self.contextGestureContainerNode.shouldBegin = { [weak self] point in
guard let strongSelf = self else {
@@ -2239,7 +2267,8 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
fixedItemHeight = nil
}
- self.itemGrid.update(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), useSideInsets: !isList, scrollIndicatorInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: bottomInset, right: sideInset), lockScrollingAtTop: isScrollingLockedAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: nil, items: items, theme: self.itemGridBinding.chatPresentationData.theme.theme, synchronous: wasFirstTime ? .full : .none)
+ let listSideInset = isList ? sideInset + 16.0 : sideInset
+ self.itemGrid.update(size: size, insets: UIEdgeInsets(top: topInset, left: listSideInset, bottom: bottomInset, right: listSideInset), useSideInsets: !isList, scrollIndicatorInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: bottomInset, right: sideInset), lockScrollingAtTop: isScrollingLockedAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: nil, items: items, theme: self.itemGridBinding.chatPresentationData.theme.theme, synchronous: wasFirstTime ? .full : .none)
if let initialMessageIndexValue = self.initialMessageIndex, items.items.contains(where: { item in
if let _ = item as? VisualMediaItem {
return true
@@ -2257,9 +2286,44 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
}
}
}
+
+ self.listBackgroundView.isHidden = !isList
+ self.listMaskView.isHidden = !isList
+ if isList {
+ self.listBackgroundView.tintColor = presentationData.theme.list.itemBlocksBackgroundColor
+ self.listMaskView.tintColor = presentationData.theme.list.blocksBackgroundColor
+ self.updateListBackground(transition: transition)
+ }
}
}
+ private func updateListBackground(transition: ContainedViewLayoutTransition) {
+ guard let (size, topInset, _, bottomInset, _, _, _, _, _, _) = self.currentParams else {
+ return
+ }
+ let _ = bottomInset
+ guard size.width != 0.0 else {
+ return
+ }
+
+ var distanceToTop: CGFloat = -100.0
+ var distanceToBottom: CGFloat = -100.0
+
+ let scrollingOffset = self.itemGrid.scrollingOffset
+ distanceToTop = -scrollingOffset + topInset
+
+ let contentBottomOffset = self.itemGrid.contentBottomOffset
+ distanceToBottom = -contentBottomOffset + size.height
+
+ distanceToTop = max(-100.0, distanceToTop)
+ distanceToBottom = max(-100.0, distanceToBottom)
+
+ let listBackgroundFrame = CGRect(origin: CGPoint(x: 16.0, y: distanceToTop), size: CGSize(width: max(1.0, size.width - 16.0 * 2.0), height: max(1.0, size.height - distanceToBottom - distanceToTop)))
+ let listMaskFrame = CGRect(origin: CGPoint(x: 0.0, y: listBackgroundFrame.minY), size: CGSize(width: listBackgroundFrame.width + 16.0 * 2.0, height: listBackgroundFrame.height))
+ transition.updateFrame(view: self.listBackgroundView, frame: listBackgroundFrame)
+ transition.updateFrame(view: self.listMaskView, frame: listMaskFrame)
+ }
+
public func currentTopTimestamp() -> Int32? {
var timestamp: Int32?
self.itemGrid.forEachVisibleItem { item in
diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD
index 4b984b59f0..d935fec442 100644
--- a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD
+++ b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD
@@ -46,6 +46,7 @@ swift_library(
"//submodules/SegmentedControlNode",
"//submodules/TelegramUIPreferences",
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
+ "//submodules/TelegramUI/Components/EdgeEffect",
],
visibility = [
"//visibility:public",
diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsagePanelContainerComponent.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsagePanelContainerComponent.swift
index 6c50be3b61..57576d65b8 100644
--- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsagePanelContainerComponent.swift
+++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsagePanelContainerComponent.swift
@@ -561,7 +561,7 @@ final class StorageUsagePanelContainerComponent: Component {
self.panelsBackgroundLayer.backgroundColor = component.theme.list.itemBlocksBackgroundColor.cgColor
self.topPanelSeparatorLayer.backgroundColor = component.theme.list.itemBlocksSeparatorColor.cgColor
self.topPanelBackgroundView.backgroundColor = component.theme.list.itemBlocksBackgroundColor
- self.topPanelMergedBackgroundView.backgroundColor = component.theme.rootController.navigationBar.blurredBackgroundColor
+ self.topPanelMergedBackgroundView.backgroundColor = component.theme.list.blocksBackgroundColor
}
let topPanelCoverHeight: CGFloat = 10.0
diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift
index 87729ec7df..827f5feb2d 100644
--- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift
+++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift
@@ -25,6 +25,7 @@ import GalleryData
import AnimatedTextComponent
import BottomButtonPanelComponent
import GlassBackgroundComponent
+import EdgeEffect
#if DEBUG
import os.signpost
@@ -124,20 +125,17 @@ final class StorageUsageScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
let context: AccountContext
- let overNavigationContainer: UIView
let makeStorageUsageExceptionsScreen: (CacheStorageSettings.PeerStorageCategory) -> ViewController?
let peer: EnginePeer?
let ready: Promise
init(
context: AccountContext,
- overNavigationContainer: UIView,
makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?,
peer: EnginePeer?,
ready: Promise
) {
self.context = context
- self.overNavigationContainer = overNavigationContainer
self.makeStorageUsageExceptionsScreen = makeStorageUsageExceptionsScreen
self.peer = peer
self.ready = ready
@@ -755,6 +753,8 @@ final class StorageUsageScreenComponent: Component {
private let navigationEditButton = ComponentView()
private let navigationDoneButton = ComponentView()
+ private let edgeEffectView: EdgeEffectView
+
private let headerView = ComponentView()
private let headerOffsetContainer: UIView
private let headerDescriptionView = ComponentView()
@@ -768,7 +768,8 @@ final class StorageUsageScreenComponent: Component {
private var doneStatusNode: RadialStatusNode?
private let scrollContainerView: UIView
-
+ private let topContentOverlayView: UIView
+
private let pieChartView = ComponentView()
private let chartTotalLabel = ComponentView()
private let categoriesView = ComponentView()
@@ -811,7 +812,11 @@ final class StorageUsageScreenComponent: Component {
self.headerOffsetContainer = SparseContainerView()
self.scrollContainerView = UIView()
-
+
+ self.topContentOverlayView = UIView()
+ self.topContentOverlayView.isUserInteractionEnabled = false
+ self.topContentOverlayView.alpha = 0.0
+
self.scrollView = ScrollViewImpl()
self.keepDurationSectionContainerView = UIView()
@@ -823,6 +828,8 @@ final class StorageUsageScreenComponent: Component {
self.navigationRightButtonsBackground = GlassBackgroundView()
+ self.edgeEffectView = EdgeEffectView()
+
super.init(frame: frame)
self.scrollView.delaysContentTouches = true
@@ -848,6 +855,8 @@ final class StorageUsageScreenComponent: Component {
self.scrollView.layer.addSublayer(self.headerProgressBackgroundLayer)
self.scrollView.layer.addSublayer(self.headerProgressForegroundLayer)
+
+ self.addSubview(self.edgeEffectView)
}
required init?(coder: NSCoder) {
@@ -961,6 +970,7 @@ final class StorageUsageScreenComponent: Component {
if let panelContainerView = self.panelContainer.view as? StorageUsagePanelContainerComponent.View {
panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition)
}
+ self.topContentOverlayView.alpha = 1.0 - expansionDistanceFactor
var offsetFraction: CGFloat = abs(headerOffset - minOffset) / 60.0
offsetFraction = min(1.0, max(0.0, offsetFraction))
@@ -1105,10 +1115,10 @@ final class StorageUsageScreenComponent: Component {
}
if self.headerOffsetContainer.superview == nil {
- component.overNavigationContainer.addSubview(self.headerOffsetContainer)
+ self.addSubview(self.headerOffsetContainer)
}
if self.navigationRightButtonsBackground.superview == nil {
- component.overNavigationContainer.addSubview(self.navigationRightButtonsBackground)
+ self.addSubview(self.navigationRightButtonsBackground)
}
var wasLockedAtPanels = false
@@ -1222,6 +1232,11 @@ final class StorageUsageScreenComponent: Component {
self.backgroundColor = environment.theme.list.blocksBackgroundColor
+ let edgeEffectHeight: CGFloat = environment.navigationHeight + 24.0
+ let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
+ transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
+ self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: min(64.0, edgeEffectHeight), transition: transition)
+
var contentHeight: CGFloat = 0.0
let topInset: CGFloat = 19.0
@@ -2219,10 +2234,16 @@ final class StorageUsageScreenComponent: Component {
self.scrollContainerView.addSubview(panelContainerView)
}
transition.setFrame(view: panelContainerView, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: panelContainerSize))
+ if self.topContentOverlayView.superview == nil {
+ self.scrollContainerView.insertSubview(self.topContentOverlayView, belowSubview: panelContainerView)
+ }
+ self.topContentOverlayView.backgroundColor = environment.theme.list.blocksBackgroundColor
+ transition.setFrame(view: self.topContentOverlayView, frame: CGRect(origin: CGPoint(x: 0.0, y: panelContainerView.frame.minY - availableSize.height), size: availableSize))
}
contentHeight += panelContainerSize.height
} else {
self.panelContainer.view?.removeFromSuperview()
+ self.topContentOverlayView.removeFromSuperview()
}
self.ignoreScrolling = true
@@ -3315,8 +3336,6 @@ final class StorageUsageScreenComponent: Component {
public final class StorageUsageScreen: ViewControllerComponentContainer {
private let context: AccountContext
- private let overNavigationContainer: UIView
-
private let readyValue = Promise()
override public var ready: Promise {
return self.readyValue
@@ -3327,19 +3346,13 @@ public final class StorageUsageScreen: ViewControllerComponentContainer {
public init(context: AccountContext, makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?, peer: EnginePeer? = nil, focusOnItemTag: StorageUsageEntryTag? = nil) {
self.context = context
- self.overNavigationContainer = SparseContainerView()
-
let componentReady = Promise()
- super.init(context: context, component: StorageUsageScreenComponent(context: context, overNavigationContainer: self.overNavigationContainer, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .default)
+ super.init(context: context, component: StorageUsageScreenComponent(context: context, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .transparent)
if peer != nil {
self.navigationPresentation = .modal
}
- if let navigationBar = self.navigationBar {
- navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0)
- }
-
self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true)))
}
diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift
index f28a25671b..042bcda543 100644
--- a/submodules/TelegramUI/Sources/AppDelegate.swift
+++ b/submodules/TelegramUI/Sources/AppDelegate.swift
@@ -551,80 +551,94 @@ private func extractAccountManagerState(records: AccountRecordsView CallSessionManagerImplementationVersion in
- CallSessionManagerImplementationVersion(version: version, supportsVideo: supportsVideo)
- }, appData: self.regularDeviceToken.get()
- |> map { token in
- let tokenEnvironment: String
- #if DEBUG
- tokenEnvironment = "sandbox"
- #else
- tokenEnvironment = "production"
- #endif
-
- let data = buildConfig.bundleData(withAppToken: token, tokenType: "apns", tokenEnvironment: tokenEnvironment, signatureDict: signatureDict)
- if let data = data, let _ = String(data: data, encoding: .utf8) {
- } else {
- Logger.shared.log("data", "can't deserialize")
- }
- return data
- }, externalRequestVerificationStream: self.firebaseRequestVerificationSecretStream.get(), externalRecaptchaRequestVerification: { method, siteKey in
- return Signal { subscriber in
- let recaptchaClient: Promise
- if let current = self.recaptchaClientsBySiteKey[siteKey] {
- recaptchaClient = current
- } else {
- recaptchaClient = Promise()
- self.recaptchaClientsBySiteKey[siteKey] = recaptchaClient
-
- Recaptcha.fetchClient(withSiteKey: siteKey) { client, error in
- Queue.mainQueue().async {
- guard let client else {
- Logger.shared.log("App \(self.episodeId)", "RecaptchaClient creation error: \(String(describing: error)).")
- return
- }
- recaptchaClient.set(.single(client))
- }
- }
- }
+ let networkArguments = NetworkInitializationArguments(
+ apiId: apiId,
+ apiHash: apiHash,
+ languagesCategory: languagesCategory,
+ appVersion: appVersion,
+ voipMaxLayer: PresentationCallManagerImpl.voipMaxLayer,
+ voipVersions: PresentationCallManagerImpl.voipVersions(includeExperimental: true, includeReference: false).map { version, supportsVideo -> CallSessionManagerImplementationVersion in
+ CallSessionManagerImplementationVersion(version: version, supportsVideo: supportsVideo)
+ },
+ appData: self.regularDeviceToken.get() |> map { token in
+ let tokenEnvironment: String
+ #if DEBUG
+ tokenEnvironment = "sandbox"
+ #else
+ tokenEnvironment = "production"
+ #endif
- return (recaptchaClient.get()
- |> take(1)
- |> mapToSignal { recaptchaClient -> Signal in
- return Signal { subscriber in
- var recaptchaAction: RecaptchaAction?
- switch method {
- case "signup":
- recaptchaAction = RecaptchaAction.signup
- default:
- break
- }
+ let data = buildConfig.bundleData(withAppToken: token, tokenType: "apns", tokenEnvironment: tokenEnvironment, signatureDict: signatureDict)
+ if let data = data, let _ = String(data: data, encoding: .utf8) {
+ } else {
+ Logger.shared.log("data", "can't deserialize")
+ }
+ return data
+ },
+ externalRequestVerificationStream: self.firebaseRequestVerificationSecretStream.get(),
+ externalRecaptchaRequestVerification: { method, siteKey in
+ return Signal { subscriber in
+ let recaptchaClient: Promise
+ if let current = self.recaptchaClientsBySiteKey[siteKey] {
+ recaptchaClient = current
+ } else {
+ recaptchaClient = Promise()
+ self.recaptchaClientsBySiteKey[siteKey] = recaptchaClient
- guard let recaptchaAction else {
- subscriber.putNext(nil)
- subscriber.putCompletion()
-
- return EmptyDisposable
- }
- recaptchaClient.execute(withAction: recaptchaAction) { token, error in
- if let token {
- subscriber.putNext(token)
- Logger.shared.log("App \(self.episodeId)", "RecaptchaClient executed successfully")
- } else {
- subscriber.putNext(nil)
- Logger.shared.log("App \(self.episodeId)", "RecaptchaClient execute error: \(String(describing: error))")
+ Recaptcha.fetchClient(withSiteKey: siteKey) { client, error in
+ Queue.mainQueue().async {
+ guard let client else {
+ Logger.shared.log("App \(self.episodeId)", "RecaptchaClient creation error: \(String(describing: error)).")
+ return
+ }
+ recaptchaClient.set(.single(client))
}
- subscriber.putCompletion()
- }
-
- return ActionDisposable {
}
}
- |> runOn(Queue.mainQueue())
- }).startStandalone(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
- }
- |> runOn(Queue.mainQueue())
- }, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: buildConfig.isICloudEnabled)
+
+ return (recaptchaClient.get()
+ |> take(1)
+ |> mapToSignal { recaptchaClient -> Signal in
+ return Signal { subscriber in
+ var recaptchaAction: RecaptchaAction?
+ switch method {
+ case "signup":
+ recaptchaAction = RecaptchaAction.signup
+ default:
+ break
+ }
+
+ guard let recaptchaAction else {
+ subscriber.putNext(nil)
+ subscriber.putCompletion()
+
+ return EmptyDisposable
+ }
+ recaptchaClient.execute(withAction: recaptchaAction) { token, error in
+ if let token {
+ subscriber.putNext(token)
+ Logger.shared.log("App \(self.episodeId)", "RecaptchaClient executed successfully")
+ } else {
+ subscriber.putNext(nil)
+ Logger.shared.log("App \(self.episodeId)", "RecaptchaClient execute error: \(String(describing: error))")
+ }
+ subscriber.putCompletion()
+ }
+
+ return ActionDisposable {
+ }
+ }
+ |> runOn(Queue.mainQueue())
+ }).startStandalone(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
+ }
+ |> runOn(Queue.mainQueue())
+ },
+ autolockDeadine: autolockDeadine,
+ encryptionProvider: OpenSSLEncryptionProvider(),
+ deviceModelName: nil,
+ useBetaFeatures: !buildConfig.isAppStoreBuild,
+ isICloudEnabled: buildConfig.isICloudEnabled
+ )
guard let appGroupUrl = maybeAppGroupUrl else {
self.mainWindow?.presentNative(UIAlertController(title: nil, message: "Error 2", preferredStyle: .alert))
diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift
index 09e0372e91..9462b2d182 100644
--- a/submodules/TelegramUI/Sources/ChatControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift
@@ -838,6 +838,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.wrappingNode.contentNode.addSubnode(self.contentContainerNode)
self.contentContainerNode.contentNode.addSubnode(self.backgroundNode)
self.contentContainerNode.contentNode.addSubnode(self.historyNodeContainer)
+
+ self.contentContainerNode.contentNode.addSubnode(self.messageTransitionNode)
+
self.contentContainerNode.contentNode.addSubnode(self.floatingTopicsPanelContainer)
if let navigationBar = self.navigationBar {
@@ -863,8 +866,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.inputPanelContainerNode.addSubnode(self.inputPanelClippingNode)
self.inputPanelContainerNode.addSubnode(self.inputPanelOverlayNode)
self.inputPanelClippingNode.addSubnode(self.inputPanelBackgroundNode)
+
+ self.wrappingNode.contentNode.addSubnode(self.messageTransitionNode.overlayContainerNode)
- self.wrappingNode.contentNode.addSubnode(self.messageTransitionNode)
self.contentContainerNode.contentNode.addSubnode(self.navigateButtons)
self.wrappingNode.contentNode.addSubnode(self.presentationContextMarker)
self.contentContainerNode.contentNode.addSubnode(self.contentDimNode)
@@ -2028,7 +2032,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
messageTransitionNode.bounds = previousMessageTransitionNode.bounds
messageTransitionNode.transform = previousMessageTransitionNode.transform
- self.wrappingNode.contentNode.insertSubnode(self.messageTransitionNode, aboveSubnode: previousMessageTransitionNode)
+ previousMessageTransitionNode.supernode?.insertSubnode(self.messageTransitionNode, aboveSubnode: previousMessageTransitionNode)
self.emptyType = nil
self.isLoadingValue = false
@@ -2330,7 +2334,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
bottomBackgroundEdgeEffectNode = value
self.bottomBackgroundEdgeEffectNode = value
value.isUserInteractionEnabled = false
- self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
+ self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.messageTransitionNode.view)
}
}
}
@@ -2531,7 +2535,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
topBackgroundEdgeEffectNode = value
self.topBackgroundEdgeEffectNode = value
value.isUserInteractionEnabled = false
- self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view)
+ self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.messageTransitionNode.view)
}
}
}
diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift
index 0bf833a029..15bbad296b 100644
--- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift
+++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift
@@ -433,6 +433,12 @@ func chatHistoryEntriesForView(
var i = 0
let unreadEntry: ChatHistoryEntry = .UnreadEntry(maxReadIndex, presentationData)
for entry in entries {
+ if case let .MessageGroupEntry(_, messages, _) = entry {
+ if !messages.isEmpty && maxReadIndex >= messages[0].0.index {
+ i += 1
+ continue
+ }
+ }
if entry > unreadEntry {
if i != 0 {
entries.insert(unreadEntry, at: i)
diff --git a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
index 00ba078433..b990c02468 100644
--- a/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
+++ b/submodules/TelegramUI/Sources/ChatHistoryViewForLocation.swift
@@ -65,7 +65,7 @@ func chatHistoryViewForLocation(
let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData)
- if view.isLoading {
+ if view.isLoading || (view.entries.isEmpty && (view.holeEarlier || view.holeLater)) {
return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType))
}
@@ -147,7 +147,7 @@ func chatHistoryViewForLocation(
if preloaded {
return .HistoryView(view: view, type: .Generic(type: updateType), scrollPosition: nil, flashIndicators: false, originalScrollPosition: nil, initialData: combinedInitialData, id: location.id)
} else {
- if view.isLoading {
+ if view.isLoading || (view.entries.isEmpty && (view.holeEarlier || view.holeLater)) {
return .Loading(initialData: combinedInitialData, type: .Generic(type: updateType))
}
var scrollPosition: ChatHistoryViewScrollPosition?
@@ -311,7 +311,7 @@ func chatHistoryViewForLocation(
let combinedInitialData = ChatHistoryCombinedInitialData(initialData: initialData, buttonKeyboardMessage: view.topTaggedMessages.first, cachedData: cachedData, cachedDataMessages: cachedDataMessages, readStateData: readStateData)
- if view.isLoading {
+ if view.isLoading || (view.entries.isEmpty && (view.holeEarlier || view.holeLater)) {
return ChatHistoryViewUpdate.Loading(initialData: combinedInitialData, type: .Generic(type: updateType))
}
@@ -461,7 +461,7 @@ func fetchAndPreloadReplyThreadInfo(context: AccountContext, subject: ReplyThrea
case .Loading:
return nil
case let .HistoryView(view, _, _, _, _, _, _):
- if view.isLoading {
+ if view.isLoading || (view.entries.isEmpty && (view.holeEarlier || view.holeLater)) {
return nil
}
return view.entries.isEmpty
diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift
index 6f493bf636..82217835b6 100644
--- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift
@@ -337,21 +337,25 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
private let source: ChatMessageTransitionNodeImpl.Source
private let getContentAreaInScreenSpace: () -> CGRect
+ private let portalSourceView: PortalSourceView
private let scrollingContainer: ASDisplayNode
private let containerNode: ASDisplayNode
private let clippingNode: ASDisplayNode
+ private var portalTargetView: PortalView?
weak var overlayController: OverlayTransitionContainerController?
var animationEnded: (() -> Void)?
var updateAfterCompletion: Bool = false
- init(itemNode: ChatMessageItemNodeProtocol, contextSourceNode: ContextExtractedContentContainingNode, source: ChatMessageTransitionNodeImpl.Source, getContentAreaInScreenSpace: @escaping () -> CGRect) {
+ init(itemNode: ChatMessageItemNodeProtocol, contextSourceNode: ContextExtractedContentContainingNode, source: ChatMessageTransitionNodeImpl.Source, overlayContainerNode: ASDisplayNode, getContentAreaInScreenSpace: @escaping () -> CGRect) {
+ self.portalSourceView = PortalSourceView()
+
self.itemNode = itemNode
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
self.clippingNode = ASDisplayNode()
- self.clippingNode.clipsToBounds = true
+ self.clippingNode.clipsToBounds = false
self.scrollingContainer = ASDisplayNode()
self.containerNode = ASDisplayNode()
@@ -359,10 +363,18 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
self.source = source
super.init()
+
+ self.view.addSubview(self.portalSourceView)
- self.addSubnode(self.clippingNode)
+ self.portalSourceView.addSubview(self.clippingNode.view)
self.clippingNode.addSubnode(self.scrollingContainer)
self.scrollingContainer.addSubnode(self.containerNode)
+
+ if let portalTargetView = PortalView(matchPosition: true) {
+ self.portalTargetView = portalTargetView
+ self.portalSourceView.addPortal(view: portalTargetView)
+ overlayContainerNode.view.addSubview(portalTargetView.view)
+ }
}
deinit {
@@ -374,6 +386,13 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
}
func beginAnimation() {
+ if let portalTargetView = self.portalTargetView {
+ portalTargetView.view.alpha = 0.0
+ portalTargetView.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
+
+ self.portalSourceView.layer.animateAlpha(from: 0.01, to: 1.0, duration: 0.12)
+ }
+
let verticalDuration: Double = ChatMessageTransitionNodeImpl.animationDuration
let horizontalDuration: Double = verticalDuration
let delay: Double = 0.0
@@ -970,6 +989,8 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
private var decorationItemNodes: [DecorationItemNodeImpl] = []
private var messageReactionContexts: [MessageReactionContext] = []
private var customOffsetHandlers: [CustomOffsetHandlerImpl] = []
+
+ public let overlayContainerNode: ASDisplayNode
var hasScheduledTransitions: Bool {
return !self.currentPendingItems.isEmpty
@@ -983,6 +1004,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
self.listNode = listNode
self.getContentAreaInScreenSpace = getContentAreaInScreenSpace
self.onTransitionEvent = onTransitionEvent
+ self.overlayContainerNode = ASDisplayNode()
super.init()
@@ -1066,7 +1088,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran
}
if let contextSourceNode = contextSourceNode {
- let animatingItemNode = AnimatingItemNode(itemNode: itemNode, contextSourceNode: contextSourceNode, source: source, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
+ let animatingItemNode = AnimatingItemNode(itemNode: itemNode, contextSourceNode: contextSourceNode, source: source, overlayContainerNode: self.overlayContainerNode, getContentAreaInScreenSpace: self.getContentAreaInScreenSpace)
animatingItemNode.updateLayout(size: self.bounds.size)
self.animatingItemNodes.append(animatingItemNode)
diff --git a/submodules/TelegramUI/Sources/SharedWakeupManager.swift b/submodules/TelegramUI/Sources/SharedWakeupManager.swift
index e259fd8693..4e6bb1fcc7 100644
--- a/submodules/TelegramUI/Sources/SharedWakeupManager.swift
+++ b/submodules/TelegramUI/Sources/SharedWakeupManager.swift
@@ -50,6 +50,8 @@ private struct AccountTasks {
}
}
+private let backgroundTaskSubmissionDelay: Double = 10.0
+
private struct PendingMediaUploadKey: Hashable {
let accountId: AccountRecordId
let messageId: MessageId
@@ -99,7 +101,8 @@ public final class SharedWakeupManager {
private var backgroundProcessingTaskId: String?
private var backgroundProcessingTaskLaunched: Bool = false
private var backgroundProcessingTaskCancellationRequestedByApp: Bool = false
-
+ private var pendingBackgroundProcessingTaskTimer: SwiftSignalKit.Timer?
+
private var pendingStoryUploadsByKey: [PendingStoryUploadKey: Float] = [:]
private var pendingStoryUploadStatusesByKey: [PendingStoryUploadKey: PendingStoryUploadStatus] = [:]
private var backgroundStoryProcessingTaskProgressByKey: [PendingStoryUploadKey: Float] = [:]
@@ -107,7 +110,8 @@ public final class SharedWakeupManager {
private var backgroundStoryProcessingTaskId: String?
private var backgroundStoryProcessingTaskLaunched: Bool = false
private var backgroundStoryProcessingTaskCancellationRequestedByApp: Bool = false
-
+ private var pendingBackgroundStoryProcessingTaskTimer: SwiftSignalKit.Timer?
+
public init(beginBackgroundTask: @escaping (String, @escaping () -> Void) -> UIBackgroundTaskIdentifier?, endBackgroundTask: @escaping (UIBackgroundTaskIdentifier) -> Void, backgroundTimeRemaining: @escaping () -> Double, acquireIdleExtension: @escaping () -> Disposable?, activeAccounts: Signal<(primary: Account?, accounts: [(AccountRecordId, Account)]), NoError>, liveLocationPolling: Signal, watchTasks: Signal, inForeground: Signal, hasActiveAudioSession: Signal, notificationManager: SharedNotificationManager?, mediaManager: MediaManager, callManager: PresentationCallManager?, accountUserInterfaceInUse: @escaping (AccountRecordId) -> Signal, presentationData: @escaping () -> PresentationData?) {
assert(Queue.mainQueue().isCurrent())
@@ -155,6 +159,10 @@ public final class SharedWakeupManager {
}
strongSelf.allowBackgroundTimeExtensionDeadlineTimer?.invalidate()
strongSelf.allowBackgroundTimeExtensionDeadlineTimer = nil
+ strongSelf.pendingBackgroundProcessingTaskTimer?.invalidate()
+ strongSelf.pendingBackgroundProcessingTaskTimer = nil
+ strongSelf.pendingBackgroundStoryProcessingTaskTimer?.invalidate()
+ strongSelf.pendingBackgroundStoryProcessingTaskTimer = nil
}
strongSelf.updateBackgroundProcessingTaskStateFromPendingMediaUploads()
strongSelf.updateBackgroundProcessingTaskStateFromPendingStoryUploads()
@@ -356,6 +364,8 @@ public final class SharedWakeupManager {
self.pendingStoryUploadsDisposable?.dispose()
self.managedPausedInBackgroundPlayer?.dispose()
self.keepIdleDisposable?.dispose()
+ self.pendingBackgroundProcessingTaskTimer?.invalidate()
+ self.pendingBackgroundStoryProcessingTaskTimer?.invalidate()
if let (taskId, _, timer) = self.currentTask {
timer.invalidate()
self.endBackgroundTask(taskId)
@@ -369,19 +379,30 @@ public final class SharedWakeupManager {
let shouldHaveTask = !self.pendingMediaUploadsByKey.isEmpty && !self.inForeground
let hadTask = self.backgroundProcessingTaskId != nil
-
+
if shouldHaveTask {
- if !hadTask {
- self.startBackgroundProcessingTaskIfNeeded()
+ if !hadTask && self.pendingBackgroundProcessingTaskTimer == nil {
+ let timer = SwiftSignalKit.Timer(timeout: backgroundTaskSubmissionDelay, repeat: false, completion: { [weak self] in
+ guard let self else {
+ return
+ }
+ self.pendingBackgroundProcessingTaskTimer = nil
+ self.startBackgroundProcessingTaskIfNeeded()
+ }, queue: .mainQueue())
+ self.pendingBackgroundProcessingTaskTimer = timer
+ timer.start()
}
} else {
+ self.pendingBackgroundProcessingTaskTimer?.invalidate()
+ self.pendingBackgroundProcessingTaskTimer = nil
+
if let backgroundProcessingTaskId = self.backgroundProcessingTaskId {
if !self.backgroundProcessingTaskCancellationRequestedByApp {
self.backgroundProcessingTaskCancellationRequestedByApp = true
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: backgroundProcessingTaskId)
Logger.shared.log("Wakeup", "Requested BG task cancellation by app: \(backgroundProcessingTaskId)")
}
-
+
if !self.backgroundProcessingTaskLaunched {
self.backgroundProcessingTaskId = nil
self.backgroundProcessingTaskProgressByKey = [:]
@@ -399,19 +420,30 @@ public final class SharedWakeupManager {
let shouldHaveTask = !self.pendingStoryUploadStatusesByKey.isEmpty && !self.inForeground
let hadTask = self.backgroundStoryProcessingTaskId != nil
-
+
if shouldHaveTask {
- if !hadTask {
- self.startBackgroundStoryProcessingTaskIfNeeded()
+ if !hadTask && self.pendingBackgroundStoryProcessingTaskTimer == nil {
+ let timer = SwiftSignalKit.Timer(timeout: backgroundTaskSubmissionDelay, repeat: false, completion: { [weak self] in
+ guard let self else {
+ return
+ }
+ self.pendingBackgroundStoryProcessingTaskTimer = nil
+ self.startBackgroundStoryProcessingTaskIfNeeded()
+ }, queue: .mainQueue())
+ self.pendingBackgroundStoryProcessingTaskTimer = timer
+ timer.start()
}
} else {
+ self.pendingBackgroundStoryProcessingTaskTimer?.invalidate()
+ self.pendingBackgroundStoryProcessingTaskTimer = nil
+
if let backgroundStoryProcessingTaskId = self.backgroundStoryProcessingTaskId {
if !self.backgroundStoryProcessingTaskCancellationRequestedByApp {
self.backgroundStoryProcessingTaskCancellationRequestedByApp = true
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: backgroundStoryProcessingTaskId)
Logger.shared.log("Wakeup", "Requested story BG task cancellation by app: \(backgroundStoryProcessingTaskId)")
}
-
+
if !self.backgroundStoryProcessingTaskLaunched {
self.backgroundStoryProcessingTaskId = nil
self.backgroundStoryProcessingTaskProgressByKey = [:]