diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index 579c99d4a2..a0eb02b74e 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -659,6 +659,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie self.chatListDisplayNode.chatListNode.updateState { state in var state = state state.editing = true + state.peerIdWithRevealedOptions = nil return state } } diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index a42a9492e5..311814e5f2 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -795,11 +795,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } if let reorderControlSizeAndApply = reorderControlSizeAndApply { + let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0.width, y: 0.0), size: reorderControlSizeAndApply.0) if strongSelf.reorderControlNode == nil { let reorderControlNode = reorderControlSizeAndApply.1() strongSelf.reorderControlNode = reorderControlNode strongSelf.addSubnode(reorderControlNode) - let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0.width, y: 0.0), size: reorderControlSizeAndApply.0) reorderControlNode.frame = reorderControlFrame reorderControlNode.alpha = 0.0 transition.updateAlpha(node: reorderControlNode, alpha: 1.0) @@ -809,6 +809,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.updateAlpha(node: strongSelf.badgeBackgroundNode, alpha: 0.0) transition.updateAlpha(node: strongSelf.mentionBadgeNode, alpha: 0.0) transition.updateAlpha(node: strongSelf.statusNode, alpha: 0.0) + } else if let reorderControlNode = strongSelf.reorderControlNode { + let _ = reorderControlSizeAndApply.1() + transition.updateFrame(node: reorderControlNode, frame: reorderControlFrame) } } else if let reorderControlNode = strongSelf.reorderControlNode { strongSelf.reorderControlNode = nil @@ -1045,7 +1048,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) - strongSelf.setRevealOptions((left: peerLeftRevealOptions, right: peerRevealOptions)) + if item.editing { + strongSelf.setRevealOptions((left: [], right: [])) + } else { + strongSelf.setRevealOptions((left: peerLeftRevealOptions, right: peerRevealOptions)) + } strongSelf.setRevealOptionsOpened(item.hasActiveRevealControls, animated: true) } }) diff --git a/TelegramUI/ChatListSelection.swift b/TelegramUI/ChatListSelection.swift index 4f041483ea..757061200a 100644 --- a/TelegramUI/ChatListSelection.swift +++ b/TelegramUI/ChatListSelection.swift @@ -39,8 +39,8 @@ func chatListSelectionOptions(postbox: Postbox, peerIds: Set) -> Signal< if let unreadCounts = view.views[key] as? UnreadMessageCountsView { loop: for entry in unreadCounts.entries { switch entry { - case let .peer(_, count): - if count != 0 { + case let .peer(_, state): + if let state = state, state.isUnread { hasUnread = true break loop } diff --git a/TelegramUI/ChatMessageInteractiveMediaNode.swift b/TelegramUI/ChatMessageInteractiveMediaNode.swift index ce3f24738a..a44541d503 100644 --- a/TelegramUI/ChatMessageInteractiveMediaNode.swift +++ b/TelegramUI/ChatMessageInteractiveMediaNode.swift @@ -318,7 +318,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode { if file.isSticker { updateImageSignal = chatMessageSticker(account: account, file: file, small: false) } else { - updateImageSignal = chatMessageVideo(postbox: account.postbox, videoReference: .message(message: MessageReference(message), media: file)) + updateImageSignal = mediaGridMessageVideo(postbox: account.postbox, videoReference: .message(message: MessageReference(message), media: file), onlyFullSize: currentMedia != nil) } } diff --git a/TelegramUI/FastBlur.h b/TelegramUI/FastBlur.h index fa372b6b29..6e5b38f304 100644 --- a/TelegramUI/FastBlur.h +++ b/TelegramUI/FastBlur.h @@ -4,6 +4,7 @@ #import void telegramFastBlur(int imageWidth, int imageHeight, int imageStride, void *pixels); +void telegramFastBlurMore(int imageWidth, int imageHeight, int imageStride, void *pixels); void stickerThumbnailAlphaBlur(int imageWidth, int imageHeight, int imageStride, void *pixels); #endif diff --git a/TelegramUI/FastBlur.m b/TelegramUI/FastBlur.m index e25537789d..e196f30436 100644 --- a/TelegramUI/FastBlur.m +++ b/TelegramUI/FastBlur.m @@ -103,6 +103,103 @@ yi += stride; free(rgb); } +void telegramFastBlurMore(int imageWidth, int imageHeight, int imageStride, void *pixels) +{ + uint8_t *pix = (uint8_t *)pixels; + const int w = imageWidth; + const int h = imageHeight; + const int stride = imageStride; + const int radius = 7; + const int r1 = radius + 1; + const int div = radius * 2 + 1; + + if (radius > 15 || div >= w || div >= h) + { + return; + } + + uint64_t *rgb = malloc(imageStride * imageHeight * sizeof(uint64_t)); + + int x, y, i; + + int yw = 0; + const int we = w - r1; + for (y = 0; y < h; y++) { + uint64_t cur = get_colors (&pix[yw]); + uint64_t rgballsum = -radius * cur; + uint64_t rgbsum = cur * ((r1 * (r1 + 1)) >> 1); + + for (i = 1; i <= radius; i++) { + uint64_t cur = get_colors (&pix[yw + i * 4]); + rgbsum += cur * (r1 - i); + rgballsum += cur; + } + + x = 0; + +#define update(start, middle, end) \ +rgb[y * w + x] = (rgbsum >> 6) & 0x00FF00FF00FF00FF; \ +\ +rgballsum += get_colors (&pix[yw + (start) * 4]) - \ +2 * get_colors (&pix[yw + (middle) * 4]) + \ +get_colors (&pix[yw + (end) * 4]); \ +rgbsum += rgballsum; \ +x++; \ + + while (x < r1) { + update (0, x, x + r1); + } + while (x < we) { + update (x - r1, x, x + r1); + } + while (x < w) { + update (x - r1, x, w - 1); + } +#undef update + + yw += stride; + } + + const int he = h - r1; + for (x = 0; x < w; x++) { + uint64_t rgballsum = -radius * rgb[x]; + uint64_t rgbsum = rgb[x] * ((r1 * (r1 + 1)) >> 1); + for (i = 1; i <= radius; i++) { + rgbsum += rgb[i * w + x] * (r1 - i); + rgballsum += rgb[i * w + x]; + } + + y = 0; + int yi = x * 4; + +#define update(start, middle, end) \ +int64_t res = rgbsum >> 6; \ +pix[yi] = (uint8_t)res; \ +pix[yi + 1] = (uint8_t)(res >> 16); \ +pix[yi + 2] = (uint8_t)(res >> 32); \ +\ +rgballsum += rgb[x + (start) * w] - \ +2 * rgb[x + (middle) * w] + \ +rgb[x + (end) * w]; \ +rgbsum += rgballsum; \ +y++; \ +yi += stride; + + while (y < r1) { + update (0, y, y + r1); + } + while (y < he) { + update (y - r1, y, y + r1); + } + while (y < h) { + update (y - r1, y, h - 1); + } +#undef update + } + + free(rgb); +} + void stickerThumbnailAlphaBlur(int imageWidth, int imageHeight, int imageStride, void *pixels) { vImage_Buffer srcBuffer; srcBuffer.width = imageWidth; diff --git a/TelegramUI/PhotoResources.swift b/TelegramUI/PhotoResources.swift index 4d857428f6..b93e0b8dd8 100644 --- a/TelegramUI/PhotoResources.swift +++ b/TelegramUI/PhotoResources.swift @@ -238,7 +238,7 @@ private func chatMessageImageFileThumbnailDatas(account: Account, fileReference: return signal } -private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false) -> Signal<(Data?, (Data, String)?, Bool), NoError> { +private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaReference, thumbnailSize: Bool = false, onlyFullSize: Bool = false) -> Signal<(Data?, (Data, String)?, Bool), NoError> { if let smallestRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { let thumbnailResource = smallestRepresentation.resource let fullSizeResource = fileReference.media.resource @@ -256,15 +256,20 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef } else { let fetchedThumbnail = fetchedMediaResource(postbox: postbox, reference: fileReference.resourceReference(thumbnailResource), statsCategory: .video) - let thumbnail = Signal { subscriber in - let fetchedDisposable = fetchedThumbnail.start() - let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) - }, error: subscriber.putError, completed: subscriber.putCompletion) - - return ActionDisposable { - fetchedDisposable.dispose() - thumbnailDisposable.dispose() + let thumbnail: Signal + if onlyFullSize { + thumbnail = .single(nil) + } else { + thumbnail = Signal { subscriber in + let fetchedDisposable = fetchedThumbnail.start() + let thumbnailDisposable = postbox.mediaBox.resourceData(thumbnailResource).start(next: { next in + subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) + }, error: subscriber.putError, completed: subscriber.putCompletion) + + return ActionDisposable { + fetchedDisposable.dispose() + thumbnailDisposable.dispose() + } } } @@ -293,8 +298,12 @@ private func chatMessageVideoDatas(postbox: Postbox, fileReference: FileMediaRef } } } - } |> filter({ _ in - return true//$0.0 != nil || $0.1 != nil || $0.2 + } |> filter({ + if onlyFullSize { + return $0.1 != nil || $0.2 + } else { + return true//$0.0 != nil || $0.1 != nil || $0.2 + } }) return signal @@ -1396,14 +1405,14 @@ func gifPaneVideoThumbnail(account: Account, videoReference: FileMediaReference) } } -func mediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - return internalMediaGridMessageVideo(postbox: postbox, videoReference: videoReference) +func mediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, onlyFullSize: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + return internalMediaGridMessageVideo(postbox: postbox, videoReference: videoReference, onlyFullSize: onlyFullSize) |> map { return $0.1 } } -func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> { +func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaReference, imageReference: ImageMediaReference? = nil, onlyFullSize: Bool = false) -> Signal<(() -> CGSize?, (TransformImageArguments) -> DrawingContext?), NoError> { let signal: Signal<(Data?, (Data, String)?, Bool), NoError> if let imageReference = imageReference { signal = chatMessagePhotoDatas(postbox: postbox, photoReference: imageReference, tryAdditionalRepresentations: true) @@ -1411,7 +1420,7 @@ func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaRe return (thumbnailData, fullSizeData.flatMap({ ($0, "") }), fullSizeComplete) } } else { - signal = chatMessageVideoDatas(postbox: postbox, fileReference: videoReference) + signal = chatMessageVideoDatas(postbox: postbox, fileReference: videoReference, onlyFullSize: onlyFullSize) } return signal @@ -1431,7 +1440,6 @@ func internalMediaGridMessageVideo(postbox: Postbox, videoReference: FileMediaRe } return nil }, { arguments in - assertNotOnMainThread() let context = DrawingContext(size: arguments.drawingSize, clear: true) let drawingRect = arguments.drawingRect @@ -2008,7 +2016,10 @@ func chatMessageImageFile(account: Account, fileReference: FileMediaReference, t var blurredThumbnailImage: UIImage? if let thumbnailImage = thumbnailImage { let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height) - let thumbnailContextSize = thumbnailSize.aspectFitted(CGSize(width: 150.0, height: 150.0)) + + let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0)) + + let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) thumbnailContext.withFlippedContext { c in c.interpolationQuality = .none @@ -2016,7 +2027,25 @@ func chatMessageImageFile(account: Account, fileReference: FileMediaReference, t } telegramFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - blurredThumbnailImage = thumbnailContext.generateImage() + var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) + if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { + thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) + } + + if thumbnailContextFittingSize.width > thumbnailContextSize.width { + let additionalContextSize = thumbnailContextFittingSize + let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) + additionalBlurContext.withFlippedContext { c in + c.interpolationQuality = .default + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) + } + } + telegramFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) + blurredThumbnailImage = additionalBlurContext.generateImage() + } else { + blurredThumbnailImage = thumbnailContext.generateImage() + } } context.withFlippedContext { c in @@ -2207,15 +2236,35 @@ func chatAvatarGalleryPhoto(account: Account, representations: [(TelegramMediaIm var blurredThumbnailImage: UIImage? if let thumbnailImage = thumbnailImage { let thumbnailSize = CGSize(width: thumbnailImage.width, height: thumbnailImage.height) - let thumbnailContextSize = thumbnailSize.aspectFitted(CGSize(width: 150.0, height: 150.0)) + + let initialThumbnailContextFittingSize = fittedSize.fitted(CGSize(width: 100.0, height: 100.0)) + + let thumbnailContextSize = thumbnailSize.aspectFitted(initialThumbnailContextFittingSize) let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) thumbnailContext.withFlippedContext { c in - c.interpolationQuality = .none c.draw(thumbnailImage, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) } telegramFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) - blurredThumbnailImage = thumbnailContext.generateImage() + var thumbnailContextFittingSize = CGSize(width: floor(arguments.drawingSize.width * 0.5), height: floor(arguments.drawingSize.width * 0.5)) + if thumbnailContextFittingSize.width < 150.0 || thumbnailContextFittingSize.height < 150.0 { + thumbnailContextFittingSize = thumbnailContextFittingSize.aspectFilled(CGSize(width: 150.0, height: 150.0)) + } + + if thumbnailContextFittingSize.width > thumbnailContextSize.width { + let additionalContextSize = thumbnailContextFittingSize + let additionalBlurContext = DrawingContext(size: additionalContextSize, scale: 1.0) + additionalBlurContext.withFlippedContext { c in + c.interpolationQuality = .default + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: additionalContextSize)) + } + } + telegramFastBlur(Int32(additionalContextSize.width), Int32(additionalContextSize.height), Int32(additionalBlurContext.bytesPerRow), additionalBlurContext.bytes) + blurredThumbnailImage = additionalBlurContext.generateImage() + } else { + blurredThumbnailImage = thumbnailContext.generateImage() + } } context.withFlippedContext { c in @@ -2226,7 +2275,7 @@ func chatAvatarGalleryPhoto(account: Account, representations: [(TelegramMediaIm c.setBlendMode(.copy) if let blurredThumbnailImage = blurredThumbnailImage, let cgImage = blurredThumbnailImage.cgImage { - c.interpolationQuality = .low + c.interpolationQuality = .default drawImage(context: c, image: cgImage, orientation: imageOrientation, in: fittedRect) c.setBlendMode(.normal) } diff --git a/TelegramUI/PresentationCall.swift b/TelegramUI/PresentationCall.swift index 443676dc32..15e3e9f22c 100644 --- a/TelegramUI/PresentationCall.swift +++ b/TelegramUI/PresentationCall.swift @@ -391,7 +391,7 @@ public final class PresentationCall { self.reportedIncomingCall = true self.callKitIntegration?.reportIncomingCall(uuid: self.internalId, handle: "\(self.peerId.id)", displayTitle: self.peer?.displayTitle ?? "Unknown", completion: { [weak self] error in if let error = error { - if error.domain == "com.apple.CallKit.error.incomingcall" && error.code == -3 { + if error.domain == "com.apple.CallKit.error.incomingcall" && (error.code == -3 || error.code == 3) { Logger.shared.log("PresentationCall", "reportIncomingCall device in DND mode") } else { Logger.shared.log("PresentationCall", "reportIncomingCall error \(error)") @@ -486,33 +486,40 @@ public final class PresentationCall { } if let presentationState = presentationState { self.statePromise.set(presentationState) - self.updateTone(presentationState) + self.updateTone(presentationState, previous: previous) } } - private func updateTone(_ state: PresentationCallState) { + private func updateTone(_ state: PresentationCallState, previous: CallSession?) { var tone: PresentationCallTone? - switch state { - case .connecting: - tone = .connecting - case .requesting(true): - tone = .ringing - case let .terminated(reason): - if let reason = reason { - switch reason { - case let .ended(type): - switch type { - case .busy: - tone = .busy - case .hungUp, .missed: - tone = .ended + if let previous = previous { + switch previous.state { + case .accepting, .active, .dropping, .requesting: + switch state { + case .connecting: + tone = .connecting + case .requesting(true): + tone = .ringing + case let .terminated(reason): + if let reason = reason { + switch reason { + case let .ended(type): + switch type { + case .busy: + tone = .busy + case .hungUp, .missed: + tone = .ended + } + case .error: + tone = .failed + } } - case .error: - tone = .failed + default: + break } - } - default: - break + default: + break + } } if tone != self.toneRenderer?.tone { if let tone = tone { diff --git a/TelegramUI/PresentationCallToneData.swift b/TelegramUI/PresentationCallToneData.swift index 8a43d9e818..4be4fb3651 100644 --- a/TelegramUI/PresentationCallToneData.swift +++ b/TelegramUI/PresentationCallToneData.swift @@ -74,8 +74,12 @@ enum PresentationCallTone { var loopCount: Int? { switch self { - case .busy, .failed, .ended: + case .busy: return 3 + case .failed: + return 1 + case .ended: + return 1 default: return nil } diff --git a/TelegramUI/UrlHandling.swift b/TelegramUI/UrlHandling.swift index c727736c6c..c7f25c19ed 100644 --- a/TelegramUI/UrlHandling.swift +++ b/TelegramUI/UrlHandling.swift @@ -169,9 +169,9 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } else { if let peer = peer as? TelegramUser, peer.botInfo != nil { - return .peer(peer.id, .info) - } else { return .peer(peer.id, .chat(textInputState: nil, messageId: nil)) + } else { + return .peer(peer.id, .info) } } } else {