Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2019-07-29 19:17:17 +02:00
commit f6858dfc94
30 changed files with 2458 additions and 2272 deletions

View File

@ -4485,6 +4485,8 @@ Any member of this group will be able to see messages in the channel.";
"Chat.AttachmentMultipleFilesDisabled" = "Slowmode is enabled. You can't send multiple files at once.";
"Chat.AttachmentMultipleForwardDisabled" = "Slowmode is enabled. You can't forward multiple messages at once.";
"Chat.MultipleTextMessagesDisabled" = "Slowmode is enabled. You can't send multiple messages at once.";
"Share.MultipleMessagesDisabled" = "Slowmode is enabled. You can't send multiple messages at once.";
"Chat.SlowmodeSendError" = "Slowmode is enabled.";
"StickerPacksSettings.AnimatedStickersInfo" = "Animated stickers in a chat will play continuously.";
"Conversation.Owner" = "owner";

View File

@ -622,6 +622,12 @@ final class FFMpegMediaFrameSourceContext: NSObject {
}
if let closestFrame = closestFrame {
actualPts = closestFrame.pts
} else {
if let videoStream = initializedState.videoStream {
actualPts = videoStream.duration
} else {
actualPts = extraVideoFrames.last!.pts
}
}
}
if let audioStream = initializedState.audioStream {

View File

@ -784,7 +784,7 @@ private final class MediaPlayerContext {
}
} else if let worstStatus = worstStatus, case let .finished(finishedAt) = worstStatus, finishedAt.isFinite {
let nextTickDelay = max(0.0, finishedAt - timestamp) / self.baseRate
if nextTickDelay.isLessThanOrEqualTo(0.0) || timestamp.isEqual(to: 0.0) {
if nextTickDelay.isLessThanOrEqualTo(0.0) {
rate = 0.0
performActionAtEndNow = true
} else {

View File

@ -655,74 +655,97 @@ struct ctr_state {
if (!_addedControlHeader) {
_addedControlHeader = true;
uint8_t controlBytes[64];
arc4random_buf(controlBytes, 64);
int32_t controlVersion;
if (_useIntermediateFormat) {
controlVersion = 0xdddddddd;
} else {
controlVersion = 0xefefefef;
}
memcpy(controlBytes + 56, &controlVersion, 4);
int16_t datacenterTag = (int16_t)_datacenterTag;
memcpy(controlBytes + 60, &datacenterTag, 2);
uint8_t controlBytesReversed[64];
for (int i = 0; i < 64; i++) {
controlBytesReversed[i] = controlBytes[64 - 1 - i];
}
NSData *aesKey = [[NSData alloc] initWithBytes:controlBytes + 8 length:32];
NSData *aesIv = [[NSData alloc] initWithBytes:controlBytes + 8 + 32 length:16];
NSData *incomingAesKey = [[NSData alloc] initWithBytes:controlBytesReversed + 8 length:32];
NSData *incomingAesIv = [[NSData alloc] initWithBytes:controlBytesReversed + 8 + 32 length:16];
NSData *effectiveSecret = nil;
if (_mtpSecret != nil) {
effectiveSecret = _mtpSecret.secret;
}
if (effectiveSecret.length != 16 && effectiveSecret.length != 17) {
effectiveSecret = nil;
}
if (effectiveSecret) {
NSMutableData *aesKeyData = [[NSMutableData alloc] init];
[aesKeyData appendData:aesKey];
if (effectiveSecret.length == 16) {
[aesKeyData appendData:effectiveSecret];
} else if (effectiveSecret.length == 17) {
[aesKeyData appendData:[effectiveSecret subdataWithRange:NSMakeRange(1, effectiveSecret.length - 1)]];
}
NSData *aesKeyHash = MTSha256(aesKeyData);
aesKey = [aesKeyHash subdataWithRange:NSMakeRange(0, 32)];
for (int retryCount = 0; retryCount < 10; retryCount++) {
uint8_t controlBytes[64];
arc4random_buf(controlBytes, 64);
NSMutableData *incomingAesKeyData = [[NSMutableData alloc] init];
[incomingAesKeyData appendData:incomingAesKey];
if (effectiveSecret.length == 16) {
[incomingAesKeyData appendData:effectiveSecret];
} else if (effectiveSecret.length == 17) {
[incomingAesKeyData appendData:[effectiveSecret subdataWithRange:NSMakeRange(1, effectiveSecret.length - 1)]];
int32_t controlVersion;
if (_useIntermediateFormat) {
controlVersion = 0xdddddddd;
} else {
controlVersion = 0xefefefef;
}
NSData *incomingAesKeyHash = MTSha256(incomingAesKeyData);
incomingAesKey = [incomingAesKeyHash subdataWithRange:NSMakeRange(0, 32)];
memcpy(controlBytes + 56, &controlVersion, 4);
int16_t datacenterTag = (int16_t)_datacenterTag;
memcpy(controlBytes + 60, &datacenterTag, 2);
uint8_t controlBytesReversed[64];
for (int i = 0; i < 64; i++) {
controlBytesReversed[i] = controlBytes[64 - 1 - i];
}
NSData *aesKey = [[NSData alloc] initWithBytes:controlBytes + 8 length:32];
NSData *aesIv = [[NSData alloc] initWithBytes:controlBytes + 8 + 32 length:16];
NSData *incomingAesKey = [[NSData alloc] initWithBytes:controlBytesReversed + 8 length:32];
NSData *incomingAesIv = [[NSData alloc] initWithBytes:controlBytesReversed + 8 + 32 length:16];
NSData *effectiveSecret = nil;
if (_mtpSecret != nil) {
effectiveSecret = _mtpSecret.secret;
}
if (effectiveSecret.length != 16 && effectiveSecret.length != 17) {
effectiveSecret = nil;
}
if (effectiveSecret) {
NSMutableData *aesKeyData = [[NSMutableData alloc] init];
[aesKeyData appendData:aesKey];
if (effectiveSecret.length == 16) {
[aesKeyData appendData:effectiveSecret];
} else if (effectiveSecret.length == 17) {
[aesKeyData appendData:[effectiveSecret subdataWithRange:NSMakeRange(1, effectiveSecret.length - 1)]];
}
NSData *aesKeyHash = MTSha256(aesKeyData);
aesKey = [aesKeyHash subdataWithRange:NSMakeRange(0, 32)];
NSMutableData *incomingAesKeyData = [[NSMutableData alloc] init];
[incomingAesKeyData appendData:incomingAesKey];
if (effectiveSecret.length == 16) {
[incomingAesKeyData appendData:effectiveSecret];
} else if (effectiveSecret.length == 17) {
[incomingAesKeyData appendData:[effectiveSecret subdataWithRange:NSMakeRange(1, effectiveSecret.length - 1)]];
}
NSData *incomingAesKeyHash = MTSha256(incomingAesKeyData);
incomingAesKey = [incomingAesKeyHash subdataWithRange:NSMakeRange(0, 32)];
}
MTAesCtr *outgoingAesCtr = [[MTAesCtr alloc] initWithKey:aesKey.bytes keyLength:32 iv:aesIv.bytes decrypt:false];
MTAesCtr *incomingAesCtr = [[MTAesCtr alloc] initWithKey:incomingAesKey.bytes keyLength:32 iv:incomingAesIv.bytes decrypt:false];
uint8_t encryptedControlBytes[64];
[outgoingAesCtr encryptIn:controlBytes out:encryptedControlBytes len:64];
uint32_t intHeader = 0;
memcpy(&intHeader, encryptedControlBytes, 4);
if (retryCount == 9) {
assert(false);
} else {
if (intHeader == 0x44414548 ||
intHeader == 0x54534f50 ||
intHeader == 0x20544547 ||
intHeader == 0x4954504f ||
intHeader == 0xdddddddd ||
intHeader == 0xeeeeeeee ||
intHeader == 0x02010316) {
continue;
}
}
NSMutableData *outData = [[NSMutableData alloc] initWithLength:64 + packetData.length];
memcpy(outData.mutableBytes, controlBytes, 56);
memcpy(outData.mutableBytes + 56, encryptedControlBytes + 56, 8);
[outgoingAesCtr encryptIn:packetData.bytes out:outData.mutableBytes + 64 len:packetData.length];
_incomingAesCtr = incomingAesCtr;
_outgoingAesCtr = outgoingAesCtr;
[completeData appendData:outData];
break;
}
_outgoingAesCtr = [[MTAesCtr alloc] initWithKey:aesKey.bytes keyLength:32 iv:aesIv.bytes decrypt:false];
_incomingAesCtr = [[MTAesCtr alloc] initWithKey:incomingAesKey.bytes keyLength:32 iv:incomingAesIv.bytes decrypt:false];
uint8_t encryptedControlBytes[64];
[_outgoingAesCtr encryptIn:controlBytes out:encryptedControlBytes len:64];
NSMutableData *outData = [[NSMutableData alloc] initWithLength:64 + packetData.length];
memcpy(outData.mutableBytes, controlBytes, 56);
memcpy(outData.mutableBytes + 56, encryptedControlBytes + 56, 8);
[_outgoingAesCtr encryptIn:packetData.bytes out:outData.mutableBytes + 64 len:packetData.length];
[completeData appendData:outData];
} else {
NSMutableData *encryptedData = [[NSMutableData alloc] initWithLength:packetData.length];
[_outgoingAesCtr encryptIn:packetData.bytes out:encryptedData.mutableBytes len:packetData.length];

View File

@ -18,10 +18,6 @@ import Foundation
public struct PendingMessageStatus: Equatable {
public let isRunning: Bool
public let progress: Float
public static func ==(lhs: PendingMessageStatus, rhs: PendingMessageStatus) -> Bool {
return lhs.isRunning == rhs.isRunning && lhs.progress.isEqual(to: rhs.progress)
}
}
private enum PendingMessageState {
@ -58,7 +54,8 @@ private final class PendingMessageContext {
var contentType: PendingMessageUploadedContentType? = nil
let activityDisposable = MetaDisposable()
var status: PendingMessageStatus?
var statusSubscribers = Bag<(PendingMessageStatus?) -> Void>()
var error: PendingMessageFailureReason?
var statusSubscribers = Bag<(PendingMessageStatus?, PendingMessageFailureReason?) -> Void>()
var forcedReuploadOnce: Bool = false
}
@ -66,6 +63,7 @@ public enum PendingMessageFailureReason {
case flood
case publicBan
case mediaRestricted
case slowmodeActive
}
private func reasonForError(_ error: String) -> PendingMessageFailureReason? {
@ -75,6 +73,8 @@ private func reasonForError(_ error: String) -> PendingMessageFailureReason? {
return .publicBan
} else if error.hasPrefix("CHAT_SEND_") && error.hasSuffix("_FORBIDDEN") {
return .mediaRestricted
} else if error.hasPrefix("SLOWMODE_WAIT") {
return .slowmodeActive
} else {
return nil
}
@ -202,7 +202,7 @@ public final class PendingMessageManager {
if context.status != nil {
context.status = nil
for subscriber in context.statusSubscribers.copyItems() {
subscriber(nil)
subscriber(nil, context.error)
}
}
@ -261,7 +261,7 @@ public final class PendingMessageManager {
}
}
public func pendingMessageStatus(_ id: MessageId) -> Signal<PendingMessageStatus?, NoError> {
public func pendingMessageStatus(_ id: MessageId) -> Signal<(PendingMessageStatus?, PendingMessageFailureReason?), NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -274,11 +274,11 @@ public final class PendingMessageManager {
self.messageContexts[id] = messageContext
}
let index = messageContext.statusSubscribers.add({ status in
subscriber.putNext(status)
let index = messageContext.statusSubscribers.add({ status, error in
subscriber.putNext((status, error))
})
subscriber.putNext(messageContext.status)
subscriber.putNext((messageContext.status, messageContext.error))
disposable.set(ActionDisposable {
self.queue.async {
@ -334,7 +334,7 @@ public final class PendingMessageManager {
if status != messageContext.status {
messageContext.status = status
for subscriber in messageContext.statusSubscribers.copyItems() {
subscriber(status)
subscriber(messageContext.status, messageContext.error)
}
}
}
@ -445,23 +445,7 @@ public final class PendingMessageManager {
return .progress(1.0)
}
messages[0].0.sendDisposable.set((sendMessage
|> deliverOn(self.queue)
|> afterDisposed { [weak self] in
/*if let strongSelf = self {
assert(strongSelf.queue.isCurrent())
for (_, id, _) in messages {
if let current = strongSelf.messageContexts[id] {
current.status = .none
for subscriber in current.statusSubscribers.copyItems() {
subscriber(nil)
}
if current.statusSubscribers.isEmpty {
strongSelf.messageContexts.removeValue(forKey: id)
}
}
}
}*/
}).start())
|> deliverOn(self.queue)).start())
}
private func commitSendingSingleMessage(messageContext: PendingMessageContext, messageId: MessageId, content: PendingMessageUploadedContentAndReuploadInfo) {
@ -471,21 +455,7 @@ public final class PendingMessageManager {
return .progress(1.0)
}
messageContext.sendDisposable.set((sendMessage
|> deliverOn(self.queue)
|> afterDisposed { [weak self] in
/*if let strongSelf = self {
assert(strongSelf.queue.isCurrent())
if let current = strongSelf.messageContexts[messageId] {
current.status = .none
for subscriber in current.statusSubscribers.copyItems() {
subscriber(nil)
}
if current.statusSubscribers.isEmpty {
strongSelf.messageContexts.removeValue(forKey: messageId)
}
}
}*/
}).start(next: { [weak self] next in
|> deliverOn(self.queue)).start(next: { [weak self] next in
if let strongSelf = self {
assert(strongSelf.queue.isCurrent())
@ -495,7 +465,7 @@ public final class PendingMessageManager {
let status = PendingMessageStatus(isRunning: true, progress: progress)
current.status = status
for subscriber in current.statusSubscribers.copyItems() {
subscriber(status)
subscriber(current.status, current.error)
}
}
}
@ -513,7 +483,7 @@ public final class PendingMessageManager {
let status = PendingMessageStatus(isRunning: true, progress: 0.0)
messageContext.status = status
for subscriber in messageContext.statusSubscribers.copyItems() {
subscriber(status)
subscriber(messageContext.status, messageContext.error)
}
self.addContextActivityIfNeeded(messageContext, peerId: id.peerId)
@ -548,7 +518,7 @@ public final class PendingMessageManager {
let status = PendingMessageStatus(isRunning: true, progress: progress)
current.status = status
for subscriber in current.statusSubscribers.copyItems() {
subscriber(status)
subscriber(current.status, current.error)
}
}
case let .content(content):
@ -582,7 +552,7 @@ public final class PendingMessageManager {
let status = PendingMessageStatus(isRunning: true, progress: 0.0)
context.status = status
for subscriber in context.statusSubscribers.copyItems() {
subscriber(status)
subscriber(context.status, context.error)
}
self.addContextActivityIfNeeded(context, peerId: peerId)
context.uploadDisposable.set((uploadSignal
@ -596,7 +566,7 @@ public final class PendingMessageManager {
let status = PendingMessageStatus(isRunning: true, progress: progress)
current.status = status
for subscriber in current.statusSubscribers.copyItems() {
subscriber(status)
subscriber(context.status, context.error)
}
}
case let .content(content):
@ -780,6 +750,15 @@ public final class PendingMessageManager {
return .complete()
}
} else if let failureReason = reasonForError(error.errorDescription), let message = messages.first?.0 {
for (message, _) in messages {
if let context = strongSelf.messageContexts[message.id] {
context.error = failureReason
for f in context.statusSubscribers.copyItems() {
f(context.status, context.error)
}
}
}
if let context = strongSelf.peerSummaryContexts[message.id.peerId] {
for subscriber in context.messageFailedSubscribers.copyItems() {
subscriber(failureReason)
@ -996,6 +975,13 @@ public final class PendingMessageManager {
return
}
} else if let failureReason = reasonForError(error.errorDescription) {
if let context = strongSelf.messageContexts[message.id] {
context.error = failureReason
for f in context.statusSubscribers.copyItems() {
f(context.status, context.error)
}
}
if let context = strongSelf.peerSummaryContexts[message.id.peerId] {
for subscriber in context.messageFailedSubscribers.copyItems() {
subscriber(failureReason)

View File

@ -1,7 +1,7 @@
import Foundation
import UIKit
private func makeDarkPresentationTheme(accentColor: UIColor) -> PresentationTheme {
private func makeDarkPresentationTheme(accentColor: UIColor, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xeb5545)
let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
@ -313,13 +313,14 @@ private func makeDarkPresentationTheme(accentColor: UIColor) -> PresentationThem
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
inAppNotification: inAppNotification
inAppNotification: inAppNotification,
preview: preview
)
}
public let defaultDarkPresentationTheme = makeDarkPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff))
public let defaultDarkPresentationTheme = makeDarkPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff), preview: false)
public func makeDarkPresentationTheme(accentColor: UIColor?) -> PresentationTheme {
public func makeDarkPresentationTheme(accentColor: UIColor?, preview: Bool) -> PresentationTheme {
let accentColor = accentColor ?? defaultDayAccentColor
return makeDarkPresentationTheme(accentColor: accentColor)
return makeDarkPresentationTheme(accentColor: accentColor, preview: preview)
}

View File

@ -1,7 +1,7 @@
import Foundation
import UIKit
private func makeDarkPresentationTheme(accentColor: UIColor) -> PresentationTheme {
private func makeDarkPresentationTheme(accentColor: UIColor, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xff6767)
let constructiveColor: UIColor = UIColor(rgb: 0x08a723)
let secretColor: UIColor = UIColor(rgb: 0x89df9e)
@ -317,13 +317,14 @@ private func makeDarkPresentationTheme(accentColor: UIColor) -> PresentationThem
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
inAppNotification: inAppNotification
inAppNotification: inAppNotification,
preview: preview
)
}
public let defaultDarkAccentPresentationTheme = makeDarkAccentPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff))
public let defaultDarkAccentPresentationTheme = makeDarkAccentPresentationTheme(accentColor: UIColor(rgb: 0x2ea6ff), preview: false)
public func makeDarkAccentPresentationTheme(accentColor: UIColor?) -> PresentationTheme {
public func makeDarkAccentPresentationTheme(accentColor: UIColor?, preview: Bool) -> PresentationTheme {
let accentColor = accentColor ?? defaultDayAccentColor
return makeDarkPresentationTheme(accentColor: accentColor)
return makeDarkPresentationTheme(accentColor: accentColor, preview: preview)
}

View File

@ -2,7 +2,7 @@ import Foundation
import UIKit
import TelegramCore
private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgroundColor: UIColor, day: Bool) -> PresentationTheme {
private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgroundColor: UIColor, day: Bool, preview: Bool) -> PresentationTheme {
let destructiveColor: UIColor = UIColor(rgb: 0xff3b30)
let constructiveColor: UIColor = UIColor(rgb: 0x00c900)
let secretColor: UIColor = UIColor(rgb: 0x00b12c)
@ -323,16 +323,17 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
chatList: chatList,
chat: chat,
actionSheet: actionSheet,
inAppNotification: inAppNotification
inAppNotification: inAppNotification,
preview: preview
)
}
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(accentColor: UIColor(rgb: 0x007ee5), serviceBackgroundColor: defaultServiceBackgroundColor, day: false)
public let defaultPresentationTheme = makeDefaultDayPresentationTheme(accentColor: UIColor(rgb: 0x007ee5), serviceBackgroundColor: defaultServiceBackgroundColor, day: false, preview: false)
public let defaultDayAccentColor = UIColor(rgb: 0x007ee5)
public let defaultServiceBackgroundColor = UIColor(rgb: 0x000000, alpha: 0.3)
public func makeDefaultDayPresentationTheme(accentColor: UIColor? = nil, serviceBackgroundColor: UIColor, day: Bool) -> PresentationTheme {
public func makeDefaultDayPresentationTheme(accentColor: UIColor? = nil, serviceBackgroundColor: UIColor, day: Bool, preview: Bool) -> PresentationTheme {
let accentColor = accentColor ?? defaultDayAccentColor
return makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, day: day)
return makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, day: day, preview: preview)
}

View File

@ -2,19 +2,19 @@ import Foundation
import UIKit
import TelegramUIPreferences
public func makePresentationTheme(themeReference: PresentationThemeReference, accentColor: UIColor, serviceBackgroundColor: UIColor) -> PresentationTheme {
public func makePresentationTheme(themeReference: PresentationThemeReference, accentColor: UIColor, serviceBackgroundColor: UIColor, preview: Bool = false) -> PresentationTheme {
let theme: PresentationTheme
switch themeReference {
case let .builtin(reference):
switch reference {
case .dayClassic:
theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, day: false)
theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, day: false, preview: preview)
case .night:
theme = makeDarkPresentationTheme(accentColor: accentColor)
theme = makeDarkPresentationTheme(accentColor: accentColor, preview: preview)
case .nightAccent:
theme = makeDarkAccentPresentationTheme(accentColor: accentColor)
theme = makeDarkAccentPresentationTheme(accentColor: accentColor, preview: preview)
case .day:
theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, day: true)
theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, day: true, preview: preview)
}
}
return theme

View File

@ -914,10 +914,11 @@ public final class PresentationTheme: Equatable {
public let chat: PresentationThemeChat
public let actionSheet: PresentationThemeActionSheet
public let inAppNotification: PresentationThemeInAppNotification
public let preview: Bool
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
public init(name: PresentationThemeName, author: String?, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, inAppNotification: PresentationThemeInAppNotification) {
public init(name: PresentationThemeName, author: String?, overallDarkAppearance: Bool, intro: PresentationThemeIntro, passcode: PresentationThemePasscode, rootController: PresentationThemeRootController, list: PresentationThemeList, chatList: PresentationThemeChatList, chat: PresentationThemeChat, actionSheet: PresentationThemeActionSheet, inAppNotification: PresentationThemeInAppNotification, preview: Bool = false) {
self.name = name
self.author = author
self.overallDarkAppearance = overallDarkAppearance
@ -929,6 +930,7 @@ public final class PresentationTheme: Equatable {
self.chat = chat
self.actionSheet = actionSheet
self.inAppNotification = inAppNotification
self.preview = preview
}
public func image(_ key: Int32, _ generate: (PresentationTheme) -> UIImage?) -> UIImage? {

View File

@ -57,7 +57,7 @@ public class LocalBundleResource: TelegramMediaResource {
}
public func isEqual(to: MediaResource) -> Bool {
if let to = to as? LocalBundleResourceId {
if let to = to as? LocalBundleResource {
return self.name == to.name && self.ext == to.ext
} else {
return false

View File

@ -1870,8 +1870,8 @@ final class SharedApplicationContext {
unknownMessageCategory = UNNotificationCategory(identifier: "unknown", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions)
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions)
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: carPlayOptions)
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: hiddenContentString, options: options)
@ -1882,7 +1882,7 @@ final class SharedApplicationContext {
replyMessageCategory = UNNotificationCategory(identifier: "withReply", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions)
replyLegacyMessageCategory = UNNotificationCategory(identifier: "r", actions: [reply], intentIdentifiers: [INSearchForMessagesIntentIdentifier], options: carPlayOptions)
replyLegacyMediaMessageCategory = UNNotificationCategory(identifier: "m", actions: [reply], intentIdentifiers: [], options: [])
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], options: [])
replyMediaMessageCategory = UNNotificationCategory(identifier: "withReplyMedia", actions: [reply], intentIdentifiers: [], options: carPlayOptions)
legacyChannelMessageCategory = UNNotificationCategory(identifier: "c", actions: [], intentIdentifiers: [], options: [])
muteMessageCategory = UNNotificationCategory(identifier: "withMute", actions: [], intentIdentifiers: [], options: [])
muteMediaMessageCategory = UNNotificationCategory(identifier: "withMuteMedia", actions: [], intentIdentifiers: [], options: [])

View File

@ -761,6 +761,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
var dismissInputImpl: (() -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var errorImpl: (() -> Void)?
var scrollToRankImpl: (() -> Void)?
let actualPeerId = Atomic<PeerId>(value: peerId)
let upgradedToSupergroupImpl: (PeerId, @escaping () -> Void) -> Void = { peerId, completion in
@ -809,6 +810,10 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
}, updateFocusedOnRank: { focusedOnRank in
updateState { $0.withUpdatedFocusedOnRank(focusedOnRank) }
if focusedOnRank {
scrollToRankImpl?()
}
}, dismissAdmin: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
@ -1082,7 +1087,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Management_LabelEditor), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelView: channelView, adminView: adminView, initialParticipant: initialParticipant, canEdit: canEdit), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: nil, animateChanges: true)
let listState = ItemListNodeState(entries: channelAdminControllerEntries(presentationData: presentationData, state: state, accountPeerId: context.account.peerId, channelView: channelView, adminView: adminView, initialParticipant: initialParticipant, canEdit: canEdit), style: .blocks, focusItemTag: focusItemTag, ensureVisibleItemTag: nil, emptyStateItem: nil, animateChanges: true)
return (controllerState, (listState, arguments))
}
@ -1091,6 +1096,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
let controller = ItemListController(context: context, state: signal)
controller.experimentalSnapScrollToItem = true
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)
controller?.dismiss()
@ -1111,6 +1117,27 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
}
}
scrollToRankImpl = { [weak controller] in
controller?.afterLayout({
guard let controller = controller else {
return
}
var resultItemNode: ListViewItemNode?
let _ = controller.frameForItemNode({ itemNode in
if let itemNode = itemNode as? ItemListSingleLineInputItemNode {
if let tag = itemNode.tag as? ChannelAdminEntryTag, tag == .rank {
resultItemNode = itemNode
return true
}
}
return false
})
if let resultItemNode = resultItemNode {
controller.ensureItemNodeVisible(resultItemNode)
}
})
}
return controller
}

View File

@ -3759,20 +3759,21 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
self.failedMessageEventsDisposable.set((self.context.account.pendingMessageManager.failedMessageEvents(peerId: peerId)
|> deliverOnMainQueue).start(next: { [weak self] reason in
if let strongSelf = self {
let subjectFlags: TelegramChatBannedRightsFlags = .banSendMedia
let text: String
let moreInfo: Bool
switch reason {
case .flood:
text = strongSelf.presentationData.strings.Conversation_SendMessageErrorFlood
moreInfo = true
case .publicBan:
text = strongSelf.presentationData.strings.Conversation_SendMessageErrorGroupRestricted
moreInfo = true
case .mediaRestricted:
strongSelf.interfaceInteraction?.displayRestrictedInfo(.mediaRecording, .alert)
return
case .flood:
text = strongSelf.presentationData.strings.Conversation_SendMessageErrorFlood
moreInfo = true
case .publicBan:
text = strongSelf.presentationData.strings.Conversation_SendMessageErrorGroupRestricted
moreInfo = true
case .mediaRestricted:
strongSelf.interfaceInteraction?.displayRestrictedInfo(.mediaRecording, .alert)
return
case .slowmodeActive:
text = strongSelf.presentationData.strings.Chat_SlowmodeSendError
moreInfo = false
}
let actions: [TextAlertAction]
if moreInfo {
@ -5837,7 +5838,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
return nil
}
return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id)
|> mapToSignal { status -> Signal<Bool, NoError> in
|> mapToSignal { status, _ -> Signal<Bool, NoError> in
if status != nil {
return .never()
} else {

View File

@ -41,7 +41,11 @@ enum ChatHistoryEntry: Identifiable, Comparable {
case let .MessageEntry(message, presentationData, _, _, _, _):
var type = 2
if presentationData.largeEmoji && message.elligibleForLargeEmoji && messageIsElligibleForLargeEmoji(message) {
type = 3
if animatedEmojiResource(emoji: message.text) != nil {
type = 3
} else {
type = 4
}
}
return UInt64(message.stableId) | ((UInt64(type) << 40))
case let .MessageGroupEntry(groupInfo, _, _):

View File

@ -195,7 +195,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
var updatedPlaybackStatus: Signal<FileMediaResourceStatus, NoError>?
if let updatedFile = updatedFile, updatedMedia || updatedMessageId {
updatedPlaybackStatus = combineLatest(messageFileMediaResourceStatus(context: item.context, file: updatedFile, message: item.message, isRecentActions: item.associatedData.isRecentActions), item.context.account.pendingMessageManager.pendingMessageStatus(item.message.id))
updatedPlaybackStatus = combineLatest(messageFileMediaResourceStatus(context: item.context, file: updatedFile, message: item.message, isRecentActions: item.associatedData.isRecentActions), item.context.account.pendingMessageManager.pendingMessageStatus(item.message.id) |> map { $0.0 })
|> map { resourceStatus, pendingStatus -> FileMediaResourceStatus in
if let pendingStatus = pendingStatus {
var progress = pendingStatus.progress

View File

@ -555,7 +555,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if statusUpdated {
if let image = media as? TelegramMediaImage {
if message.flags.isSending {
updatedStatusSignal = combineLatest(chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image)), context.account.pendingMessageManager.pendingMessageStatus(message.id))
updatedStatusSignal = combineLatest(chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image)), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
if let pendingStatus = pendingStatus {
let adjustedProgress = max(pendingStatus.progress, 0.027)
@ -571,7 +571,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
}
} else if let file = media as? TelegramMediaFile {
updatedStatusSignal = combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file), context.account.pendingMessageManager.pendingMessageStatus(message.id))
updatedStatusSignal = combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|> map { resourceStatus, pendingStatus -> (MediaResourceStatus, MediaResourceStatus?) in
if let pendingStatus = pendingStatus {
let adjustedProgress = max(pendingStatus.progress, 0.027)

View File

@ -6,6 +6,9 @@ import Postbox
import TelegramCore
import TelegramPresentationData
private let leftInset: CGFloat = 16.0
private let rightInset: CGFloat = 46.0
private final class ActionSheetItemNode: ASDisplayNode {
private let title: String
private let action: () -> Void
@ -16,6 +19,8 @@ private final class ActionSheetItemNode: ASDisplayNode {
private let iconNode: ASImageNode
private let titleNode: ImmediateTextNode
private var maxWidth: CGFloat?
init(theme: PresentationTheme, title: String, action: @escaping () -> Void) {
self.title = title
self.action = action
@ -62,13 +67,17 @@ private final class ActionSheetItemNode: ASDisplayNode {
func updateTheme(_ theme: PresentationTheme) {
self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
self.highlightedBackgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor)
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor)
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Menu/SilentIcon"), color: theme.actionSheet.primaryTextColor)
if let maxWidth = self.maxWidth {
let _ = self.titleNode.updateLayout(CGSize(width: maxWidth - leftInset - rightInset, height: .greatestFiniteMagnitude))
}
}
func updateLayout(maxWidth: CGFloat) -> (CGFloat, CGFloat, (CGFloat) -> Void) {
let leftInset: CGFloat = 16.0
let rightInset: CGFloat = 46.0
self.maxWidth = maxWidth
let titleSize = self.titleNode.updateLayout(CGSize(width: maxWidth - leftInset - rightInset, height: .greatestFiniteMagnitude))
let height: CGFloat = 44.0

View File

@ -52,7 +52,7 @@ func messageFileMediaResourceStatus(context: AccountContext, file: TelegramMedia
}
if message.flags.isSending {
return combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file), context.account.pendingMessageManager.pendingMessageStatus(message.id), playbackStatus)
return combineLatest(messageMediaFileStatus(context: context, messageId: message.id, file: file), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 }, playbackStatus)
|> map { resourceStatus, pendingStatus, playbackStatus -> FileMediaResourceStatus in
let mediaStatus: FileMediaResourceMediaStatus
if let playbackStatus = playbackStatus {

View File

@ -353,7 +353,7 @@ func openChatMessage(context: AccountContext, message: Message, standalone: Bool
return nil
}))
case let .theme(media):
let controller = ThemePreviewController(context: context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, day: true), media: .message(message: MessageReference(message), media: media))
let controller = ThemePreviewController(context: context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, day: true, preview: false), media: .message(message: MessageReference(message), media: media))
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
}

View File

@ -663,7 +663,7 @@ public class PeerMediaCollectionController: TelegramController {
return nil
}
return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id)
|> mapToSignal { status -> Signal<Bool, NoError> in
|> mapToSignal { status, _ -> Signal<Bool, NoError> in
if status != nil {
return .never()
} else {

View File

@ -71,7 +71,7 @@ struct PresentationResourcesChat {
let hasWallpaper = !wallpaper.isEmpty
let key: PresentationResourceKey = !hasWallpaper ? PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithoutWallpaper : PresentationResourceKey.chatPrincipalThemeEssentialGraphicsWithWallpaper
return theme.object(key.rawValue, { theme in
return PrincipalThemeEssentialGraphics(theme.chat, wallpaper: wallpaper)
return PrincipalThemeEssentialGraphics(theme.chat, wallpaper: wallpaper, preview: theme.preview)
}) as! PrincipalThemeEssentialGraphics
}

View File

@ -127,82 +127,134 @@ public final class PrincipalThemeEssentialGraphics {
public let radialIndicatorFileIconIncoming: UIImage
public let radialIndicatorFileIconOutgoing: UIImage
init(_ theme: PresentationThemeChat, wallpaper: TelegramWallpaper) {
init(_ theme: PresentationThemeChat, wallpaper: TelegramWallpaper, preview: Bool = false) {
let incoming: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.incoming.bubble.withoutWallpaper : theme.message.incoming.bubble.withWallpaper
let outgoing: PresentationThemeBubbleColorComponents = wallpaper.isEmpty ? theme.message.outgoing.bubble.withoutWallpaper : theme.message.outgoing.bubble.withWallpaper
self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none)
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none)
self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom)
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom)
self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both)
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both)
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none)
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none)
self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom)
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom)
self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both)
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both)
let emptyImage = UIImage()
if preview {
self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none)
self.chatMessageBackgroundIncomingHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopSideImage = emptyImage
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedBottomImage = emptyImage
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedBothImage = emptyImage
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideImage = emptyImage
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none)
self.chatMessageBackgroundOutgoingHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopSideImage = emptyImage
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedBottomImage = emptyImage
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedBothImage = emptyImage
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideImage = emptyImage
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = emptyImage
self.checkBubbleFullImage = emptyImage
self.checkBubblePartialImage = emptyImage
self.checkMediaFullImage = emptyImage
self.checkMediaPartialImage = emptyImage
self.checkFreeFullImage = emptyImage
self.checkFreePartialImage = emptyImage
self.clockBubbleIncomingFrameImage = emptyImage
self.clockBubbleIncomingMinImage = emptyImage
self.clockBubbleOutgoingFrameImage = emptyImage
self.clockBubbleOutgoingMinImage = emptyImage
self.clockMediaFrameImage = emptyImage
self.clockMediaMinImage = emptyImage
self.clockFreeFrameImage = emptyImage
self.clockFreeMinImage = emptyImage
self.dateAndStatusMediaBackground = emptyImage
self.dateAndStatusFreeBackground = emptyImage
self.incomingDateAndStatusImpressionIcon = emptyImage
self.outgoingDateAndStatusImpressionIcon = emptyImage
self.mediaImpressionIcon = emptyImage
self.freeImpressionIcon = emptyImage
self.dateStaticBackground = emptyImage
self.dateFloatingBackground = emptyImage
self.radialIndicatorFileIconIncoming = emptyImage
self.radialIndicatorFileIconOutgoing = emptyImage
} else {
self.chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .none)
self.chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .none)
self.chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundIncomingMergedTopSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundIncomingMergedTopSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .bottom)
self.chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .bottom)
self.chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .both)
self.chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .both)
self.chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .none)
self.chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .none)
self.chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: false))
self.chatMessageBackgroundOutgoingMergedTopSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundOutgoingMergedTopSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .top(side: true))
self.chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .bottom)
self.chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .bottom)
self.chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .both)
self.chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .both)
self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side)
self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side)
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side)
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side)
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor)!
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor)!
self.checkMediaFullImage = generateCheckImage(partial: false, color: .white)!
self.checkMediaPartialImage = generateCheckImage(partial: true, color: .white)!
let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper)
self.checkFreeFullImage = generateCheckImage(partial: false, color: serviceColor.primaryText)!
self.checkFreePartialImage = generateCheckImage(partial: true, color: serviceColor.primaryText)!
self.clockBubbleIncomingFrameImage = generateClockFrameImage(color: theme.message.incoming.pendingActivityColor)!
self.clockBubbleIncomingMinImage = generateClockMinImage(color: theme.message.incoming.pendingActivityColor)!
self.clockBubbleOutgoingFrameImage = generateClockFrameImage(color: theme.message.outgoing.pendingActivityColor)!
self.clockBubbleOutgoingMinImage = generateClockMinImage(color: theme.message.outgoing.pendingActivityColor)!
self.clockMediaFrameImage = generateClockFrameImage(color: .white)!
self.clockMediaMinImage = generateClockMinImage(color: .white)!
self.clockFreeFrameImage = generateClockFrameImage(color: serviceColor.primaryText)!
self.clockFreeMinImage = generateClockMinImage(color: serviceColor.primaryText)!
self.dateAndStatusMediaBackground = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.message.mediaDateAndStatusFillColor)!
self.dateAndStatusFreeBackground = generateStretchableFilledCircleImage(diameter: 18.0, color: serviceColor.dateFillStatic)!
let impressionCountImage = UIImage(bundleImageName: "Chat/Message/ImpressionCount")!
self.incomingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.incoming.secondaryTextColor)!
self.outgoingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.outgoing.secondaryTextColor)!
self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)!
self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)!
let chatDateSize: CGFloat = 20.0
self.dateStaticBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillStatic.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.dateFloatingBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillFloating.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: incoming.fill)!
self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: outgoing.fill)!
self.chatMessageBackgroundIncomingMergedSideImage = messageBubbleImage(incoming: true, fillColor: incoming.fill, strokeColor: incoming.stroke, neighbors: .side)
self.chatMessageBackgroundOutgoingMergedSideImage = messageBubbleImage(incoming: false, fillColor: outgoing.fill, strokeColor: outgoing.stroke, neighbors: .side)
self.chatMessageBackgroundIncomingMergedSideHighlightedImage = messageBubbleImage(incoming: true, fillColor: incoming.highlightedFill, strokeColor: incoming.stroke, neighbors: .side)
self.chatMessageBackgroundOutgoingMergedSideHighlightedImage = messageBubbleImage(incoming: false, fillColor: outgoing.highlightedFill, strokeColor: outgoing.stroke, neighbors: .side)
self.checkBubbleFullImage = generateCheckImage(partial: false, color: theme.message.outgoingCheckColor)!
self.checkBubblePartialImage = generateCheckImage(partial: true, color: theme.message.outgoingCheckColor)!
self.checkMediaFullImage = generateCheckImage(partial: false, color: .white)!
self.checkMediaPartialImage = generateCheckImage(partial: true, color: .white)!
let serviceColor = serviceMessageColorComponents(chatTheme: theme, wallpaper: wallpaper)
self.checkFreeFullImage = generateCheckImage(partial: false, color: serviceColor.primaryText)!
self.checkFreePartialImage = generateCheckImage(partial: true, color: serviceColor.primaryText)!
self.clockBubbleIncomingFrameImage = generateClockFrameImage(color: theme.message.incoming.pendingActivityColor)!
self.clockBubbleIncomingMinImage = generateClockMinImage(color: theme.message.incoming.pendingActivityColor)!
self.clockBubbleOutgoingFrameImage = generateClockFrameImage(color: theme.message.outgoing.pendingActivityColor)!
self.clockBubbleOutgoingMinImage = generateClockMinImage(color: theme.message.outgoing.pendingActivityColor)!
self.clockMediaFrameImage = generateClockFrameImage(color: .white)!
self.clockMediaMinImage = generateClockMinImage(color: .white)!
self.clockFreeFrameImage = generateClockFrameImage(color: serviceColor.primaryText)!
self.clockFreeMinImage = generateClockMinImage(color: serviceColor.primaryText)!
self.dateAndStatusMediaBackground = generateStretchableFilledCircleImage(diameter: 18.0, color: theme.message.mediaDateAndStatusFillColor)!
self.dateAndStatusFreeBackground = generateStretchableFilledCircleImage(diameter: 18.0, color: serviceColor.dateFillStatic)!
let impressionCountImage = UIImage(bundleImageName: "Chat/Message/ImpressionCount")!
self.incomingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.incoming.secondaryTextColor)!
self.outgoingDateAndStatusImpressionIcon = generateTintedImage(image: impressionCountImage, color: theme.message.outgoing.secondaryTextColor)!
self.mediaImpressionIcon = generateTintedImage(image: impressionCountImage, color: .white)!
self.freeImpressionIcon = generateTintedImage(image: impressionCountImage, color: serviceColor.primaryText)!
let chatDateSize: CGFloat = 20.0
self.dateStaticBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillStatic.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.dateFloatingBackground = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(serviceColor.dateFillFloating.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)
self.radialIndicatorFileIconIncoming = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: incoming.fill)!
self.radialIndicatorFileIconOutgoing = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/RadialProgressIconDocumentIncoming"), color: outgoing.fill)!
}
}
}

View File

@ -340,6 +340,11 @@ public final class ShareController: ViewController {
override public func loadDisplayNode() {
self.displayNode = ShareControllerNode(sharedContext: self.sharedContext, defaultAction: self.defaultAction, requestLayout: { [weak self] transition in
self?.requestLayout(transition: transition)
}, presentError: { [weak self] title, text in
guard let strongSelf = self else {
return
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: title, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, externalShare: self.externalShare, immediateExternalShare: self.immediateExternalShare)
self.controllerNode.dismiss = { [weak self] shared in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
@ -353,98 +358,141 @@ public final class ShareController: ViewController {
})
}
self.controllerNode.share = { [weak self] text, peerIds in
if let strongSelf = self {
switch strongSelf.subject {
case let .url(url):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: url, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .text(string):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: string, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .quote(string, url):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber])
attributedText.append(NSAttributedString(string: "\n\n\(url)"))
let entities = generateChatInputTextEntities(attributedText)
messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .image(representations):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)), replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .media(mediaReference):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .mapMedia(media):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil))
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages).start()
}
return .complete()
case let .messages(messages):
for peerId in peerIds {
var messagesToEnqueue: [EnqueueMessage] = []
if !text.isEmpty {
messagesToEnqueue.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
for message in messages {
messagesToEnqueue.append(.forward(source: message.id, grouping: .auto, attributes: []))
}
let _ = enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue).start()
}
return .single(.done)
case let .fromExternal(f):
return f(peerIds, text, strongSelf.currentAccount)
|> map { state -> ShareState in
switch state {
case .preparing:
return .preparing
case let .progress(value):
return .progress(value)
case .done:
return .done
}
}
guard let strongSelf = self else {
return .complete()
}
var shareSignals: [Signal<[MessageId?], NoError>] = []
switch strongSelf.subject {
case let .url(url):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: url, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .text(string):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: string, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .quote(string, url):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber])
attributedText.append(NSAttributedString(string: "\n\n\(url)"))
let entities = generateChatInputTextEntities(attributedText)
messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .image(representations):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)), replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .media(mediaReference):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .mapMedia(media):
for peerId in peerIds {
var messages: [EnqueueMessage] = []
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .messages(messages):
for peerId in peerIds {
var messagesToEnqueue: [EnqueueMessage] = []
if !text.isEmpty {
messagesToEnqueue.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
for message in messages {
messagesToEnqueue.append(.forward(source: message.id, grouping: .auto, attributes: []))
}
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue))
}
case let .fromExternal(f):
return f(peerIds, text, strongSelf.currentAccount)
|> map { state -> ShareState in
switch state {
case .preparing:
return .preparing
case let .progress(value):
return .progress(value)
case .done:
return .done
}
}
}
return .complete()
let account = strongSelf.currentAccount
let queue = Queue.mainQueue()
var displayedError = false
return combineLatest(queue: queue, shareSignals)
|> mapToSignal { messageIdSets -> Signal<ShareState, NoError> in
var statuses: [Signal<(MessageId, PendingMessageStatus?, PendingMessageFailureReason?), NoError>] = []
for messageIds in messageIdSets {
for case let id? in messageIds {
statuses.append(account.pendingMessageManager.pendingMessageStatus(id)
|> map { status, error -> (MessageId, PendingMessageStatus?, PendingMessageFailureReason?) in
return (id, status, error)
})
}
}
return combineLatest(queue: queue, statuses)
|> mapToSignal { statuses -> Signal<ShareState, NoError> in
var hasStatuses = false
for (id, status, error) in statuses {
if let error = error {
Queue.mainQueue().async {
let _ = (account.postbox.transaction { transaction -> Peer? in
transaction.deleteMessages([id])
return transaction.getPeer(id.peerId)
}
|> deliverOnMainQueue).start(next: { peer in
guard let strongSelf = self, let peer = peer else {
return
}
if !displayedError, case .slowmodeActive = error {
displayedError = true
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: peer.displayTitle, text: strongSelf.presentationData.strings.Chat_SlowmodeSendError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
})
}
}
let _ = account.postbox.transaction({ transaction in
}).start()
if status != nil {
hasStatuses = true
}
}
if !hasStatuses {
return .single(.done)
}
return .complete()
}
|> take(1)
}
}
self.controllerNode.shareExternal = { [weak self] in
if let strongSelf = self {

View File

@ -30,6 +30,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
private let defaultAction: ShareControllerAction?
private let requestLayout: (ContainedViewLayoutTransition) -> Void
private let presentError: (String?, String) -> Void
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)?
@ -70,11 +71,12 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
private var hapticFeedback: HapticFeedback?
init(sharedContext: SharedAccountContext, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, externalShare: Bool, immediateExternalShare: Bool) {
init(sharedContext: SharedAccountContext, defaultAction: ShareControllerAction?, requestLayout: @escaping (ContainedViewLayoutTransition) -> Void, presentError: @escaping (String?, String) -> Void, externalShare: Bool, immediateExternalShare: Bool) {
self.sharedContext = sharedContext
self.presentationData = sharedContext.currentPresentationData.with { $0 }
self.externalShare = externalShare
self.immediateExternalShare = immediateExternalShare
self.presentError = presentError
self.defaultAction = defaultAction
self.requestLayout = requestLayout
@ -500,6 +502,15 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
defaultAction.action()
}
} else {
if !self.inputFieldNode.text.isEmpty {
for peer in self.controllerInteraction!.selectedPeers {
if let channel = peer.peer as? TelegramChannel, channel.isRestrictedBySlowmode {
self.presentError(channel.title, self.presentationData.strings.Share_MultipleMessagesDisabled)
return
}
}
}
self.inputFieldNode.deactivateInput()
let transition = ContainedViewLayoutTransition.animated(duration: 0.12, curve: .easeInOut)
transition.updateAlpha(node: self.actionButtonNode, alpha: 0.0)

View File

@ -111,7 +111,7 @@ final class ShareControllerPeerGridItem: GridItem {
func node(layout: GridNodeLayout, synchronousLoad: Bool) -> GridItemNode {
let node = ShareControllerPeerGridItemNode()
node.controllerInteraction = self.controllerInteraction
node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, search: self.search, synchronousLoad: synchronousLoad)
node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, search: self.search, synchronousLoad: synchronousLoad, force: false)
return node
}
@ -121,13 +121,14 @@ final class ShareControllerPeerGridItem: GridItem {
return
}
node.controllerInteraction = self.controllerInteraction
node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, search: self.search, synchronousLoad: false)
node.setup(account: self.account, theme: self.theme, strings: self.strings, peer: self.peer, presence: self.presence, search: self.search, synchronousLoad: false, force: false)
}
}
final class ShareControllerPeerGridItemNode: GridItemNode {
private var currentState: (Account, RenderedPeer, Bool, PeerPresence?)?
private var currentState: (Account, PresentationTheme, PresentationStrings, RenderedPeer, Bool, PeerPresence?)?
private let peerNode: SelectablePeerNode
private var presenceManager: PeerPresenceStatusManager?
var controllerInteraction: ShareControllerInteraction?
@ -138,7 +139,7 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
self.peerNode.toggleSelection = { [weak self] in
if let strongSelf = self {
if let (_, peer, search, _) = strongSelf.currentState {
if let (_, _, _, peer, search, _) = strongSelf.currentState {
if let _ = peer.peers[peer.peerId] {
strongSelf.controllerInteraction?.togglePeer(peer, search)
}
@ -146,10 +147,16 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
}
}
self.addSubnode(self.peerNode)
self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in
guard let strongSelf = self, let currentState = strongSelf.currentState else {
return
}
strongSelf.setup(account: currentState.0, theme: currentState.1, strings: currentState.2, peer: currentState.3, presence: currentState.5, search: currentState.4, synchronousLoad: false, force: true)
})
}
func setup(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, search: Bool, synchronousLoad: Bool) {
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != peer || !arePeerPresencesEqual(self.currentState!.3, presence) {
func setup(account: Account, theme: PresentationTheme, strings: PresentationStrings, peer: RenderedPeer, presence: PeerPresence?, search: Bool, synchronousLoad: Bool, force: Bool) {
if force || self.currentState == nil || self.currentState!.0 !== account || self.currentState!.3 != peer || !arePeerPresencesEqual(self.currentState!.5, presence) {
let itemTheme = SelectablePeerNodeTheme(textColor: theme.actionSheet.primaryTextColor, secretTextColor: theme.chatList.secretTitleColor, selectedTextColor: theme.actionSheet.controlAccentColor, checkBackgroundColor: theme.actionSheet.opaqueItemBackgroundColor, checkFillColor: theme.actionSheet.controlAccentColor, checkColor: theme.actionSheet.checkContentColor, avatarPlaceholderColor: theme.list.mediaPlaceholderColor)
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
@ -163,15 +170,18 @@ final class ShareControllerPeerGridItemNode: GridItemNode {
self.peerNode.theme = itemTheme
self.peerNode.setup(account: account, theme: theme, strings: strings, peer: peer, online: online, synchronousLoad: synchronousLoad)
self.currentState = (account, peer, search, presence)
self.currentState = (account, theme, strings, peer, search, presence)
self.setNeedsLayout()
if let presence = presence as? TelegramUserPresence {
self.presenceManager?.reset(presence: presence)
}
}
self.updateSelection(animated: false)
}
func updateSelection(animated: Bool) {
var selected = false
if let controllerInteraction = self.controllerInteraction, let (_, peer, _, _) = self.currentState {
if let controllerInteraction = self.controllerInteraction, let (_, _, _, peer, _, _) = self.currentState {
selected = controllerInteraction.selectedPeerIds.contains(peer.peerId)
}

View File

@ -131,6 +131,7 @@ final class ThemeSettingsColorSliderNode: ASDisplayNode {
private let brightnessKnobNode: ThemeSettingsColorKnobNode
private var validLayout: CGSize?
private var panning = false
var valueChanged: ((CGFloat) -> Void)?
@ -143,6 +144,7 @@ final class ThemeSettingsColorSliderNode: ASDisplayNode {
}
var _value: CGFloat = 0.5
var lastReportedValue: CGFloat?
var value: CGFloat {
get {
@ -196,7 +198,9 @@ final class ThemeSettingsColorSliderNode: ASDisplayNode {
let inset: CGFloat = 16.0
transition.updateFrame(node: self.brightnessNode, frame: CGRect(x: inset, y: floor((size.height - 30.0) / 2.0), width: size.width - inset * 2.0, height: 30.0))
self.updateKnobLayout(size: size, transition: .immediate)
if !self.panning {
self.updateKnobLayout(size: size, transition: .immediate)
}
}
@objc private func brightnessPan(_ recognizer: UIPanGestureRecognizer) {
@ -213,18 +217,27 @@ final class ThemeSettingsColorSliderNode: ASDisplayNode {
var ended = false
switch recognizer.state {
case .changed:
self.panning = true
self.updateKnobLayout(size: size, transition: .immediate)
recognizer.setTranslation(CGPoint(), in: recognizer.view)
case .ended:
self.updateKnobLayout(size: size, transition: .immediate)
self.panning = false
ended = true
default:
break
}
if self.value != previousValue || ended {
var update = true
if let lastReportedValue = self.lastReportedValue, abs(self.value - lastReportedValue) < 0.05 {
update = false
}
if update || ended {
self.update()
self.valueChanged?(self.value)
self.lastReportedValue = self.value
print("upda")
}
}
}

View File

@ -67,7 +67,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
case wallpaper(PresentationTheme, String)
case accentColor(PresentationTheme, String, PresentationThemeAccentColor?)
case autoNightTheme(PresentationTheme, String, String)
case themeTint(PresentationTheme, String, Bool)
case themeItem(PresentationTheme, PresentationStrings, [PresentationThemeReference], PresentationThemeReference, [Int64: PresentationThemeAccentColor], PresentationThemeAccentColor?, Bool)
case iconHeader(PresentationTheme, String)
case iconItem(PresentationTheme, PresentationStrings, [PresentationAppIcon], String?)
@ -78,7 +77,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
var section: ItemListSectionId {
switch self {
case .themeListHeader, .chatPreview, .themeItem, .themeTint, .accentColor:
case .themeListHeader, .chatPreview, .themeItem, .accentColor:
return ThemeSettingsControllerSection.chatPreview.rawValue
case .fontSizeHeader, .fontSize:
return ThemeSettingsControllerSection.fontSize.rawValue
@ -99,8 +98,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return 1
case .themeItem:
return 2
case .themeTint:
return 3
case .accentColor:
return 4
case .wallpaper:
@ -140,12 +137,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
} else {
return false
}
case let .themeTint(lhsTheme, lhsTitle, lhsValue):
if case let .themeTint(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
return true
} else {
return false
}
case let .accentColor(lhsTheme, lhsText, lhsColor):
if case let .accentColor(rhsTheme, rhsText, rhsColor) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsColor == rhsColor {
return true
@ -239,10 +230,6 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
return ItemListDisclosureItem(theme: theme, title: text, label: "", sectionId: self.section, style: .blocks, action: {
arguments.openWallpaperSettings()
})
case let .themeTint(theme, title, value):
return ItemListSwitchItem(theme: theme, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleLargeEmoji(value)
}, tag: ThemeSettingsEntryTag.tint)
case let .accentColor(theme, _, color):
var colors = PresentationThemeBaseColor.allCases
if theme.overallDarkAppearance {
@ -450,7 +437,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let disableAnimations = settings.disableAnimations
let accentColor = settings.themeSpecificAccentColors[settings.theme.index]?.color ?? defaultDayAccentColor
let theme = makePresentationTheme(themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor)
let theme = makePresentationTheme(themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, preview: true)
let wallpaper: TelegramWallpaper
if let themeSpecificWallpaper = settings.themeSpecificChatWallpapers[settings.theme.index] {