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

This commit is contained in:
Ilya Laktyushin 2019-11-12 18:56:18 +04:00
commit e1ddf060f0
95 changed files with 5211 additions and 4716 deletions

9
BUCK
View File

@ -432,6 +432,14 @@ apple_resource(
visibility = ["PUBLIC"],
)
apple_resource(
name = "WatchAppExtensionResources",
files = glob([
"Watch/Extension/Resources/**/*",
], exclude = ["Watch/Extension/Resources/**/.*"]),
visibility = ["PUBLIC"],
)
apple_binary(
name = "WatchAppExtensionBinary",
srcs = glob([
@ -467,6 +475,7 @@ apple_binary(
],
deps = [
":WatchAppStringResources",
":WatchAppExtensionResources",
],
)

View File

@ -111,7 +111,7 @@ static void reportMemory() {
- (void)completeWithBestAttemptContent {
_contentReady = true;
//_updatedUnreadCount = @(-1);
_updatedUnreadCount = @(-1);
if (_contentReady && _updatedUnreadCount) {
[self _internalComplete];
}
@ -229,7 +229,7 @@ static void reportMemory() {
NSData *attachmentData = nil;
id parsedAttachment = nil;
if (_isLockedValue) {
if (!_isLockedValue) {
NSString *attachmentDataString = decryptedPayload[@"attachb64"];
if ([attachmentDataString isKindOfClass:[NSString class]]) {
attachmentData = parseBase64(attachmentDataString);

View File

@ -40,7 +40,7 @@
<key>NSExtensionPointIdentifier</key>
<string>com.apple.intents-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
<string>IntentHandler</string>
</dict>
</dict>
</plist>

View File

@ -50,7 +50,8 @@ enum IntentHandlingError {
}
@available(iOSApplicationExtension 10.0, *)
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling {
@objc(IntentHandler)
public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling {
private let accountPromise = Promise<Account?>()
private let resolvePersonsDisposable = MetaDisposable()
@ -133,7 +134,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
self.actionDisposable.dispose()
}
override func handler(for intent: INIntent) -> Any {
override public func handler(for intent: INIntent) -> Any {
return self
}
@ -264,7 +265,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// MARK: - INSendMessageIntentHandling
func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
public func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion([INPersonResolutionResult.notRequired()])
return
@ -275,7 +276,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
@available(iOSApplicationExtension 11.0, *)
func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
public func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
if let peerId = intent.conversationIdentifier.flatMap(Int64.init) {
let account = self.accountPromise.get()
@ -318,7 +319,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
}
func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
public func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion(INStringResolutionResult.notRequired())
return
@ -330,7 +331,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
}
func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
public func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
@ -341,7 +342,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
completion(response)
}
func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
public func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@ -387,11 +388,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// MARK: - INSearchForMessagesIntentHandling
func resolveAttributes(for intent: INSearchForMessagesIntent, with completion: @escaping (INMessageAttributeOptionsResolutionResult) -> Void) {
public func resolveAttributes(for intent: INSearchForMessagesIntent, with completion: @escaping (INMessageAttributeOptionsResolutionResult) -> Void) {
completion(.success(with: .unread))
}
func handle(intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) {
public func handle(intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) {
self.actionDisposable.set((self.accountPromise.get()
|> take(1)
|> castError(IntentHandlingError.self)
@ -439,7 +440,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// MARK: - INSetMessageAttributeIntentHandling
func resolveAttribute(for intent: INSetMessageAttributeIntent, with completion: @escaping (INMessageAttributeResolutionResult) -> Void) {
public func resolveAttribute(for intent: INSetMessageAttributeIntent, with completion: @escaping (INMessageAttributeResolutionResult) -> Void) {
let supportedAttributes: [INMessageAttribute] = [.read, .unread]
var attribute = intent.attribute
if attribute == .flagged {
@ -452,7 +453,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
}
func handle(intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
public func handle(intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@ -511,7 +512,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// MARK: - INStartAudioCallIntentHandling
func resolveContacts(for intent: INStartAudioCallIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
public func resolveContacts(for intent: INStartAudioCallIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion([INPersonResolutionResult.notRequired()])
return
@ -522,11 +523,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
@available(iOSApplicationExtension 11.0, *)
func resolveDestinationType(for intent: INStartAudioCallIntent, with completion: @escaping (INCallDestinationTypeResolutionResult) -> Void) {
public func resolveDestinationType(for intent: INStartAudioCallIntent, with completion: @escaping (INCallDestinationTypeResolutionResult) -> Void) {
completion(.success(with: .normal))
}
func handle(intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Void) {
public func handle(intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Void) {
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@ -561,15 +562,15 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// MARK: - INSearchCallHistoryIntentHandling
@available(iOSApplicationExtension 11.0, *)
func resolveCallTypes(for intent: INSearchCallHistoryIntent, with completion: @escaping (INCallRecordTypeOptionsResolutionResult) -> Void) {
public func resolveCallTypes(for intent: INSearchCallHistoryIntent, with completion: @escaping (INCallRecordTypeOptionsResolutionResult) -> Void) {
completion(.success(with: .missed))
}
func resolveCallType(for intent: INSearchCallHistoryIntent, with completion: @escaping (INCallRecordTypeResolutionResult) -> Void) {
public func resolveCallType(for intent: INSearchCallHistoryIntent, with completion: @escaping (INCallRecordTypeResolutionResult) -> Void) {
completion(.success(with: .missed))
}
func handle(intent: INSearchCallHistoryIntent, completion: @escaping (INSearchCallHistoryIntentResponse) -> Void) {
public func handle(intent: INSearchCallHistoryIntent, completion: @escaping (INSearchCallHistoryIntentResponse) -> Void) {
self.actionDisposable.set((self.accountPromise.get()
|> take(1)
|> castError(IntentHandlingError.self)

View File

@ -33,5 +33,7 @@
<string>merchant.privatbank.test.telergramios</string>
<string>merchant.privatbank.prod.telergram</string>
</array>
<key>com.apple.developer.pushkit.unrestricted-voip</key>
<true/>
</dict>
</plist>

View File

@ -32,5 +32,7 @@
<array>
<string>X834Q8SBVP.org.telegram.Telegram-iOS</string>
</array>
<key>com.apple.developer.pushkit.unrestricted-voip</key>
<true/>
</dict>
</plist>

View File

@ -599,6 +599,8 @@
"UserInfo.AddContact" = "Add Contact";
"UserInfo.ShareContact" = "Share Contact";
"UserInfo.StartSecretChat" = "Start Secret Chat";
"UserInfo.StartSecretChatConfirmation" = "Are you sure you want to start a secret chat with %@?";
"UserInfo.StartSecretChatStart" = "Start";
"UserInfo.DeleteContact" = "Delete Contact";
"UserInfo.CreateNewContact" = "Create New Contact";
"UserInfo.AddToExisting" = "Add to Existing";
@ -5082,6 +5084,23 @@ Any member of this group will be able to see messages in the channel.";
"ClearCache.StorageFree" = "Free";
"ClearCache.ClearCache" = "Clear Telegram Cache";
"ClearCache.Clear" = "Clear";
"ClearCache.Forever" = "Forever";
"ChatList.DeletedChats_1" = "Deleted 1 chat";
"ChatList.DeletedChats_any" = "Deleted %@ chats";
"Appearance.ColorThemeNight" = "COLOR THEME — AUTO-NIGHT MODE";
"UserInfo.StartSecretChatConfirmation" = "Are you sure you want to start a secret chat with\n%@?";
"UserInfo.StartSecretChatStart" = "Start";
"Wallet.AccessDenied.Title" = "Please Allow Access";
"Wallet.AccessDenied.Camera" = "TON Wallet needs access to your camera to take photos and videos.\n\nPlease go to Settings > Privacy > Camera and set TON Wallet to ON.";
"Wallet.AccessDenied.Settings" = "Settings";
"GroupInfo.ShowMoreMembers_0" = "%@ more";
"GroupInfo.ShowMoreMembers_1" = "%@ more";
"GroupInfo.ShowMoreMembers_2" = "%@ more";
"GroupInfo.ShowMoreMembers_3_10" = "%@ more";
"GroupInfo.ShowMoreMembers_many" = "%@ more";
"GroupInfo.ShowMoreMembers_any" = "%@ more";

View File

@ -403,15 +403,16 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
func authorizeAccessToCamera(completion: @escaping () -> Void) {
AVCaptureDevice.requestAccess(for: AVMediaType.video) { [weak self] response in
guard let strongSelf = self else {
return
}
Queue.mainQueue().async {
guard let strongSelf = self else {
return
}
if response {
completion()
} else {
let presentationData = strongSelf.presentationData
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: presentationData.strings.Wallet_AccessDenied_Title, text: presentationData.strings.Wallet_AccessDenied_Camera, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Wallet_Intro_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Wallet_AccessDenied_Settings, action: {
let controller = standardTextAlertController(theme: presentationData.theme.alert, title: presentationData.strings.Wallet_AccessDenied_Title, text: presentationData.strings.Wallet_AccessDenied_Camera, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Wallet_Intro_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Wallet_AccessDenied_Settings, action: {
strongSelf.openPlatformSettings()
})])
strongSelf.window.present(controller, on: .root)
@ -705,33 +706,46 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
self.walletContext = walletContext
let beginWithController: (ViewController) -> Void = { controller in
navigationController.setViewControllers([controller], animated: false)
var previousBlockchainName = initialConfigBlockchainName
let _ = (updatedConfigValue
|> deliverOnMainQueue).start(next: { resolved, blockchainName in
let _ = walletContext.tonInstance.validateConfig(config: resolved.value, blockchainName: blockchainName).start(error: { _ in
}, completed: {
let _ = walletContext.tonInstance.updateConfig(config: resolved.value, blockchainName: blockchainName).start()
if previousBlockchainName != blockchainName {
previousBlockchainName = blockchainName
let begin: (Bool) -> Void = { animated in
navigationController.setViewControllers([controller], animated: false)
if animated {
navigationController.viewControllers.last?.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
var previousBlockchainName = initialConfigBlockchainName
let _ = (updatedConfigValue
|> deliverOnMainQueue).start(next: { resolved, blockchainName in
let _ = walletContext.tonInstance.validateConfig(config: resolved.value, blockchainName: blockchainName).start(error: { _ in
}, completed: {
let _ = walletContext.tonInstance.updateConfig(config: resolved.value, blockchainName: blockchainName).start()
let overlayController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
mainWindow.present(overlayController, on: .root)
let _ = (deleteAllLocalWalletsData(storage: walletContext.storage, tonInstance: walletContext.tonInstance)
|> deliverOnMainQueue).start(error: { [weak overlayController] _ in
overlayController?.dismiss()
}, completed: { [weak overlayController] in
overlayController?.dismiss()
if previousBlockchainName != blockchainName {
previousBlockchainName = blockchainName
navigationController.setViewControllers([WalletSplashScreen(context: walletContext, mode: .intro, walletCreatedPreloadState: nil)], animated: true)
})
}
let overlayController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
mainWindow.present(overlayController, on: .root)
let _ = (deleteAllLocalWalletsData(storage: walletContext.storage, tonInstance: walletContext.tonInstance)
|> deliverOnMainQueue).start(error: { [weak overlayController] _ in
overlayController?.dismiss()
}, completed: { [weak overlayController] in
overlayController?.dismiss()
navigationController.setViewControllers([WalletSplashScreen(context: walletContext, mode: .intro, walletCreatedPreloadState: nil)], animated: true)
})
}
})
})
})
}
if let splashScreen = navigationController.viewControllers.first as? WalletApplicationSplashScreen, let _ = controller as? WalletSplashScreen {
splashScreen.animateOut(completion: {
begin(true)
})
} else {
begin(false)
}
}
let _ = (combineLatest(queue: .mainQueue(),

View File

@ -219,5 +219,5 @@
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";
"Wallet.Receive.ShareInvoiceUrlInfo" = "Share this link with other Gram wallet owners to receive %@ Grams from them.";
"Wallet.AccessDenied.Title" = "Please Allow Access";
"Wallet.AccessDenied.Camera" = "TON Wallet needs access to camera to scan QR codes.\n\nPlease go to Settings > Privacy > Camera and set TON Wallet to ON.";
"Wallet.AccessDenied.Camera" = "TON Wallet needs access to your camera to take photos and videos.\n\nPlease go to Settings > Privacy > Camera and set TON Wallet to ON.";
"Wallet.AccessDenied.Settings" = "Settings";

View File

@ -131,21 +131,14 @@
+ (SSignal *)_requestImageWithUrl:(NSString *)url subscription:(TGBridgeSubscription *)subscription
{
SSignal *remoteSignal = [[[[[TGBridgeClient instance] requestSignalWithSubscription:subscription] onStart:^
{
SSignal *remoteSignal = [[[[TGBridgeClient instance] requestSignalWithSubscription:subscription] onStart:^
{
if (![[self mediaManager] hasUrl:url])
[[self mediaManager] addUrl:url];
}] onDispose:^
}] then:[[self _downloadedFileWithUrl:url] onNext:^(id next)
{
[[self mediaManager] removeUrl:url];
}] mapToSignal:^SSignal *(id next)
{
return [[self _downloadedFileWithUrl:url] onNext:^(id next)
{
[[self mediaManager] removeUrl:url];
}];
}];
}]];
return [[self _cachedOrPendingWithUrl:url] catch:^SSignal *(id error)
{
return remoteSignal;

View File

@ -146,6 +146,7 @@ REMOVE_ENTITLEMENT_KEYS=(\
COPY_ENTITLEMENT_KEYS=(\
"com.apple.developer.associated-domains" \
"com.apple.developer.icloud-services" \
"com.apple.developer.pushkit.unrestricted-voip" \
)
REPLACE_TO_PRODUCTION_ENTITLEMENT_KEYS=(\
@ -217,9 +218,12 @@ for ITEM in $APP_ITEMS_WITH_PROVISIONING_PROFILE; do
VALUE=$(/usr/libexec/PlistBuddy -c "Print :$KEY" "$PROFILE_ENTITLEMENTS_PATH" 2>/dev/null || echo "")
if [ ! -z "$VALUE" ]; then
PLUTIL_KEY=$(echo "$KEY" | sed 's/\./\\\./g')
VALUE=$(plutil -extract "$PLUTIL_KEY" xml1 -o - "${!ENTITLEMENTS_VAR}")
/usr/libexec/PlistBuddy -c "Delete $KEY" "$PROFILE_ENTITLEMENTS_PATH" 2>/dev/null
plutil -insert "$PLUTIL_KEY" -xml "$VALUE" "$PROFILE_ENTITLEMENTS_PATH"
TEST_VALUE=$(plutil -extract "$PLUTIL_KEY" xml1 -o - "${!ENTITLEMENTS_VAR}" 1>/dev/null || echo "error")
if [ "$TEST_VALUE" != "error" ]; then
VALUE=$(plutil -extract "$PLUTIL_KEY" xml1 -o - "${!ENTITLEMENTS_VAR}")
/usr/libexec/PlistBuddy -c "Delete $KEY" "$PROFILE_ENTITLEMENTS_PATH" 2>/dev/null
plutil -insert "$PLUTIL_KEY" -xml "$VALUE" "$PROFILE_ENTITLEMENTS_PATH"
fi
fi
done
fi

View File

@ -30,9 +30,7 @@ public final class AnimationNode : ASDisplayNode {
view.animationSpeed = self.speed
view.backgroundColor = .clear
view.isOpaque = false
view.logHierarchyKeypaths()
if let colors = colors {
for (key, value) in colors {
let colorCallback = LOTColorValueCallback(color: value.cgColor)

View File

@ -0,0 +1,23 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "AppIntents",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/SyncCore:SyncCore#shared",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/Display:Display#shared",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AvatarNode:AvatarNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
],
weak_frameworks = [
"Intents",
],
)

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View File

@ -0,0 +1,67 @@
import Foundation
import UIKit
import Intents
import Display
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import TelegramPresentationData
import AvatarNode
import AccountContext
public func donateSendMessageIntent(account: Account, sharedContext: SharedAccountContext, peerIds: [PeerId]) {
if #available(iOSApplicationExtension 13.2, iOS 13.2, *) {
let _ = (account.postbox.transaction { transaction -> [Peer] in
var peers: [Peer] = []
for peerId in peerIds {
if peerId.namespace == Namespaces.Peer.CloudUser && peerId != account.peerId, let peer = transaction.getPeer(peerId) {
peers.append(peer)
}
}
return peers
}
|> mapToSignal { peers -> Signal<[(Peer, UIImage?)], NoError> in
var signals: [Signal<(Peer, UIImage?), NoError>] = []
for peer in peers {
let peerAndAvatar = (peerAvatarImage(account: account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil))
|> map { avatarImage in
return (peer, avatarImage)
}
signals.append(peerAndAvatar)
}
return combineLatest(signals)
}
|> deliverOnMainQueue).start(next: { peers in
for (peer, avatarImage) in peers {
guard let peer = peer as? TelegramUser, peer.botInfo == nil && !peer.flags.contains(.isSupport) else {
continue
}
let presentationData = sharedContext.currentPresentationData.with { $0 }
let recipientHandle = INPersonHandle(value: "tg\(peer.id.id)", type: .unknown)
var nameComponents = PersonNameComponents()
nameComponents.givenName = peer.firstName
nameComponents.familyName = peer.lastName
let displayTitle = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peer.id.id)")
let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peer.id.id)", serviceName: nil, sender: nil)
if let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) {
intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName)
}
let interaction = INInteraction(intent: intent, response: nil)
interaction.direction = .outgoing
interaction.groupIdentifier = "sendMessage_\(account.peerId.toInt64())"
interaction.donate()
}
})
}
}
public func deleteAllSendMessageIntents(accountPeerId: PeerId) {
if #available(iOS 10.0, *) {
INInteraction.delete(with: "sendMessage_\(accountPeerId.toInt64())")
}
}

View File

@ -237,7 +237,7 @@ using AS::MutexLocker;
UIImage *image = nil;
if (shouldCreateGraphicsContext && !currentContext) {
ASDisplayNodeAssert(NO, @"Failed to create a CGContext (size: %@)", NSStringFromCGSize(bounds.size));
//ASDisplayNodeAssert(NO, @"Failed to create a CGContext (size: %@)", NSStringFromCGSize(bounds.size));
return nil;
}

View File

@ -43,7 +43,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
let additionalSize = CGSize(width: screenSize.width, height: screenSize.height + 20.0)
for device in DeviceMetrics.allCases {
if let _ = onScreenNavigationHeight, device.onScreenNavigationHeight(inLandscape: false) == nil {
if let _ = onScreenNavigationHeight, device.onScreenNavigationHeight(inLandscape: false, systemOnScreenNavigationHeight: nil) == nil {
if case .tablet = device.type {
if screenSize.height == 1024.0 && screenSize.width == 768.0 {
} else {
@ -111,16 +111,22 @@ public enum DeviceMetrics: CaseIterable, Equatable {
}
}
func onScreenNavigationHeight(inLandscape: Bool) -> CGFloat? {
func onScreenNavigationHeight(inLandscape: Bool, systemOnScreenNavigationHeight: CGFloat?) -> CGFloat? {
switch self {
case .iPhoneX, .iPhoneXSMax:
return inLandscape ? 21.0 : 34.0
case .iPadPro3rdGen, .iPadPro11Inch:
case .iPhoneX, .iPhoneXSMax:
return inLandscape ? 21.0 : 34.0
case .iPadPro3rdGen, .iPadPro11Inch:
return 21.0
case .iPad, .iPadPro, .iPadPro10Inch:
if let systemOnScreenNavigationHeight = systemOnScreenNavigationHeight, !systemOnScreenNavigationHeight.isZero {
return 21.0
case let .unknown(_, _, onScreenNavigationHeight):
return onScreenNavigationHeight
default:
} else {
return nil
}
case let .unknown(_, _, onScreenNavigationHeight):
return onScreenNavigationHeight
default:
return nil
}
}

View File

@ -1766,7 +1766,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}, node: {
assert(Queue.mainQueue().isCurrent())
return referenceNode.syncWith({ $0 })!
}, params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), previousItem: index == 0 ? nil : self.items[index - 1], nextItem: index == self.items.count - 1 ? nil : self.items[index + 1], animation: updateAnimation, completion: { layout, apply in
}, params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), previousItem: index == 0 ? nil : self.items[index - 1], nextItem: index == self.items.count - 1 ? nil : self.items[index + 1], animation: updateAnimation, completion: { layout, apply in
var updatedState = state
var updatedOperations = operations
@ -1841,7 +1841,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
let index = insertionItemIndexAndDirection.0
let threadId = pthread_self()
var tailRecurse = false
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: self.items[index], previousNode: previousNodes[index], index: index, previousItem: index == 0 ? nil : self.items[index - 1], nextItem: self.items.count == index + 1 ? nil : self.items[index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), updateAnimation: updateAnimation, completion: { (node, layout, apply) in
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: self.items[index], previousNode: previousNodes[index], index: index, previousItem: index == 0 ? nil : self.items[index - 1], nextItem: self.items.count == index + 1 ? nil : self.items[index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimation: updateAnimation, completion: { (node, layout, apply) in
if pthread_equal(pthread_self(), threadId) != 0 && !tailRecurse {
tailRecurse = true
@ -1877,7 +1877,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} else {
let updateItem = updateIndicesAndItems[0]
if let previousNode = previousNodes[updateItem.index] {
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), updateAnimation: animated ? .System(duration: insertionAnimationDuration) : .None, completion: { _, layout, apply in
self.nodeForItem(synchronous: synchronous, synchronousLoads: synchronousLoads, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right, availableHeight: state.visibleSize.height - state.insets.top - state.insets.bottom), updateAnimation: animated ? .System(duration: insertionAnimationDuration) : .None, completion: { _, layout, apply in
state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, animation: animated ? .System(duration: insertionAnimationDuration) : .None, apply: apply, operations: &operations)
updateIndicesAndItems.remove(at: 0)

View File

@ -73,11 +73,13 @@ public struct ListViewItemLayoutParams {
public let width: CGFloat
public let leftInset: CGFloat
public let rightInset: CGFloat
public let availableHeight: CGFloat
public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat) {
public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, availableHeight: CGFloat) {
self.width = width
self.leftInset = leftInset
self.rightInset = rightInset
self.availableHeight = availableHeight
}
}

View File

@ -747,15 +747,19 @@ open class NavigationController: UINavigationController, ContainableController,
case .compact:
if visibleModalCount != 0 {
let effectiveRootModalDismissProgress: CGFloat
let visibleRootModalDismissProgress: CGFloat
let additionalModalFrameProgress: CGFloat
if visibleModalCount == 1 {
effectiveRootModalDismissProgress = topModalIsFlat ? 1.0 : topModalDismissProgress
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
additionalModalFrameProgress = 0.0
} else if visibleModalCount == 2 {
effectiveRootModalDismissProgress = 0.0
visibleRootModalDismissProgress = topModalDismissProgress
additionalModalFrameProgress = 1.0 - topModalDismissProgress
} else {
effectiveRootModalDismissProgress = 0.0
visibleRootModalDismissProgress = effectiveRootModalDismissProgress
additionalModalFrameProgress = 1.0
}
@ -820,8 +824,8 @@ open class NavigationController: UINavigationController, ContainableController,
maxOffset = (topInset + 10.0 - (layout.size.height - layout.size.height * maxScale) / 2.0)
}
let scale = 1.0 * effectiveRootModalDismissProgress + (1.0 - effectiveRootModalDismissProgress) * maxScale
let offset = (1.0 - effectiveRootModalDismissProgress) * maxOffset
let scale = 1.0 * visibleRootModalDismissProgress + (1.0 - visibleRootModalDismissProgress) * maxScale
let offset = (1.0 - visibleRootModalDismissProgress) * maxOffset
transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: scale, offset: CGPoint(x: 0.0, y: offset))
}
} else {

View File

@ -467,6 +467,11 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes
}
currentParent = currentParent?.superview
}
if let controller = self.container.controllers.last {
if controller.view.disablesInteractiveModalDismiss {
enableScrolling = false
}
}
self.isInteractiveDimissEnabled = enableScrolling
if let layout = self.validLayout {
if layout.inputHeight != nil && layout.inputHeight != 0.0 {

View File

@ -310,7 +310,7 @@ public class Window1 {
let isLandscape = boundsSize.width > boundsSize.height
let safeInsets = self.deviceMetrics.safeInsets(inLandscape: isLandscape)
let onScreenNavigationHeight = self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape)
let onScreenNavigationHeight = self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape, systemOnScreenNavigationHeight: self.hostView.onScreenNavigationHeight)
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsets, onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil, inVoiceOver: UIAccessibility.isVoiceOverRunning)
self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate)
@ -950,7 +950,7 @@ public class Window1 {
statusBarHeight = max(statusBarHeightValue, statusBarHost.statusBarFrame.size.height)
}
if self.deviceMetrics.type == .tablet, let onScreenNavigationHeight = self.hostView.onScreenNavigationHeight, onScreenNavigationHeight != self.deviceMetrics.onScreenNavigationHeight(inLandscape: false) {
if self.deviceMetrics.type == .tablet, let onScreenNavigationHeight = self.hostView.onScreenNavigationHeight, onScreenNavigationHeight != self.deviceMetrics.onScreenNavigationHeight(inLandscape: false, systemOnScreenNavigationHeight: self.hostView.onScreenNavigationHeight) {
self.deviceMetrics = DeviceMetrics(screenSize: UIScreen.main.bounds.size, statusBarHeight: statusBarHeight ?? defaultStatusBarHeight, onScreenNavigationHeight: onScreenNavigationHeight)
}
@ -967,7 +967,7 @@ public class Window1 {
}
let previousInputOffset = inputHeightOffsetForLayout(self.windowLayout)
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, metrics: layoutMetricsForScreenSize(updatingLayout.layout.size), statusBarHeight: statusBarHeight, forceInCallStatusBarText: updatingLayout.layout.forceInCallStatusBarText, inputHeight: updatingLayout.layout.inputHeight, safeInsets: updatingLayout.layout.safeInsets, onScreenNavigationHeight: self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape), upperKeyboardInputPositionBound: updatingLayout.layout.upperKeyboardInputPositionBound, inVoiceOver: updatingLayout.layout.inVoiceOver)
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, metrics: layoutMetricsForScreenSize(updatingLayout.layout.size), statusBarHeight: statusBarHeight, forceInCallStatusBarText: updatingLayout.layout.forceInCallStatusBarText, inputHeight: updatingLayout.layout.inputHeight, safeInsets: updatingLayout.layout.safeInsets, onScreenNavigationHeight: self.deviceMetrics.onScreenNavigationHeight(inLandscape: isLandscape, systemOnScreenNavigationHeight: self.hostView.onScreenNavigationHeight), upperKeyboardInputPositionBound: updatingLayout.layout.upperKeyboardInputPositionBound, inVoiceOver: updatingLayout.layout.inVoiceOver)
let childLayout = containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics)
let childLayoutUpdated = self.updatedContainerLayout != childLayout

View File

@ -851,7 +851,7 @@ const CGPoint TGLocationPickerPinOffset = { 0.0f, 33.0f };
}];
CLLocationCoordinate2D coordinate = _currentUserLocation.coordinate;
searchSignal = [self.nearbyPlacesSignal(searchQuery, _currentUserLocation) deliverOn:[SQueue mainQueue]];
searchSignal = [searchSignal then:[self.nearbyPlacesSignal(searchQuery, _currentUserLocation) deliverOn:[SQueue mainQueue]]];
if (_searchDisposable == nil)
_searchDisposable = [[SMetaDisposable alloc] init];

View File

@ -1221,7 +1221,7 @@
{
[super scrollViewDidScroll:scrollView];
_mapView.compassInsets = UIEdgeInsetsMake(TGLocationMapInset + 108.0f + (scrollView.contentOffset.y + scrollView.contentInset.top) / 2.0f, 0.0f, 0.0f, 10.0f + TGScreenPixel);
_mapView.compassInsets = UIEdgeInsetsMake(TGLocationMapInset + 120.0f + (scrollView.contentOffset.y + scrollView.contentInset.top) / 2.0f, 0.0f, 0.0f, 10.0f + TGScreenPixel);
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

View File

@ -18,6 +18,12 @@ private final class LegacyImagePickerController: LegacyController, TGLegacyCamer
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.disablesInteractiveModalDismiss = true
}
func legacyCameraControllerCompletedWithNoResult() {
self.completion(nil)
}

View File

@ -700,7 +700,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
guard let strongSelf = self else {
return
}
strongSelf.interaction.present(SecureIdPlaintextFormController(context: strongSelf.context, secureIdContext: secureIdContext, type: type, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { valueWithContext in
strongSelf.interaction.push(SecureIdPlaintextFormController(context: strongSelf.context, secureIdContext: secureIdContext, type: type, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { valueWithContext in
if let strongSelf = self {
strongSelf.interaction.updateState { state in
if case let .form(form) = state, let formData = form.formData {
@ -723,7 +723,7 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
return state
}
}
}), nil)
}))
}
if let currentValue = currentValue {
@ -941,9 +941,9 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
if let peer = list.accountPeer as? TelegramUser, let phone = peer.phone, !phone.isEmpty {
immediatelyAvailableValue = .phone(SecureIdPhoneValue(phone: phone))
}
self.interaction.present(SecureIdPlaintextFormController(context: self.context, secureIdContext: secureIdContext, type: .phone, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
self.interaction.push(SecureIdPlaintextFormController(context: self.context, secureIdContext: secureIdContext, type: .phone, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
updatedValues(.phone)(value.flatMap({ [$0] }) ?? [])
}), nil)
}))
}
case .email:
if findValue(values, key: .email) != nil {
@ -953,9 +953,9 @@ final class SecureIdAuthControllerNode: ViewControllerTracingNode {
if let email = list.twoStepEmail {
immediatelyAvailableValue = .email(SecureIdEmailValue(email: email))
}
self.interaction.present(SecureIdPlaintextFormController(context: self.context, secureIdContext: secureIdContext, type: .email, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
self.interaction.push(SecureIdPlaintextFormController(context: self.context, secureIdContext: secureIdContext, type: .email, immediatelyAvailableValue: immediatelyAvailableValue, updatedValue: { value in
updatedValues(.email)(value.flatMap({ [$0] }) ?? [])
}), nil)
}))
}
}
}

View File

@ -37,6 +37,8 @@ public final class SecureIdPlaintextFormController: FormController<SecureIdPlain
super.init(initParams: SecureIdPlaintextFormControllerNodeInitParams(context: context, secureIdContext: secureIdContext), presentationData: self.presentationData)
self.navigationPresentation = .modal
switch type {
case .phone:
self.title = self.presentationData.strings.Passport_Phone_Title

View File

@ -782,11 +782,8 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
guard let controller = controller, let navigationController = controller.navigationController as? NavigationController else {
return
}
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(upgradedPeerId), keepStack: .never, animated: false, completion: {
navigationController.pushViewController(channelAdminsController(context: context, peerId: upgradedPeerId, loadCompleted: {
f()
}), animated: false)
}))
rebuildControllerStackAfterSupergroupUpgrade(controller: controller, navigationController: navigationController)
}
controller.visibleBottomContentOffsetChanged = { offset in
if case let .known(value) = offset, value < 40.0 {
@ -795,3 +792,19 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
}
return controller
}
func rebuildControllerStackAfterSupergroupUpgrade(controller: ViewController, navigationController: NavigationController) {
var controllers = navigationController.viewControllers
for i in 0 ..< controllers.count {
if controllers[i] === controller {
for j in 0 ..< i {
if controllers[j] is ChatController {
controllers.removeSubrange(j + 1 ... i - 1)
break
}
}
break
}
}
navigationController.setViewControllers(controllers, animated: false)
}

View File

@ -858,9 +858,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina
return
}
sourcePeerId.set(.single((upgradedPeerId, true)))
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(upgradedPeerId), keepStack: .never, animated: false, completion: {
navigationController.pushViewController(controller, animated: false)
}))
rebuildControllerStackAfterSupergroupUpgrade(controller: controller, navigationController: navigationController)
}
controller.visibleBottomContentOffsetChanged = { offset in

View File

@ -465,7 +465,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
} else if let cachedChannelData = view.cachedData as? CachedChannelData, cachedChannelData.peerGeoLocation != nil {
selectedType = .publicChannel
} else {
selectedType = .privateChannel
selectedType = .publicChannel
}
}
}
@ -512,7 +512,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
case .publicChannel:
var displayAvailability = false
if peer.addressName == nil {
displayAvailability = publicChannelsToRevoke == nil || !(publicChannelsToRevoke!.isEmpty)
displayAvailability = publicChannelsToRevoke != nil && !(publicChannelsToRevoke!.isEmpty)
}
if displayAvailability {

View File

@ -40,6 +40,9 @@ import AppBundle
import Markdown
import LocalizedPeerData
private let maxParticipantsDisplayedLimit: Int32 = 50
private let maxParticipantsDisplayedCollapseLimit: Int32 = 60
private final class GroupInfoArguments {
let context: AccountContext
@ -71,8 +74,9 @@ private final class GroupInfoArguments {
let openLocation: (PeerGeoLocation) -> Void
let changeLocation: () -> Void
let displayLocationContextMenu: (String) -> Void
let expandParticipants: () -> Void
init(context: AccountContext, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, tapAvatarAction: @escaping () -> Void, changeProfilePhoto: @escaping () -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, ViewControllerPresentationArguments) -> Void, changeNotificationMuteSettings: @escaping () -> Void, openPreHistory: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openAdministrators: @escaping () -> Void, openPermissions: @escaping () -> Void, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updateEditingDescriptionText: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addMember: @escaping () -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void, leave: @escaping () -> Void, displayUsernameShareMenu: @escaping (String) -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, openStickerPackSetup: @escaping () -> Void, openGroupTypeSetup: @escaping () -> Void, openLinkedChannelSetup: @escaping () -> Void, openLocation: @escaping (PeerGeoLocation) -> Void, changeLocation: @escaping () -> Void, displayLocationContextMenu: @escaping (String) -> Void) {
init(context: AccountContext, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, tapAvatarAction: @escaping () -> Void, changeProfilePhoto: @escaping () -> Void, pushController: @escaping (ViewController) -> Void, presentController: @escaping (ViewController, ViewControllerPresentationArguments) -> Void, changeNotificationMuteSettings: @escaping () -> Void, openPreHistory: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openAdministrators: @escaping () -> Void, openPermissions: @escaping () -> Void, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updateEditingDescriptionText: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, addMember: @escaping () -> Void, promotePeer: @escaping (RenderedChannelParticipant) -> Void, restrictPeer: @escaping (RenderedChannelParticipant) -> Void, removePeer: @escaping (PeerId) -> Void, leave: @escaping () -> Void, displayUsernameShareMenu: @escaping (String) -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayAboutContextMenu: @escaping (String) -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, openStickerPackSetup: @escaping () -> Void, openGroupTypeSetup: @escaping () -> Void, openLinkedChannelSetup: @escaping () -> Void, openLocation: @escaping (PeerGeoLocation) -> Void, changeLocation: @escaping () -> Void, displayLocationContextMenu: @escaping (String) -> Void, expandParticipants: @escaping () -> Void) {
self.context = context
self.avatarAndNameInfoContext = avatarAndNameInfoContext
self.tapAvatarAction = tapAvatarAction
@ -102,6 +106,7 @@ private final class GroupInfoArguments {
self.openLocation = openLocation
self.changeLocation = changeLocation
self.displayLocationContextMenu = displayLocationContextMenu
self.expandParticipants = expandParticipants
}
}
@ -189,6 +194,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
case permissions(PresentationTheme, String, String)
case addMember(PresentationTheme, String, editing: Bool)
case member(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, index: Int, peerId: PeerId, peer: Peer, participant: RenderedChannelParticipant?, presence: PeerPresence?, memberStatus: GroupInfoMemberStatus, editing: ItemListPeerItemEditing, revealActions: [ParticipantRevealAction], enabled: Bool, selectable: Bool)
case expand(PresentationTheme, String)
case leave(PresentationTheme, String)
var section: ItemListSectionId {
@ -203,7 +209,7 @@ private enum GroupInfoEntry: ItemListNodeEntry {
return GroupInfoSection.sharedMediaAndNotifications.rawValue
case .permissions, .administrators:
return GroupInfoSection.memberManagement.rawValue
case .addMember, .member:
case .addMember, .member, .expand:
return GroupInfoSection.members.rawValue
case .leave:
return GroupInfoSection.leave.rawValue
@ -213,40 +219,40 @@ private enum GroupInfoEntry: ItemListNodeEntry {
static func ==(lhs: GroupInfoEntry, rhs: GroupInfoEntry) -> Bool {
switch lhs {
case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsCachedData, lhsState, lhsUpdatingAvatar):
if case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsState, rhsUpdatingAvatar) = rhs {
if lhsTheme !== rhsTheme {
return false
}
if lhsStrings !== rhsStrings {
return false
}
if lhsDateTimeFormat != rhsDateTimeFormat {
return false
}
if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
if !lhsPeer.isEqual(rhsPeer) {
return false
}
} else if (lhsPeer == nil) != (rhsPeer != nil) {
return false
}
if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
if !lhsCachedData.isEqual(to: rhsCachedData) {
return false
}
} else if (lhsCachedData != nil) != (rhsCachedData != nil) {
return false
}
if lhsState != rhsState {
return false
}
if lhsUpdatingAvatar != rhsUpdatingAvatar {
return false
}
return true
} else {
if case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsCachedData, rhsState, rhsUpdatingAvatar) = rhs {
if lhsTheme !== rhsTheme {
return false
}
if lhsStrings !== rhsStrings {
return false
}
if lhsDateTimeFormat != rhsDateTimeFormat {
return false
}
if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer {
if !lhsPeer.isEqual(rhsPeer) {
return false
}
} else if (lhsPeer == nil) != (rhsPeer != nil) {
return false
}
if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData {
if !lhsCachedData.isEqual(to: rhsCachedData) {
return false
}
} else if (lhsCachedData != nil) != (rhsCachedData != nil) {
return false
}
if lhsState != rhsState {
return false
}
if lhsUpdatingAvatar != rhsUpdatingAvatar {
return false
}
return true
} else {
return false
}
case let .setGroupPhoto(lhsTheme, lhsText):
if case let .setGroupPhoto(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
@ -419,6 +425,12 @@ private enum GroupInfoEntry: ItemListNodeEntry {
} else {
return false
}
case let .expand(lhsTheme, lhsText):
if case let .expand(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
}
}
@ -469,8 +481,10 @@ private enum GroupInfoEntry: ItemListNodeEntry {
return 16
case let .member(_, _, _, _, index, _, _, _, _, _, _, _, _, _):
return 20 + index
case .leave:
case .expand:
return 200000 + 1
case .leave:
return 200000 + 2
}
}
@ -592,6 +606,10 @@ private enum GroupInfoEntry: ItemListNodeEntry {
}, removePeer: { peerId in
arguments.removePeer(peerId)
})
case let .expand(theme, title):
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
arguments.expandParticipants()
})
case let .leave(theme, title):
return ItemListActionItem(theme: theme, title: title, kind: .destructive, alignment: .center, sectionId: self.section, style: .blocks, action: {
arguments.leave()
@ -627,6 +645,7 @@ private struct GroupInfoState: Equatable {
let editingState: GroupInfoEditingState?
let updatingName: ItemListAvatarAndNameInfoItemName?
let peerIdWithRevealedOptions: PeerId?
let expandedParticipants: Bool
let temporaryParticipants: [TemporaryParticipant]
let successfullyAddedParticipantIds: Set<PeerId>
@ -649,6 +668,9 @@ private struct GroupInfoState: Equatable {
if lhs.peerIdWithRevealedOptions != rhs.peerIdWithRevealedOptions {
return false
}
if lhs.expandedParticipants != rhs.expandedParticipants {
return false
}
if lhs.temporaryParticipants != rhs.temporaryParticipants {
return false
}
@ -668,39 +690,43 @@ private struct GroupInfoState: Equatable {
}
func withUpdatedUpdatingAvatar(_ updatingAvatar: ItemListAvatarAndNameInfoItemUpdatingAvatar?) -> GroupInfoState {
return GroupInfoState(updatingAvatar: updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedEditingState(_ editingState: GroupInfoEditingState?) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedUpdatingName(_ updatingName: ItemListAvatarAndNameInfoItemName?) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedPeerIdWithRevealedOptions(_ peerIdWithRevealedOptions: PeerId?) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedExpandedParticipants(_ expandedParticipants: Bool) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedTemporaryParticipants(_ temporaryParticipants: [TemporaryParticipant]) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedSuccessfullyAddedParticipantIds(_ successfullyAddedParticipantIds: Set<PeerId>) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedRemovingParticipantIds(_ removingParticipantIds: Set<PeerId>) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: removingParticipantIds, savingData: self.savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedSavingData(_ savingData: Bool) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: savingData, searchingMembers: self.searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: savingData, searchingMembers: self.searchingMembers)
}
func withUpdatedSearchingMembers(_ searchingMembers: Bool) -> GroupInfoState {
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: searchingMembers)
return GroupInfoState(updatingAvatar: self.updatingAvatar, editingState: self.editingState, updatingName: self.updatingName, peerIdWithRevealedOptions: self.peerIdWithRevealedOptions, expandedParticipants: self.expandedParticipants, temporaryParticipants: self.temporaryParticipants, successfullyAddedParticipantIds: self.successfullyAddedParticipantIds, removingParticipantIds: self.removingParticipantIds, savingData: self.savingData, searchingMembers: searchingMembers)
}
}
@ -1157,8 +1183,21 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
sortedParticipants = updatedParticipants
}
for i in 0 ..< sortedParticipants.count {
let participant = sortedParticipants[i]
var expanded = state.expandedParticipants
let participants: [RenderedChannelParticipant]
if expanded {
participants = sortedParticipants
} else {
if sortedParticipants.count > maxParticipantsDisplayedCollapseLimit {
participants = Array(sortedParticipants.prefix(Int(maxParticipantsDisplayedLimit)))
} else {
participants = sortedParticipants
expanded = true
}
}
for i in 0 ..< participants.count {
let participant = participants[i]
let memberStatus: GroupInfoMemberStatus
switch participant.participant {
case let .creator(_, rank):
@ -1220,6 +1259,10 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
entries.append(GroupInfoEntry.member(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, index: i, peerId: participant.peer.id, peer: participant.peer, participant: participant, presence: participant.presences[participant.peer.id], memberStatus: memberStatus, editing: ItemListPeerItemEditing(editable: !peerActions.isEmpty, editing: state.editingState != nil && canRemoveAnyMember, revealed: state.peerIdWithRevealedOptions == participant.peer.id), revealActions: peerActions, enabled: true, selectable: participant.peer.id != account.peerId))
}
if !expanded {
entries.append(GroupInfoEntry.expand(presentationData.theme, presentationData.strings.GroupInfo_ShowMoreMembers(Int32(memberCount - maxParticipantsDisplayedLimit))))
}
}
if let group = view.peers[view.peerId] as? TelegramGroup {
@ -1288,8 +1331,8 @@ private func valuesRequiringUpdate(state: GroupInfoState, view: PeerView) -> (ti
}
public func groupInfoController(context: AccountContext, peerId originalPeerId: PeerId, membersLoaded: @escaping () -> Void = {}) -> ViewController {
let statePromise = ValuePromise(GroupInfoState(updatingAvatar: nil, editingState: nil, updatingName: nil, peerIdWithRevealedOptions: nil, temporaryParticipants: [], successfullyAddedParticipantIds: Set(), removingParticipantIds: Set(), savingData: false, searchingMembers: false), ignoreRepeated: true)
let stateValue = Atomic(value: GroupInfoState(updatingAvatar: nil, editingState: nil, updatingName: nil, peerIdWithRevealedOptions: nil, temporaryParticipants: [], successfullyAddedParticipantIds: Set(), removingParticipantIds: Set(), savingData: false, searchingMembers: false))
let statePromise = ValuePromise(GroupInfoState(updatingAvatar: nil, editingState: nil, updatingName: nil, peerIdWithRevealedOptions: nil, expandedParticipants: false, temporaryParticipants: [], successfullyAddedParticipantIds: Set(), removingParticipantIds: Set(), savingData: false, searchingMembers: false), ignoreRepeated: true)
let stateValue = Atomic(value: GroupInfoState(updatingAvatar: nil, editingState: nil, updatingName: nil, peerIdWithRevealedOptions: nil, expandedParticipants: false, temporaryParticipants: [], successfullyAddedParticipantIds: Set(), removingParticipantIds: Set(), savingData: false, searchingMembers: false))
let updateState: ((GroupInfoState) -> GroupInfoState) -> Void = { f in
statePromise.set(stateValue.modify { f($0) })
}
@ -2077,6 +2120,10 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
})
}, displayLocationContextMenu: { text in
displayCopyContextMenuImpl?(text, .location)
}, expandParticipants: {
updateState {
$0.withUpdatedExpandedParticipants(true)
}
})
let loadMoreControl = Atomic<(PeerId, PeerChannelMemberCategoryControl)?>(value: nil)
@ -2453,7 +2500,9 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
controller.visibleBottomContentOffsetChanged = { offset in
if let (peerId, loadMoreControl) = loadMoreControl.with({ $0 }), case let .known(value) = offset, value < 40.0 {
context.peerChannelMemberCategoriesContextsManager.loadMore(peerId: peerId, control: loadMoreControl)
if stateValue.with({ $0 }).expandedParticipants {
context.peerChannelMemberCategoriesContextsManager.loadMore(peerId: peerId, control: loadMoreControl)
}
}
}
return controller

View File

@ -1361,7 +1361,8 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
})
}
startSecretChatImpl = { [weak controller] in
let _ = (context.account.postbox.transaction { transaction -> PeerId? in
let _ = (context.account.postbox.transaction { transaction -> (Peer?, PeerId?) in
let peer = transaction.getPeer(peerId)
let filteredPeerIds = Array(transaction.getAssociatedPeerIds(peerId)).filter { $0.namespace == Namespaces.Peer.SecretChat }
var activeIndices: [ChatListIndex] = []
for associatedId in filteredPeerIds {
@ -1378,54 +1379,57 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
}
activeIndices.sort()
if let index = activeIndices.last {
return index.messageIndex.id.peerId
return (peer, index.messageIndex.id.peerId)
} else {
return nil
return (peer, nil)
}
} |> deliverOnMainQueue).start(next: { currentPeerId in
} |> deliverOnMainQueue).start(next: { peer, currentPeerId in
if let currentPeerId = currentPeerId {
if let navigationController = (controller?.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(currentPeerId)))
}
} else {
var createSignal = createSecretChat(account: context.account, peerId: peerId)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
presentControllerImpl?(controller, nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
} else if let controller = controller {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let displayTitle = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? ""
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.UserInfo_StartSecretChatConfirmation(displayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.UserInfo_StartSecretChatStart, action: {
var createSignal = createSecretChat(account: context.account, peerId: peerId)
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
presentControllerImpl?(controller, nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
createSignal = createSignal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
createSignal = createSignal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
}
cancelImpl = {
createSecretChatDisposable.set(nil)
}
createSecretChatDisposable.set((createSignal |> deliverOnMainQueue).start(next: { peerId in
if let navigationController = (controller?.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
cancelImpl = {
createSecretChatDisposable.set(nil)
}
}, error: { _ in
if let controller = controller {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
createSecretChatDisposable.set((createSignal |> deliverOnMainQueue).start(next: { [weak controller] peerId in
if let navigationController = (controller?.navigationController as? NavigationController) {
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId)))
}
}, error: { [weak controller] _ in
if let controller = controller {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
})]), in: .window(.root))
}
})
}

View File

@ -430,14 +430,17 @@ final class AccountManagerImpl {
}
}
private let sharedQueue = Queue()
public final class AccountManager {
public let basePath: String
public let mediaBox: MediaBox
private let queue = Queue()
private let queue: Queue
private let impl: QueueLocalObject<AccountManagerImpl>
public let temporarySessionId: Int64
public init(basePath: String) {
self.queue = sharedQueue
self.basePath = basePath
var temporarySessionId: Int64 = 0
arc4random_buf(&temporarySessionId, 8)

View File

@ -202,11 +202,16 @@ public final class MediaBox {
return "\(self.basePath)/\(cacheString)/\(fileNameForId(id)):\(representation.uniqueId)"
}
public func storeResourceData(_ id: MediaResourceId, data: Data) {
self.dataQueue.async {
public func storeResourceData(_ id: MediaResourceId, data: Data, synchronous: Bool = false) {
let begin = {
let paths = self.storePathsForId(id)
let _ = try? data.write(to: URL(fileURLWithPath: paths.complete), options: [.atomic])
}
if synchronous {
begin()
} else {
self.dataQueue.async(begin)
}
}
public func moveResourceData(_ id: MediaResourceId, fromTempPath: String) {

View File

@ -3102,7 +3102,9 @@ public final class Postbox {
return nil
}
if let messages = self.messageHistoryTable.getMessageFailedGroup(at: index, limit: 100) {
return messages.map(self.renderIntermediateMessage)
return messages.sorted(by: { lhs, rhs in
return lhs.index < rhs.index
}).map(self.renderIntermediateMessage)
} else {
return nil
}

View File

@ -13,7 +13,7 @@ import PresentationDataUtils
private func stringForKeepMediaTimeout(strings: PresentationStrings, timeout: Int32) -> String {
if timeout > 1 * 31 * 24 * 60 * 60 {
return strings.MessageTimer_Forever
return strings.ClearCache_Forever
} else {
return timeIntervalString(strings: strings, value: timeout)
}

View File

@ -288,12 +288,12 @@ private func storageUsageControllerEntries(presentationData: PresentationData, c
let totalSpaceValue = CGFloat(totalSpace)
if telegramCacheSize > 0 {
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageCache, size: totalTelegramSize, fraction: CGFloat(totalTelegramSize) / totalSpaceValue, color: presentationData.theme.list.itemAccentColor))
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageCache, size: totalTelegramSize, fraction: CGFloat(totalTelegramSize) / totalSpaceValue, color: presentationData.theme.list.itemBarChart.color1))
} else {
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageServiceFiles, size: totalTelegramSize, fraction: CGFloat(totalTelegramSize) / totalSpaceValue, color: presentationData.theme.list.itemAccentColor))
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageServiceFiles, size: totalTelegramSize, fraction: CGFloat(totalTelegramSize) / totalSpaceValue, color: presentationData.theme.list.itemBarChart.color1))
}
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageOtherApps, size: otherAppsSpace, fraction: CGFloat(otherAppsSpace) / totalSpaceValue, color: presentationData.theme.list.itemBlocksSeparatorColor))
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageFree, size: freeSpace, fraction: CGFloat(freeSpace) / totalSpaceValue, color: UIColor(rgb: 0xf2f1f7)))
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageOtherApps, size: otherAppsSpace, fraction: CGFloat(otherAppsSpace) / totalSpaceValue, color: presentationData.theme.list.itemBarChart.color2))
categories.append(StorageUsageCategory(title: presentationData.strings.ClearCache_StorageFree, size: freeSpace, fraction: CGFloat(freeSpace) / totalSpaceValue, color: presentationData.theme.list.itemBarChart.color3))
entries.append(.storageUsage(presentationData.theme, presentationData.dateTimeFormat, categories))
@ -433,11 +433,10 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
controller?.updateItem(groupIndex: 0, itemIndex: itemIndex, { item in
let title: String
var filteredSize = sizeIndex.values.reduce(0, { $0 + ($1.0 ? $1.1 : 0) })
selectedSize = filteredSize
if otherSize.0 {
filteredSize += otherSize.1
}
selectedSize = filteredSize
if filteredSize == 0 {
title = presentationData.strings.Cache_ClearNone
@ -934,6 +933,10 @@ public func storageUsageController(context: AccountContext, cacheUsagePromise: P
}
let controller = ItemListController(context: context, state: signal)
if isModal {
controller.navigationPresentation = .modal
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
}
presentControllerImpl = { [weak controller] c, contextType, a in
controller?.present(c, in: contextType, with: a)
}

View File

@ -180,7 +180,7 @@ private final class StorageUsageItemNode: ListViewItemNode {
let inset: CGFloat = 16.0
let horizontalSpacing: CGFloat = 32.0
let verticalSpacing: CGFloat = 22.0
var textOrigin: CGPoint = CGPoint(x: horizontalSpacing, y: 52.0)
var textOrigin: CGPoint = CGPoint(x: params.leftInset + horizontalSpacing, y: 52.0)
for i in 0 ..< item.categories.count {
let makeTextLayout = makeNodesLayout[i]
@ -281,7 +281,7 @@ private final class StorageUsageItemNode: ListViewItemNode {
var categoryWidth = max(floor(lineWidth * category.fraction), 2.0)
if i == strongSelf.lineNodes.count - 1 {
categoryWidth = lineWidth - (lineOrigin.x - lineInset)
categoryWidth = max(0.0, lineWidth - (lineOrigin.x - lineInset))
}
let lineRect = CGRect(origin: lineOrigin, size: CGSize(width: categoryWidth, height: 21.0))

View File

@ -641,14 +641,14 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
case .set:
break
case let .notSet(pendingEmail):
if pendingEmail == nil {
break
//intro = pendingEmail == nil
/*if pendingEmail == nil {
let controller = TwoFactorAuthSplashScreen(context: context, mode: .intro)
pushControllerImpl?(controller, true)
} else {
}
//intro = pendingEmail == nil
return
}*/
}
}
if intro {

View File

@ -239,7 +239,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let timestamp4 = timestamp + 3000
items.append(ChatListItem(presentationData: chatListPresentationData, context: self.context, peerGroupId: .root, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(id: MessageId(peerId: peer4.id, namespace: 0, id: 0), timestamp: timestamp4)), content: .peer(message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer4.id, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: timestamp4, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peer4, text: self.presentationData.strings.Appearance_ThemePreview_ChatList_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), peer: RenderedPeer(peer: peer4), combinedReadState: nil, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(tagSummaryCount: nil, actionsSummaryCount: nil), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: false, displayAsMessage: false), editing: false, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: false, hiddenOffset: false, interaction: interaction))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let chatNodes = self.chatNodes {
for i in 0 ..< items.count {
let itemNode = chatNodes[i]
@ -308,7 +308,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
let message4 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.theme, strings: self.presentationData.strings, wallpaper: self.theme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< items.count {
let itemNode = messageNodes[i]

View File

@ -291,7 +291,7 @@ final class ThemeColorsGridControllerNode: ASDisplayNode {
}
let makeColorLayout = self.customColorItemNode.asyncLayout()
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right, availableHeight: layout.size.height)
let (colorLayout, colorApply) = makeColorLayout(self.customColorItem, params, ItemListNeighbors(top: .none, bottom: .none))
colorApply()

View File

@ -476,7 +476,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
transition.updateFrame(node: strongSelf.bottomBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height), size: CGSize(width: layout.size.width, height: 500.0)))
transition.updateFrame(node: strongSelf.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: gridLayout.contentSize.height), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
let makeResetLayout = strongSelf.resetItemNode.asyncLayout()
let makeResetDescriptionLayout = strongSelf.resetDescriptionItemNode.asyncLayout()
@ -640,7 +640,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
}
}
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: listInsets.left, rightInset: listInsets.right, availableHeight: layout.size.height)
let (colorLayout, colorApply) = makeColorLayout(self.colorItem, params, ItemListNeighbors(top: .none, bottom: .sameSection(alwaysPlain: false)))
let (galleryLayout, galleryApply) = makeGalleryLayout(self.galleryItem, params, ItemListNeighbors(top: .sameSection(alwaysPlain: false), bottom: .sameSection(alwaysPlain: true)))
let (descriptionLayout, descriptionApply) = makeDescriptionLayout(self.descriptionItem, params, ItemListNeighbors(top: .none, bottom: .none))

View File

@ -372,7 +372,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
width = layout.size.width
}
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let chatNodes = self.chatNodes {
for i in 0 ..< items.count {
let itemNode = chatNodes[i]
@ -452,7 +452,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
width = layout.size.width
}
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< items.count {
let itemNode = messageNodes[i]

View File

@ -360,7 +360,8 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
var entries: [ThemeSettingsControllerEntry] = []
entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme.uppercased()))
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
entries.append(.themeListHeader(presentationData.theme, title))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
@ -371,22 +372,22 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
let title: String
let autoNightMode: String
switch autoNightSettings.trigger {
case .system:
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
title = strings.AutoNightTheme_System
autoNightMode = strings.AutoNightTheme_System
} else {
title = strings.AutoNightTheme_Disabled
autoNightMode = strings.AutoNightTheme_Disabled
}
case .explicitNone:
title = strings.AutoNightTheme_Disabled
autoNightMode = strings.AutoNightTheme_Disabled
case .timeBased:
title = strings.AutoNightTheme_Scheduled
autoNightMode = strings.AutoNightTheme_Scheduled
case .brightness:
title = strings.AutoNightTheme_Automatic
autoNightMode = strings.AutoNightTheme_Automatic
}
entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, title))
entries.append(.autoNightTheme(presentationData.theme, strings.Appearance_AutoNightTheme, autoNightMode))
entries.append(.fontSizeHeader(presentationData.theme, strings.Appearance_TextSize.uppercased()))
entries.append(.fontSize(presentationData.theme, fontSize))
@ -573,32 +574,39 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let dateTimeFormat = presentationData.dateTimeFormat
let largeEmoji = presentationData.largeEmoji
let disableAnimations = presentationData.disableAnimations
let accentColor = settings.themeSpecificAccentColors[settings.theme.index]?.color
let theme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: settings.theme, accentColor: accentColor, serviceBackgroundColor: defaultServiceBackgroundColor, baseColor: settings.themeSpecificAccentColors[settings.theme.index]?.baseColor ?? .blue, preview: true) ?? defaultPresentationTheme
let wallpaper: TelegramWallpaper
if let themeSpecificWallpaper = settings.themeSpecificChatWallpapers[settings.theme.index] {
wallpaper = themeSpecificWallpaper
let themeReference: PresentationThemeReference
if presentationData.autoNightModeTriggered {
themeReference = settings.automaticThemeSwitchSetting.theme
} else {
wallpaper = settings.chatWallpaper
themeReference = settings.theme
}
let theme = presentationData.theme
let accentColor = settings.themeSpecificAccentColors[themeReference.index]?.color
let wallpaper = settings.themeSpecificChatWallpapers[themeReference.index] ?? settings.chatWallpaper
let rightNavigationButton = ItemListNavigationButton(content: .icon(.action), style: .regular, enabled: true, action: {
moreImpl?()
})
let defaultThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)]
var defaultThemes: [PresentationThemeReference] = []
if presentationData.autoNightModeTriggered {
} else {
defaultThemes.append(contentsOf: [.builtin(.dayClassic), .builtin(.day)])
}
defaultThemes.append(contentsOf: [.builtin(.night), .builtin(.nightAccent)])
let cloudThemes: [PresentationThemeReference] = cloudThemes.map { .cloud(PresentationCloudTheme(theme: $0, resolvedWallpaper: nil)) }
var availableThemes = defaultThemes
if defaultThemes.first(where: { $0.index == settings.theme.index }) == nil && cloudThemes.first(where: { $0.index == settings.theme.index }) == nil {
availableThemes.append(settings.theme)
if defaultThemes.first(where: { $0.index == themeReference.index }) == nil && cloudThemes.first(where: { $0.index == themeReference.index }) == nil {
availableThemes.append(themeReference)
}
availableThemes.append(contentsOf: cloudThemes)
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: themeReference, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
return (controllerState, (listState, arguments))
}
@ -622,6 +630,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
return
}
let autoNightModeTriggered = context.sharedContext.currentPresentationData.with { $0 }.autoNightModeTriggered
let resolvedWallpaper: Signal<TelegramWallpaper?, NoError>
if case let .file(file) = presentationTheme.chat.defaultWallpaper, file.id == 0 {
resolvedWallpaper = cachedWallpaper(account: context.account, slug: file.slug, settings: file.settings)
@ -653,6 +663,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
current = PresentationThemeSettings.defaultSettings
}
var theme = current.theme
var automaticThemeSwitchSetting = current.automaticThemeSwitchSetting
if autoNightModeTriggered {
automaticThemeSwitchSetting.theme = updatedTheme
} else {
theme = updatedTheme
}
let chatWallpaper: TelegramWallpaper
if let themeSpecificWallpaper = current.themeSpecificChatWallpapers[updatedTheme.index] {
chatWallpaper = themeSpecificWallpaper
@ -660,8 +678,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
let presentationTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: updatedTheme, accentColor: current.themeSpecificAccentColors[updatedTheme.index]?.color, serviceBackgroundColor: .black, baseColor: nil) ?? defaultPresentationTheme
chatWallpaper = resolvedWallpaper ?? presentationTheme.chat.defaultWallpaper
}
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: updatedTheme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
return PresentationThemeSettings(chatWallpaper: chatWallpaper, theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
})
})
}).start()

View File

@ -112,7 +112,7 @@ private func updatedFileWallpaper(id: Int64? = nil, accessHash: Int64? = nil, sl
if let color = color {
colorValue = Int32(bitPattern: color.rgb)
intensityValue = intensity
} else {
} else if isPattern {
colorValue = 0xd6e2ee
intensityValue = 50
}
@ -384,10 +384,11 @@ public class WallpaperGalleryController: ViewController {
let updatedSettings = WallpaperSettings(blur: options.contains(.blur), motion: options.contains(.motion), color: baseSettings?.color, intensity: baseSettings?.intensity)
let wallpaper = wallpaper.withUpdatedSettings(updatedSettings)
let autoNightModeTriggered = strongSelf.presentationData.autoNightModeTriggered
let _ = (updatePresentationThemeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { current in
var themeSpecificChatWallpapers = current.themeSpecificChatWallpapers
var chatWallpaper = current.chatWallpaper
if automaticThemeShouldSwitchNow(settings: current.automaticThemeSwitchSetting, systemUserInterfaceStyle: .light) {
if autoNightModeTriggered {
themeSpecificChatWallpapers[current.automaticThemeSwitchSetting.theme.index] = wallpaper
} else {
themeSpecificChatWallpapers[current.theme.index] = wallpaper
@ -412,23 +413,41 @@ public class WallpaperGalleryController: ViewController {
if options.contains(.blur) {
if let resource = resource {
let representation = CachedBlurredWallpaperRepresentation()
let _ = strongSelf.context.account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true).start()
var data: Data?
if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
data = maybeData
} else if let path = strongSelf.context.sharedContext.accountManager.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
data = maybeData
}
if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data)
let _ = strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true).start(completed: {
if let data = data {
strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let _ = (strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true)
|> filter({ $0.complete })
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
completion(wallpaper)
})
}
}
} else if case let .file(file) = wallpaper {
} else if case let .file(file) = wallpaper, let resource = resource {
if file.isPattern, let color = file.settings.color, let intensity = file.settings.intensity {
let representation = CachedPatternWallpaperRepresentation(color: color, intensity: intensity)
let _ = strongSelf.context.account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: representation, complete: true, fetch: true).start()
if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data)
let _ = strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: representation, complete: true, fetch: true).start(completed: {
var data: Data?
if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
data = maybeData
} else if let path = strongSelf.context.sharedContext.accountManager.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) {
data = maybeData
}
if let data = data {
strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
let _ = (strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true)
|> filter({ $0.complete })
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
completion(wallpaper)
})
}
@ -629,7 +648,7 @@ public class WallpaperGalleryController: ViewController {
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: currentWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
if let messageNodes = self.messageNodes {
for i in 0 ..< items.count {
let itemNode = messageNodes[i]

View File

@ -292,8 +292,8 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
signal = wallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, fileReference: fileReference, representations: convertedRepresentations, alwaysShowThumbnailFirst: true, autoFetchFullSize: false)
}
fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: convertedRepresentations[convertedRepresentations.count - 1].reference)
let account = context.account
statusSignal = context.sharedContext.accountManager.mediaBox.resourceStatus(file.file.resource)
let account = self.context.account
statusSignal = self.context.sharedContext.accountManager.mediaBox.resourceStatus(file.file.resource)
|> take(1)
|> mapToSignal { status -> Signal<MediaResourceStatus, NoError> in
if case .Local = status {

View File

@ -23,6 +23,7 @@ static_library(
"//submodules/ActivityIndicator:ActivityIndicator",
"//submodules/AppBundle:AppBundle",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/AppIntents:AppIntents",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -8,6 +8,7 @@ import TelegramCore
import SyncCore
import TelegramPresentationData
import AccountContext
import AppIntents
enum ShareState {
case preparing
@ -26,6 +27,7 @@ func openExternalShare(state: () -> Signal<ShareExternalState, NoError>) {
final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let sharedContext: SharedAccountContext
private var account: Account?
private var presentationData: PresentationData
private let externalShare: Bool
private let immediateExternalShare: Bool
@ -537,6 +539,10 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
peerIds = self.controllerInteraction!.selectedPeers.map { $0.peerId }
}
if let account = self.account {
donateSendMessageIntent(account: account, sharedContext: self.sharedContext, peerIds: peerIds)
}
if let signal = self.share?(self.inputFieldNode.text, peerIds) {
self.transitionToContentNode(ShareLoadingContainerNode(theme: self.presentationData.theme, forceNativeAppearance: true), fastOut: true)
let timestamp = CACurrentMediaTime()
@ -629,6 +635,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate
}
func updatePeers(account: Account, switchableAccounts: [AccountWithInfo], peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, defaultAction: ShareControllerAction?) {
self.account = account
if let peersContentNode = self.peersContentNode, peersContentNode.accountPeer.id == accountPeer.id {
peersContentNode.peersValue.set(.single(peers))
return

View File

@ -19,18 +19,23 @@ public enum ManagedAudioSessionType: Equatable {
}
}
private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: Bool) -> AVAudioSession.Category {
private func nativeCategoryForType(_ type: ManagedAudioSessionType, headphones: Bool, outputMode: AudioSessionOutputMode) -> AVAudioSession.Category {
switch type {
case .play:
case .play:
return .playback
case .record, .voiceCall:
return .playAndRecord
case .playWithPossiblePortOverride:
if headphones {
return .playback
case .record, .voiceCall:
return .playAndRecord
case .playWithPossiblePortOverride:
if headphones {
return .playback
} else {
} else {
switch outputMode {
case .custom(.speaker), .system:
return .playAndRecord
default:
return .playback
}
}
}
}
@ -638,25 +643,29 @@ public final class ManagedAudioSession {
self.currentTypeAndOutputMode = (type, outputMode)
do {
print("ManagedAudioSession setting category for \(type)")
let nativeCategory = nativeCategoryForType(type, headphones: self.isHeadsetPluggedInValue, outputMode: outputMode)
print("ManagedAudioSession setting category for \(type) (native: \(nativeCategory))")
var options: AVAudioSession.CategoryOptions = []
switch type {
case .play:
break
case .playWithPossiblePortOverride:
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
options.insert(.allowBluetoothA2DP)
} else {
options.insert(.allowBluetooth)
if case .playAndRecord = nativeCategory {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
options.insert(.allowBluetoothA2DP)
} else {
options.insert(.allowBluetooth)
}
}
case .record, .voiceCall:
options.insert(.allowBluetooth)
}
print("ManagedAudioSession setting active true")
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
try AVAudioSession.sharedInstance().setCategory(nativeCategoryForType(type, headphones: self.isHeadsetPluggedInValue), mode: type == .voiceCall ? .voiceChat : .default, policy: .default, options: options)
try AVAudioSession.sharedInstance().setCategory(nativeCategory, mode: type == .voiceCall ? .voiceChat : .default, policy: .default, options: options)
} else {
AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: nativeCategoryForType(type, headphones: self.isHeadsetPluggedInValue))
AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: nativeCategory)
try AVAudioSession.sharedInstance().setMode(type == .voiceCall ? .voiceChat : .default)
}
} catch let error {
@ -684,43 +693,39 @@ public final class ManagedAudioSession {
print("ManagedAudioSession setup \(outputMode) for \(type)")
var resetToBuiltin = false
switch outputMode {
case .system:
resetToBuiltin = true
case let .custom(output):
switch output {
case .builtin:
resetToBuiltin = true
case .speaker:
if type == .voiceCall {
if let routes = AVAudioSession.sharedInstance().availableInputs {
for route in routes {
if route.portType == .builtInMic {
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
break
}
}
}
}
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
case .headphones:
break
case let .port(port):
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
case .system:
resetToBuiltin = true
case let .custom(output):
switch output {
case .builtin:
resetToBuiltin = true
case .speaker:
if type == .voiceCall {
if let routes = AVAudioSession.sharedInstance().availableInputs {
for route in routes {
if route.uid == port.uid {
if route.portType == .builtInMic {
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
break
}
}
}
}
case .speakerIfNoHeadphones:
if !self.isHeadsetPluggedInValue {
}
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
} else {
case .headphones:
break
case let .port(port):
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
}
if let routes = AVAudioSession.sharedInstance().availableInputs {
for route in routes {
if route.uid == port.uid {
let _ = try? AVAudioSession.sharedInstance().setPreferredInput(route)
break
}
}
}
}
case .speakerIfNoHeadphones:
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
}
if resetToBuiltin {
@ -778,9 +783,11 @@ public final class ManagedAudioSession {
private func updateOutputMode(_ outputMode: AudioSessionOutputMode) {
if let (type, currentOutputMode) = self.currentTypeAndOutputMode, currentOutputMode != outputMode {
self.currentTypeAndOutputMode = (type, outputMode)
//self.currentTypeAndOutputMode = (type, outputMode)
do {
try self.setupOutputMode(outputMode, type: type)
try self.setup(type: type, outputMode: outputMode, activateNow: true)
//try self.setupOutputMode(outputMode, type: type)
//try self.activate()
} catch let error {
print("ManagedAudioSession overrideOutputAudioPort error \(error)")
}

View File

@ -179,7 +179,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
mediaPlaceholderColor: UIColor(rgb: 0x1c1c1d),
scrollIndicatorColor: UIColor(white: 1.0, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(white: 1.0, alpha: 0.3),
inputClearButtonColor: UIColor(rgb: 0x8b9197)
inputClearButtonColor: UIColor(rgb: 0x8b9197),
itemBarChart: PresentationThemeItemBarChart(color1: accentColor, color2: UIColor(rgb: 0x929196), color3: UIColor(rgb: 0x333333))
)
let chatList = PresentationThemeChatList(
@ -219,8 +220,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
)
let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), mediaControlInnerBackgroundColor: UIColor(rgb: 0x262628), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff)), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))),
infoPrimaryTextColor: .white,
infoLinkTextColor: accentColor,
@ -247,7 +248,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
let inputPanelMediaRecordingControl = PresentationThemeChatInputPanelMediaRecordingControl(
buttonColor: accentColor,
micLevelColor: accentColor.withAlphaComponent(0.2),
activeIconColor: .white
activeIconColor: secondaryBadgeTextColor
)
let inputPanel = PresentationThemeChatInputPanel(

View File

@ -155,7 +155,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23),
scrollIndicatorColor: UIColor(white: 1.0, alpha: 0.3),
pageIndicatorInactiveColor: mainSecondaryTextColor.withAlphaComponent(0.4),
inputClearButtonColor: mainSecondaryColor
inputClearButtonColor: mainSecondaryColor,
itemBarChart: PresentationThemeItemBarChart(color1: accentColor, color2: mainSecondaryTextColor.withAlphaComponent(0.5), color3: accentColor.withMultiplied(hue: 1.038, saturation: 0.329, brightness: 0.33))
)
let chatList = PresentationThemeChatList(
@ -197,8 +198,8 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
let buttonStrokeColor = accentColor.withMultiplied(hue: 1.014, saturation: 0.56, brightness: 0.64).withAlphaComponent(0.15)
let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.5), mediaControlInnerBackgroundColor: mainBackgroundColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: accentColor, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.585, brightness: 0.23), polls: PresentationThemeChatBubblePolls(radioButton: accentColor.withMultiplied(hue: 0.995, saturation: 0.317, brightness: 0.51), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: mainSeparatorColor, bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleColor, highlightedFill: highlightedOutgoingBubbleColor, stroke: outgoingBubbleColor)), primaryTextColor: .white, secondaryTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xf5c038), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaControlInnerBackgroundColor: outgoingBubbleColor, pendingActivityColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileTitleColor: .white, fileDescriptionColor: mainSecondaryTextColor.withAlphaComponent(0.5), fileDurationColor: mainSecondaryTextColor.withAlphaComponent(0.5), mediaPlaceholderColor: accentColor.withMultiplied(hue: 1.019, saturation: 0.804, brightness: 0.51), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0), highlight: accentColor.withMultiplied(hue: 0.99, saturation: 0.56, brightness: 1.0).withAlphaComponent(0.12), separator: mainSeparatorColor, bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: additionalBackgroundColor.withAlphaComponent(0.5), withoutWallpaper: additionalBackgroundColor.withAlphaComponent(0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: buttonStrokeColor), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor.white.withAlphaComponent(0.2), textSelectionKnobColor: UIColor.white),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: mainBackgroundColor, highlightedFill: highlightedIncomingBubbleColor, stroke: mainBackgroundColor)),
infoPrimaryTextColor: UIColor(rgb: 0xffffff),
infoLinkTextColor: accentColor,

View File

@ -163,7 +163,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
mediaPlaceholderColor: UIColor(rgb: 0xe4e4e4),
scrollIndicatorColor: UIColor(white: 0.0, alpha: 0.3),
pageIndicatorInactiveColor: UIColor(rgb: 0xe3e3e7),
inputClearButtonColor: UIColor(rgb: 0xcccccc)
inputClearButtonColor: UIColor(rgb: 0xcccccc),
itemBarChart: PresentationThemeItemBarChart(color1: accentColor, color2: UIColor(rgb: 0xc8c7cc), color3: UIColor(rgb: 0xf2f1f7))
)
let chatList = PresentationThemeChatList(
@ -203,8 +204,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
)
let message = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE1FFC7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor(rgb: 0xBBDE9F), textSelectionKnobColor: UIColor(rgb: 0x3FC33B)),
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x007ee5), accentControlColor: UIColor(rgb: 0x007ee5), mediaActiveControlColor: UIColor(rgb: 0x007ee5), mediaInactiveControlColor: UIColor(rgb: 0xcacaca), mediaControlInnerBackgroundColor: UIColor(rgb: 0xffffff), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xe8ecf0), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: UIColor(rgb: 0x007ee5), highlight: UIColor(rgb: 0x007ee5).withAlphaComponent(0.08), separator: UIColor(rgb: 0xc8c7cc), bar: UIColor(rgb: 0x007ee5)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: accentColor.withAlphaComponent(0.2), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xe1ffc7), highlightedFill: UIColor(rgb: 0xc8ffa6), stroke: UIColor(rgb: 0x86a9c9, alpha: 0.5))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x008c09, alpha: 0.8), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: UIColor(rgb: 0x00a700), accentControlColor: UIColor(rgb: 0x3fc33b), mediaActiveControlColor: UIColor(rgb: 0x3fc33b), mediaInactiveControlColor: UIColor(rgb: 0x93d987), mediaControlInnerBackgroundColor: UIColor(rgb: 0xe1ffc7), pendingActivityColor: UIColor(rgb: 0x42b649), fileTitleColor: UIColor(rgb: 0x3faa3c), fileDescriptionColor: UIColor(rgb: 0x6fb26a), fileDurationColor: UIColor(rgb: 0x008c09, alpha: 0.8), mediaPlaceholderColor: UIColor(rgb: 0xd2f2b6), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x93d987), radioProgress: UIColor(rgb: 0x3fc33b), highlight: UIColor(rgb: 0x3fc33b).withAlphaComponent(0.08), separator: UIColor(rgb: 0x93d987), bar: UIColor(rgb: 0x3fc33b)), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0x596e89, alpha: 0.35)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: .clear), actionButtonsTextColor: PresentationThemeVariableColor(color: .white), textSelectionColor: UIColor(rgb: 0xBBDE9F), textSelectionKnobColor: UIColor(rgb: 0x3FC33B)),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xd9f4ff), stroke: UIColor(rgb: 0x86A9C9, alpha: 0.5))),
infoPrimaryTextColor: UIColor(rgb: 0x000000),
infoLinkTextColor: UIColor(rgb: 0x004bad),
@ -221,8 +222,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
)
let messageDay = PresentationThemeChatMessage(
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), mediaControlInnerBackgroundColor: UIColor(rgb: 0xffffff), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: accentColor.withAlphaComponent(0.3), textSelectionKnobColor: accentColor),
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffc738), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, mediaControlInnerBackgroundColor: outgoingBubbleFillColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor), textSelectionColor: outgoingSelectionBaseColor.withAlphaComponent(0.2), textSelectionKnobColor: outgoingSelectionBaseColor),
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
infoPrimaryTextColor: UIColor(rgb: 0x000000),
infoLinkTextColor: UIColor(rgb: 0x004bad),

View File

@ -26,16 +26,6 @@ public struct PresentationDateTimeFormat: Equatable {
}
}
public struct PresentationVolumeControlStatusBarIcons: Equatable {
public let offIcon: UIImage
public let halfIcon: UIImage
public let fullIcon: UIImage
public var images: (UIImage, UIImage, UIImage) {
return (self.offIcon, self.halfIcon, self.fullIcon)
}
}
public struct PresentationAppIcon: Equatable {
public let name: String
public let imageName: String
@ -61,8 +51,8 @@ public enum PresentationDateFormat {
public final class PresentationData: Equatable {
public let strings: PresentationStrings
public let theme: PresentationTheme
public let autoNightModeTriggered: Bool
public let chatWallpaper: TelegramWallpaper
public let volumeControlStatusBarIcons: PresentationVolumeControlStatusBarIcons
public let fontSize: PresentationFontSize
public let dateTimeFormat: PresentationDateTimeFormat
public let nameDisplayOrder: PresentationPersonNameOrder
@ -70,11 +60,11 @@ public final class PresentationData: Equatable {
public let disableAnimations: Bool
public let largeEmoji: Bool
public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, volumeControlStatusBarIcons: PresentationVolumeControlStatusBarIcons, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
public init(strings: PresentationStrings, theme: PresentationTheme, autoNightModeTriggered: Bool, chatWallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
self.strings = strings
self.theme = theme
self.autoNightModeTriggered = autoNightModeTriggered
self.chatWallpaper = chatWallpaper
self.volumeControlStatusBarIcons = volumeControlStatusBarIcons
self.fontSize = fontSize
self.dateTimeFormat = dateTimeFormat
self.nameDisplayOrder = nameDisplayOrder
@ -84,7 +74,7 @@ public final class PresentationData: Equatable {
}
public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool {
return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.chatWallpaper == rhs.chatWallpaper && lhs.volumeControlStatusBarIcons == rhs.volumeControlStatusBarIcons && lhs.fontSize == rhs.fontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.disableAnimations == rhs.disableAnimations && lhs.largeEmoji == rhs.largeEmoji
return lhs.strings === rhs.strings && lhs.theme === rhs.theme && lhs.autoNightModeTriggered == rhs.autoNightModeTriggered && lhs.chatWallpaper == rhs.chatWallpaper && lhs.fontSize == rhs.fontSize && lhs.dateTimeFormat == rhs.dateTimeFormat && lhs.disableAnimations == rhs.disableAnimations && lhs.largeEmoji == rhs.largeEmoji
}
}
@ -116,11 +106,6 @@ public func dictFromLocalization(_ value: Localization) -> [String: String] {
return dict
}
private func volumeControlStatusBarIcons() -> PresentationVolumeControlStatusBarIcons {
let bundle = getAppBundle()
return PresentationVolumeControlStatusBarIcons(offIcon: UIImage(named: "Components/Volume/VolumeOff", in: bundle, compatibleWith: nil)!, halfIcon: UIImage(named: "Components/Volume/VolumeHalf", in: bundle, compatibleWith: nil)!, fullIcon: UIImage(named: "Components/Volume/VolumeFull", in: bundle, compatibleWith: nil)!)
}
private func currentDateTimeFormat() -> PresentationDateTimeFormat {
let locale = Locale.current
let dateFormatter = DateFormatter()
@ -249,10 +234,13 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
var effectiveChatWallpaper: TelegramWallpaper = themeSettings.chatWallpaper
let parameters = AutomaticThemeSwitchParameters(settings: themeSettings.automaticThemeSwitchSetting)
let autoNightModeTriggered: Bool
if automaticThemeShouldSwitchNow(parameters, systemUserInterfaceStyle: systemUserInterfaceStyle) {
effectiveTheme = themeSettings.automaticThemeSwitchSetting.theme
autoNightModeTriggered = true
} else {
effectiveTheme = themeSettings.theme
autoNightModeTriggered = false
}
let effectiveAccentColor = themeSettings.themeSpecificAccentColors[effectiveTheme.index]?.color
@ -276,7 +264,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager, s
}
let nameDisplayOrder = contactSettings.nameDisplayOrder
let nameSortOrder = currentPersonNameSortOrder()
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
}
}
@ -499,11 +487,11 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
if inForeground {
return automaticThemeShouldSwitch(themeSettings.automaticThemeSwitchSetting, systemUserInterfaceStyle: systemUserInterfaceStyle)
|> distinctUntilChanged
|> map { shouldSwitch in
|> map { autoNightModeTriggered in
var effectiveTheme: PresentationThemeReference
var effectiveChatWallpaper: TelegramWallpaper = currentWallpaper
if shouldSwitch {
if autoNightModeTriggered {
let automaticTheme = themeSettings.automaticThemeSwitchSetting.theme
if let themeSpecificWallpaper = themeSettings.themeSpecificChatWallpapers[automaticTheme.index] {
effectiveChatWallpaper = themeSpecificWallpaper
@ -546,7 +534,7 @@ public func updatedPresentationData(accountManager: AccountManager, applicationI
let nameDisplayOrder = contactSettings.nameDisplayOrder
let nameSortOrder = currentPersonNameSortOrder()
return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji)
return PresentationData(strings: stringsValue, theme: themeValue, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji)
}
} else {
return .complete()
@ -562,5 +550,5 @@ public func defaultPresentationData() -> PresentationData {
let nameSortOrder = currentPersonNameSortOrder()
let themeSettings = PresentationThemeSettings.defaultSettings
return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, chatWallpaper: .builtin(WallpaperSettings()), volumeControlStatusBarIcons: volumeControlStatusBarIcons(), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji)
return PresentationData(strings: defaultPresentationStrings, theme: defaultPresentationTheme, autoNightModeTriggered: false, chatWallpaper: .builtin(WallpaperSettings()), fontSize: themeSettings.fontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, disableAnimations: themeSettings.disableAnimations, largeEmoji: themeSettings.largeEmoji)
}

View File

@ -289,6 +289,18 @@ public final class PresentationThemeItemDisclosureActions {
}
}
public final class PresentationThemeItemBarChart {
public let color1: UIColor
public let color2: UIColor
public let color3: UIColor
public init(color1: UIColor, color2: UIColor, color3: UIColor) {
self.color1 = color1
self.color2 = color2
self.color3 = color3
}
}
public final class PresentationThemeFillStrokeForeground {
public let fillColor: UIColor
public let strokeColor: UIColor
@ -346,8 +358,9 @@ public final class PresentationThemeList {
public let scrollIndicatorColor: UIColor
public let pageIndicatorInactiveColor: UIColor
public let inputClearButtonColor: UIColor
public let itemBarChart: PresentationThemeItemBarChart
public init(blocksBackgroundColor: UIColor, plainBackgroundColor: UIColor, itemPrimaryTextColor: UIColor, itemSecondaryTextColor: UIColor, itemDisabledTextColor: UIColor, itemAccentColor: UIColor, itemHighlightedColor: UIColor, itemDestructiveColor: UIColor, itemPlaceholderTextColor: UIColor, itemBlocksBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemBlocksSeparatorColor: UIColor, itemPlainSeparatorColor: UIColor, disclosureArrowColor: UIColor, sectionHeaderTextColor: UIColor, freeTextColor: UIColor, freeTextErrorColor: UIColor, freeTextSuccessColor: UIColor, freeMonoIconColor: UIColor, itemSwitchColors: PresentationThemeSwitch, itemDisclosureActions: PresentationThemeItemDisclosureActions, itemCheckColors: PresentationThemeFillStrokeForeground, controlSecondaryColor: UIColor, freeInputField: PresentationInputFieldTheme, mediaPlaceholderColor: UIColor, scrollIndicatorColor: UIColor, pageIndicatorInactiveColor: UIColor, inputClearButtonColor: UIColor) {
public init(blocksBackgroundColor: UIColor, plainBackgroundColor: UIColor, itemPrimaryTextColor: UIColor, itemSecondaryTextColor: UIColor, itemDisabledTextColor: UIColor, itemAccentColor: UIColor, itemHighlightedColor: UIColor, itemDestructiveColor: UIColor, itemPlaceholderTextColor: UIColor, itemBlocksBackgroundColor: UIColor, itemHighlightedBackgroundColor: UIColor, itemBlocksSeparatorColor: UIColor, itemPlainSeparatorColor: UIColor, disclosureArrowColor: UIColor, sectionHeaderTextColor: UIColor, freeTextColor: UIColor, freeTextErrorColor: UIColor, freeTextSuccessColor: UIColor, freeMonoIconColor: UIColor, itemSwitchColors: PresentationThemeSwitch, itemDisclosureActions: PresentationThemeItemDisclosureActions, itemCheckColors: PresentationThemeFillStrokeForeground, controlSecondaryColor: UIColor, freeInputField: PresentationInputFieldTheme, mediaPlaceholderColor: UIColor, scrollIndicatorColor: UIColor, pageIndicatorInactiveColor: UIColor, inputClearButtonColor: UIColor, itemBarChart: PresentationThemeItemBarChart) {
self.blocksBackgroundColor = blocksBackgroundColor
self.plainBackgroundColor = plainBackgroundColor
self.itemPrimaryTextColor = itemPrimaryTextColor
@ -376,6 +389,7 @@ public final class PresentationThemeList {
self.scrollIndicatorColor = scrollIndicatorColor
self.pageIndicatorInactiveColor = pageIndicatorInactiveColor
self.inputClearButtonColor = inputClearButtonColor
self.itemBarChart = itemBarChart
}
}
@ -551,6 +565,7 @@ public final class PresentationThemePartedColors {
public let accentControlColor: UIColor
public let mediaActiveControlColor: UIColor
public let mediaInactiveControlColor: UIColor
public let mediaControlInnerBackgroundColor: UIColor
public let pendingActivityColor: UIColor
public let fileTitleColor: UIColor
public let fileDescriptionColor: UIColor
@ -563,7 +578,7 @@ public final class PresentationThemePartedColors {
public let textSelectionColor: UIColor
public let textSelectionKnobColor: UIColor
public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) {
public init(bubble: PresentationThemeBubbleColor, primaryTextColor: UIColor, secondaryTextColor: UIColor, linkTextColor: UIColor, linkHighlightColor: UIColor, scamColor: UIColor, textHighlightColor: UIColor, accentTextColor: UIColor, accentControlColor: UIColor, mediaActiveControlColor: UIColor, mediaInactiveControlColor: UIColor, mediaControlInnerBackgroundColor: UIColor, pendingActivityColor: UIColor, fileTitleColor: UIColor, fileDescriptionColor: UIColor, fileDurationColor: UIColor, mediaPlaceholderColor: UIColor, polls: PresentationThemeChatBubblePolls, actionButtonsFillColor: PresentationThemeVariableColor, actionButtonsStrokeColor: PresentationThemeVariableColor, actionButtonsTextColor: PresentationThemeVariableColor, textSelectionColor: UIColor, textSelectionKnobColor: UIColor) {
self.bubble = bubble
self.primaryTextColor = primaryTextColor
self.secondaryTextColor = secondaryTextColor
@ -575,6 +590,7 @@ public final class PresentationThemePartedColors {
self.accentControlColor = accentControlColor
self.mediaActiveControlColor = mediaActiveControlColor
self.mediaInactiveControlColor = mediaInactiveControlColor
self.mediaControlInnerBackgroundColor = mediaControlInnerBackgroundColor
self.pendingActivityColor = pendingActivityColor
self.fileTitleColor = fileTitleColor
self.fileDescriptionColor = fileDescriptionColor

View File

@ -5,7 +5,12 @@ import TelegramCore
import SyncCore
import TelegramUIPreferences
private func decodeColor<Key>(_ values: KeyedDecodingContainer<Key>, _ key: Key) throws -> UIColor {
private func decodeColor<Key>(_ values: KeyedDecodingContainer<Key>, _ key: Key, decoder: Decoder? = nil, fallbackKey: String? = nil) throws -> UIColor {
if let decoder = decoder as? PresentationThemeDecoding, let fallbackKey = fallbackKey {
let key = (decoder.codingPath.map { $0.stringValue } + [key.stringValue]).joined(separator: ".")
decoder.fallbackKeys[key] = fallbackKey
}
let value = try values.decode(String.self, forKey: key)
if value.lowercased() == "clear" {
return UIColor.clear
@ -347,6 +352,7 @@ extension PresentationThemeRootNavigationBar: Codable {
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.init(buttonColor: try decodeColor(values, .button),
disabledButtonColor: try decodeColor(values, .disabledButton),
primaryTextColor: try decodeColor(values, .primaryText),
@ -358,10 +364,10 @@ extension PresentationThemeRootNavigationBar: Codable {
badgeBackgroundColor: try decodeColor(values, .badgeFill),
badgeStrokeColor: try decodeColor(values, .badgeStroke),
badgeTextColor: try decodeColor(values, .badgeText),
segmentedBackgroundColor: try decodeColor(values, .segmentedBg),
segmentedForegroundColor: try decodeColor(values, .segmentedFg),
segmentedTextColor: try decodeColor(values, .segmentedText),
segmentedDividerColor: try decodeColor(values, .segmentedDivider))
segmentedBackgroundColor: try decodeColor(values, .segmentedBg, decoder: decoder, fallbackKey: "root.searchBar.inputFill"),
segmentedForegroundColor: try decodeColor(values, .segmentedFg, decoder: decoder, fallbackKey: "root.navBar.background"),
segmentedTextColor: try decodeColor(values, .segmentedText, decoder: decoder, fallbackKey: "root.navBar.primaryText"),
segmentedDividerColor: try decodeColor(values, .segmentedDivider, decoder: decoder, fallbackKey: "root.list.freeInputField.stroke"))
}
public func encode(to encoder: Encoder) throws {
@ -603,6 +609,29 @@ extension PresentationThemeItemDisclosureActions: Codable {
}
}
extension PresentationThemeItemBarChart: Codable {
enum CodingKeys: String, CodingKey {
case color1
case color2
case color3
}
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.init(color1: try decodeColor(values, .color1),
color2: try decodeColor(values, .color2),
color3: try decodeColor(values, .color3))
}
public func encode(to encoder: Encoder) throws {
var values = encoder.container(keyedBy: CodingKeys.self)
try encodeColor(&values, self.color1, .color1)
try encodeColor(&values, self.color2, .color2)
try encodeColor(&values, self.color3, .color3)
}
}
extension PresentationThemeFillStrokeForeground: Codable {
enum CodingKeys: String, CodingKey {
case bg
@ -683,6 +712,7 @@ extension PresentationThemeList: Codable {
case scrollIndicator
case pageIndicatorInactive
case inputClearButton
case itemBarChart
}
public convenience init(from decoder: Decoder) throws {
@ -714,7 +744,8 @@ extension PresentationThemeList: Codable {
mediaPlaceholderColor: try decodeColor(values, .mediaPlaceholder),
scrollIndicatorColor: try decodeColor(values, .scrollIndicator),
pageIndicatorInactiveColor: try decodeColor(values, .pageIndicatorInactive),
inputClearButtonColor: try decodeColor(values, .inputClearButton))
inputClearButtonColor: try decodeColor(values, .inputClearButton),
itemBarChart: try values.decode(PresentationThemeItemBarChart.self, forKey: .itemBarChart))
}
public func encode(to encoder: Encoder) throws {
@ -747,6 +778,7 @@ extension PresentationThemeList: Codable {
try encodeColor(&values, self.scrollIndicatorColor, .scrollIndicator)
try encodeColor(&values, self.pageIndicatorInactiveColor, .pageIndicatorInactive)
try encodeColor(&values, self.inputClearButtonColor, .inputClearButton)
try values.encode(self.itemBarChart, forKey: .itemBarChart)
}
}
@ -982,6 +1014,7 @@ extension PresentationThemePartedColors: Codable {
case accentControl
case mediaActiveControl
case mediaInactiveControl
case mediaControlInnerBg
case pendingActivity
case fileTitle
case fileDescription
@ -997,6 +1030,7 @@ extension PresentationThemePartedColors: Codable {
public convenience init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".")
self.init(
bubble: try values.decode(PresentationThemeBubbleColor.self, forKey: .bubble),
primaryTextColor: try decodeColor(values, .primaryText),
@ -1009,6 +1043,7 @@ extension PresentationThemePartedColors: Codable {
accentControlColor: try decodeColor(values, .accentControl),
mediaActiveControlColor: try decodeColor(values, .mediaActiveControl),
mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl),
mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: codingPath + ".bubble.withWp.bg"),
pendingActivityColor: try decodeColor(values, .pendingActivity),
fileTitleColor: try decodeColor(values, .fileTitle),
fileDescriptionColor: try decodeColor(values, .fileDescription),
@ -1036,6 +1071,7 @@ extension PresentationThemePartedColors: Codable {
try encodeColor(&values, self.accentControlColor, .accentControl)
try encodeColor(&values, self.mediaActiveControlColor, .mediaActiveControl)
try encodeColor(&values, self.mediaInactiveControlColor, .mediaInactiveControl)
try encodeColor(&values, self.mediaControlInnerBackgroundColor, .mediaControlInnerBg)
try encodeColor(&values, self.pendingActivityColor, .pendingActivity)
try encodeColor(&values, self.fileTitleColor, .fileTitle)
try encodeColor(&values, self.fileDescriptionColor, .fileDescription)

View File

@ -113,6 +113,23 @@ fileprivate class PresentationThemeEncoding: Encoder {
return container
}
private func dictionaryForNodes(_ nodes: [Node]) -> [String: Any] {
var dictionary: [String: Any] = [:]
for node in nodes {
var value: Any?
switch node.value {
case let .string(string):
value = string
case let .subnode(subnodes):
value = dictionaryForNodes(subnodes)
}
if let key = node.key {
dictionary[key] = value
}
}
return dictionary
}
func entry(for codingKey: [String]) -> Any? {
var currentNode: Node = self.data.rootNode
for component in codingKey {
@ -123,8 +140,8 @@ fileprivate class PresentationThemeEncoding: Encoder {
if component == codingKey.last {
if case let .string(string) = node.value {
return string
} else {
return nil
} else if case let .subnode(nodes) = node.value {
return dictionaryForNodes(nodes)
}
} else {
currentNode = node
@ -404,6 +421,7 @@ class PresentationThemeDecoding: Decoder {
var referenceTheme: PresentationTheme?
var serviceBackgroundColor: UIColor?
var resolvedWallpaper: TelegramWallpaper?
var fallbackKeys: [String: String] = [:]
private var _referenceCoding: PresentationThemeEncoding?
fileprivate var referenceCoding: PresentationThemeEncoding? {
@ -510,14 +528,43 @@ fileprivate struct PresentationThemeKeyedDecodingContainer<K : CodingKey>: Keyed
return entry is NSNull
}
public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
private func storageEntry(forKey key: [String]) -> Any? {
if let container = self.decoder.storage.containers.first as? [String: Any] {
func entry(container: [String: Any], forKey key: [String]) -> Any? {
if let keyComponent = key.first, let value = container[keyComponent] {
if key.count == 1 {
return value
} else if let subContainer = value as? [String: Any] {
return entry(container: subContainer, forKey: Array(key.suffix(from: 1)))
}
}
return nil
}
return entry(container: container, forKey: key)
} else {
return nil
}
}
private func containerEntry(forKey key: Key) -> Any? {
var containerEntry: Any? = self.container[key.stringValue]
if containerEntry == nil {
containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue])
let initialKey = self.codingPath.map { $0.stringValue } + [key.stringValue]
let initialKeyString = initialKey.joined(separator: ".")
if let fallbackKeyString = self.decoder.fallbackKeys[initialKeyString] {
let fallbackKey = fallbackKeyString.components(separatedBy: ".")
containerEntry = self.storageEntry(forKey: fallbackKey)
}
if containerEntry == nil {
containerEntry = self.decoder.referenceCoding?.entry(for: initialKey)
}
}
guard let entry = containerEntry else {
return containerEntry
}
public func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
guard let entry = self.containerEntry(forKey: key) else {
throw PresentationThemeDecodingError.keyNotFound
}
@ -532,12 +579,7 @@ fileprivate struct PresentationThemeKeyedDecodingContainer<K : CodingKey>: Keyed
}
public func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
var containerEntry: Any? = self.container[key.stringValue]
if containerEntry == nil {
containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue])
}
guard let entry = containerEntry else {
guard let entry = self.containerEntry(forKey: key) else {
throw PresentationThemeDecodingError.keyNotFound
}
@ -552,12 +594,7 @@ fileprivate struct PresentationThemeKeyedDecodingContainer<K : CodingKey>: Keyed
}
public func decode(_ type: String.Type, forKey key: Key) throws -> String {
var containerEntry: Any? = self.container[key.stringValue]
if containerEntry == nil {
containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue])
}
guard let entry = containerEntry else {
guard let entry = self.containerEntry(forKey: key) else {
throw PresentationThemeDecodingError.keyNotFound
}
@ -572,12 +609,7 @@ fileprivate struct PresentationThemeKeyedDecodingContainer<K : CodingKey>: Keyed
}
public func decode<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
var containerEntry: Any? = self.container[key.stringValue]
if containerEntry == nil {
containerEntry = self.decoder.referenceCoding?.entry(for: self.codingPath.map { $0.stringValue } + [key.stringValue])
}
guard let entry = containerEntry else {
guard let entry = self.containerEntry(forKey: key) else {
throw PresentationThemeDecodingError.keyNotFound
}

View File

@ -31,6 +31,7 @@ public enum PresentationResourceKey: Int32 {
case navigationPlayerMaximizedRateActiveIcon
case navigationPlayerMaximizedRateInactiveIcon
case itemListDownArrow
case itemListDisclosureArrow
case itemListCheckIcon
case itemListSecondaryCheckIcon

View File

@ -21,6 +21,12 @@ public func generateItemListPlusIcon(_ color: UIColor) -> UIImage? {
}
public struct PresentationResourcesItemList {
public static func downArrowImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListDownArrow.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Search/DownButton"), color: theme.list.itemAccentColor)
})
}
public static func disclosureArrowImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: theme.list.disclosureArrowColor)

View File

@ -197,13 +197,13 @@ framework(
"//submodules/AppLock:AppLock",
"//submodules/NotificationsPresentationData:NotificationsPresentationData",
"//submodules/UrlWhitelist:UrlWhitelist",
"//submodules/AppIntents:AppIntents",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
"$SDKROOT/System/Library/Frameworks/CoreAudio.framework",
"$SDKROOT/System/Library/Frameworks/WebKit.framework",
"$SDKROOT/System/Library/Frameworks/Intents.framework",
],
weak_frameworks = [
"Vision",
@ -211,5 +211,6 @@ framework(
"CallKit",
"PassKit",
"Contacts",
"Intents",
],
)

View File

@ -1,9 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -1,22 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "vol_full@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "vol_full@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

View File

@ -1,22 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "vol_half@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "vol_half@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

View File

@ -1,22 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "vol_off@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "vol_off@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

View File

@ -33,6 +33,7 @@ import WalletCore
import OpenSSLEncryptionProvider
import AppLock
import PresentationDataUtils
import AppIntents
#if canImport(BackgroundTasks)
import BackgroundTasks
@ -179,6 +180,8 @@ final class SharedApplicationContext {
private let authContext = Promise<UnauthorizedApplicationContext?>()
private let authContextDisposable = MetaDisposable()
private let logoutDisposable = MetaDisposable()
private let openChatWhenReadyDisposable = MetaDisposable()
private let openUrlWhenReadyDisposable = MetaDisposable()
@ -1155,7 +1158,8 @@ final class SharedApplicationContext {
}
|> take(1)
|> timeout(4.0, queue: .mainQueue(), alternate: .complete())
|> deliverOnMainQueue).start(completed: { authContextValue.rootController.view.endEditing(true)
|> deliverOnMainQueue).start(completed: {
authContextValue.rootController.view.endEditing(true)
authContextValue.rootController.dismiss()
})
} else {
@ -1174,12 +1178,32 @@ final class SharedApplicationContext {
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
statusController.dismiss()
self.mainWindow.present(context.rootController, on: .root) }))
self.mainWindow.present(context.rootController, on: .root)
}))
} else {
authContextReadyDisposable.set(nil)
}
}))
self.logoutDisposable.set((self.sharedContextPromise.get()
|> take(1)
|> mapToSignal { sharedContext -> Signal<Set<PeerId>, NoError> in
return sharedContext.sharedContext.activeAccounts
|> map { _, accounts, _ -> Set<PeerId> in
return Set(accounts.map { $0.1.peerId })
}
|> reduceLeft(value: Set<PeerId>()) { current, updated, emit in
if !current.isEmpty {
emit(current.subtracting(current.intersection(updated)))
}
return updated
}
}).start(next: { loggedOutAccountPeerIds in
for peerId in loggedOutAccountPeerIds {
deleteAllSendMessageIntents(accountPeerId: peerId)
}
}))
self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context.get() |> flatMap { WatchCommunicationManagerContext(context: $0.context) }, allowBackgroundTimeExtension: { timeout in
let _ = (self.sharedContextPromise.get()
|> take(1)).start(next: { sharedContext in
@ -1542,6 +1566,50 @@ final class SharedApplicationContext {
let _ = (self.sharedContextPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
if var encryptedPayload = payload.dictionaryPayload["p"] as? String {
encryptedPayload = encryptedPayload.replacingOccurrences(of: "-", with: "+")
encryptedPayload = encryptedPayload.replacingOccurrences(of: "_", with: "/")
while encryptedPayload.count % 4 != 0 {
encryptedPayload.append("=")
}
if let data = Data(base64Encoded: encryptedPayload) {
let _ = (sharedApplicationContext.sharedContext.activeAccounts
|> take(1)
|> mapToSignal { activeAccounts -> Signal<[(Account, MasterNotificationKey)], NoError> in
return combineLatest(activeAccounts.accounts.map { account -> Signal<(Account, MasterNotificationKey), NoError> in
return masterNotificationsKey(account: account.1, ignoreDisabled: true)
|> map { key -> (Account, MasterNotificationKey) in
return (account.1, key)
}
})
}
|> deliverOnMainQueue).start(next: { accountsAndKeys in
var accountAndDecryptedPayload: (Account, Data)?
for (account, key) in accountsAndKeys {
if let decryptedData = decryptedNotificationPayload(key: key, data: data) {
accountAndDecryptedPayload = (account, decryptedData)
break
}
}
if let (account, decryptedData) = accountAndDecryptedPayload {
if let decryptedDict = (try? JSONSerialization.jsonObject(with: decryptedData, options: [])) as? [AnyHashable: Any] {
if var updateString = decryptedDict["updates"] as? String {
updateString = updateString.replacingOccurrences(of: "-", with: "+")
updateString = updateString.replacingOccurrences(of: "_", with: "/")
while updateString.count % 4 != 0 {
updateString.append("=")
}
if let updateData = Data(base64Encoded: updateString) {
account.stateManager.processIncomingCallUpdate(data: updateData, completion: { _ in
})
}
}
}
}
})
}
}
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0)
if case PKPushType.voIP = type {
@ -1811,7 +1879,7 @@ final class SharedApplicationContext {
|> take(1)
|> deliverOnMainQueue
|> mapToSignal { sharedContext -> Signal<Void, NoError> in
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0)
sharedContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: true)
return sharedContext.sharedContext.activeAccounts
|> mapToSignal { _, accounts, _ -> Signal<Account, NoError> in
for account in accounts {
@ -1915,6 +1983,10 @@ final class SharedApplicationContext {
var carPlayOptions = options
carPlayOptions.insert(.allowInCarPlay)
//if #available(iOS 13.2, *) {
// carPlayOptions.insert(.allowAnnouncement)
//}
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)

View File

@ -55,6 +55,7 @@ import LocalizedPeerData
import PhoneNumberFormat
import SettingsUI
import UrlWhitelist
import AppIntents
public enum ChatControllerPeekActions {
case standard
@ -1429,7 +1430,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (strongSelf.context.account.postbox.transaction { transaction -> [Message] in
return transaction.getMessageFailedGroup(id) ?? []
} |> deliverOnMainQueue).start(next: { messages in
guard let strongSelf = self, let message = messages.filter({ $0.id == id }).first else {
guard let strongSelf = self else {
return
}
var groups: [UInt32: [Message]] = [:]
var notGrouped: [Message] = []
for message in messages {
if let groupInfo = message.groupInfo {
if groups[groupInfo.stableId] == nil {
groups[groupInfo.stableId] = []
}
groups[groupInfo.stableId]?.append(message)
} else {
notGrouped.append(message)
}
}
let totalGroupCount = notGrouped.count + groups.count
var maybeSelectedGroup: [Message]?
for (_, group) in groups {
if group.contains(where: { $0.id == id}) {
maybeSelectedGroup = group
break
}
}
for message in notGrouped {
if message.id == id {
maybeSelectedGroup = [message]
}
}
guard let selectedGroup = maybeSelectedGroup, let topMessage = selectedGroup.first else {
return
}
@ -1438,12 +1470,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] _, f in
if let strongSelf = self {
let _ = resendMessages(account: strongSelf.context.account, messageIds: [id]).start()
let _ = resendMessages(account: strongSelf.context.account, messageIds: selectedGroup.map({ $0.id })).start()
}
f(.dismissWithoutContent)
})))
if messages.count != 1 {
actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetryAll(messages.count).0, icon: { theme in
if totalGroupCount != 1 {
actions.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_MessageDialogRetryAll(totalGroupCount).0, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor)
}, action: { [weak self] _, f in
if let strongSelf = self {
@ -1461,7 +1493,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
f(.dismissWithoutContent)
})))
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: [], recognizer: nil)
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: topMessage)), items: .single(actions), reactionItems: [], recognizer: nil)
strongSelf.currentContextController = controller
strongSelf.window?.presentInGlobalOverlay(controller)
})
@ -1702,7 +1734,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
}, displaySwipeToReplyHint: { [weak self] in
if let strongSelf = self {
if let strongSelf = self, let validLayout = strongSelf.validLayout, min(validLayout.size.width, validLayout.size.height) > 320.0 {
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .swipeToReply(title: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintTitle, text: strongSelf.presentationData.strings.Conversation_SwipeToReplyHintText), elevatedLayout: true, action: { _ in }), in: .window(.root))
}
}, requestMessageUpdate: { [weak self] id in
@ -2798,7 +2830,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
strongSelf.donateSendMessageIntent()
donateSendMessageIntent(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, peerIds: [peerId])
}
}
@ -5461,7 +5493,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if editMediaOptions != nil {
strongSelf.editMessageMediaWithLegacySignals(signals!)
} else {
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime)
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
}
if !inputText.string.isEmpty {
//strongSelf.clearInputText()
@ -5673,6 +5705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
legacyController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
legacyController.controllerLoaded = { [weak legacyController] in
legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
legacyController?.view.disablesInteractiveModalDismiss = true
}
let controller = generator(legacyController.context)
legacyController.bind(controller: controller)
@ -5994,7 +6027,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
self.donateSendMessageIntent()
donateSendMessageIntent(account: self.context.account, sharedContext: self.context.sharedContext, peerIds: [peerId])
} else {
let mode: ChatScheduleTimeControllerMode
if peerId == self.context.account.peerId {
@ -7772,23 +7805,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: peer, chatPeer: peer, action: .clearCacheSuggestion, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
var presented = false
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ClearCache_FreeSpace, color: .accent, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
if let strongSelf = self {
let controller = storageUsageController(context: strongSelf.context, isModal: true)
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
if let strongSelf = self, !presented {
presented = true
strongSelf.push(storageUsageController(context: strongSelf.context, isModal: true))
}
}))
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])])
self.chatDisplayNode.dismissInput()
self.presentInGlobalOverlay(actionSheet)
//self.present(actionSheet, in: .window(.root))
}
@available(iOSApplicationExtension 11.0, iOS 11.0, *)
@ -8067,44 +8099,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
private func donateSendMessageIntent() {
guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId else {
return
}
if #available(iOSApplicationExtension 13.2, iOS 13.2, *) {
let _ = (self.context.account.postbox.loadedPeerWithId(peerId)
|> mapToSignal { peer -> Signal<(Peer, UIImage?), NoError> in
let avatarImage = peerAvatarImage(account: self.context.account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)
return avatarImage
|> map { avatarImage in
return (peer, avatarImage)
}
}
|> deliverOnMainQueue).start(next: { [weak self] peer, avatarImage in
if let strongSelf = self, let peer = peer as? TelegramUser, peer.botInfo == nil && !peer.flags.contains(.isSupport) {
let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown)
var nameComponents = PersonNameComponents()
nameComponents.givenName = peer.firstName
nameComponents.familyName = peer.lastName
let displayTitle = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)")
let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peerId.id)", serviceName: nil, sender: nil)
if #available(iOS 12.0, *), let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) {
intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName)
}
let interaction = INInteraction(intent: intent, response: nil)
interaction.direction = .outgoing
interaction.groupIdentifier = "sendMessage_\(strongSelf.context.account.peerId.toInt64())"
interaction.donate { error in
if let error = error {
print(error.localizedDescription)
}
}
}
})
}
}
private func updateReminderActivity() {
if self.isReminderActivityEnabled && false {
if #available(iOS 9.0, *) {

View File

@ -11,13 +11,17 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
}
var entries: [ChatHistoryEntry] = []
var adminRanks: [PeerId: CachedChannelAdminRank] = [:]
var stickersEnabled = true
if case let .peer(peerId) = location, peerId.namespace == Namespaces.Peer.CloudChannel {
for additionalEntry in view.additionalData {
if case let .cacheEntry(id, data) = additionalEntry {
if id == cachedChannelAdminRanksEntryId(peerId: peerId), let data = data as? CachedChannelAdminRanks {
adminRanks = data.ranks
}
break
} else if case let .peer(_, peer) = additionalEntry, let channel = peer as? TelegramChannel {
if let defaultBannedRights = channel.defaultBannedRights, defaultBannedRights.flags.contains(.banSendStickers) {
stickersEnabled = false
}
}
}
}
@ -42,7 +46,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
var contentTypeHint: ChatMessageEntryContentType = .generic
if presentationData.largeEmoji, entry.message.media.isEmpty {
if entry.message.text.count == 1, let _ = associatedData.animatedEmojiStickers[entry.message.text.basicEmoji.0] {
if stickersEnabled && entry.message.text.count == 1, let _ = associatedData.animatedEmojiStickers[entry.message.text.basicEmoji.0] {
contentTypeHint = .animatedEmoji
} else if messageIsElligibleForLargeEmoji(entry.message) {
contentTypeHint = .largeEmoji

View File

@ -790,7 +790,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
}
self.presentationDataDisposable = (combineLatest(context.sharedContext.presentationData, appConfiguration)
self.presentationDataDisposable = (
combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData,
appConfiguration)
|> deliverOnMainQueue).start(next: { [weak self] presentationData, appConfiguration in
if let strongSelf = self {
let previousTheme = strongSelf.currentPresentationData.theme

View File

@ -96,6 +96,32 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
let emptySize = self.emptyNode.updateLayout(size)
transition.updateFrame(node: self.emptyNode, frame: CGRect(origin: CGPoint(x: floor(size.width - emptySize.width) / 2.0, y: topInset + floor(size.height - topInset - emptySize.height) / 2.0), size: emptySize))
self.updateMultiplexedNodeLayout(changedIsExpanded: changedIsExpanded, transition: transition)
}
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect)? {
if let multiplexedNode = self.multiplexedNode {
return multiplexedNode.fileAt(point: point.offsetBy(dx: -multiplexedNode.frame.minX, dy: -multiplexedNode.frame.minY))
} else {
return nil
}
}
override var isEmpty: Bool {
return self.multiplexedNode?.files.isEmpty ?? true
}
override func willEnterHierarchy() {
super.willEnterHierarchy()
self.initializeIfNeeded()
}
private func updateMultiplexedNodeLayout(changedIsExpanded: Bool, transition: ContainedViewLayoutTransition) {
guard let (size, topInset, bottomInset, isExpanded, isVisible, deviceMetrics) = self.validLayout else {
return
}
if let multiplexedNode = self.multiplexedNode {
let previousBounds = multiplexedNode.scrollNode.layer.bounds
multiplexedNode.topInset = topInset + 60.0
@ -122,24 +148,6 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
}
}
func fileAt(point: CGPoint) -> (FileMediaReference, CGRect)? {
if let multiplexedNode = self.multiplexedNode {
return multiplexedNode.fileAt(point: point.offsetBy(dx: -multiplexedNode.frame.minX, dy: -multiplexedNode.frame.minY))
} else {
return nil
}
}
override var isEmpty: Bool {
return self.multiplexedNode?.files.isEmpty ?? true
}
override func willEnterHierarchy() {
super.willEnterHierarchy()
self.initializeIfNeeded()
}
func initializeIfNeeded() {
if self.multiplexedNode == nil {
self.trendingPromise.set(paneGifSearchForQuery(account: account, query: "", updateActivity: nil))
@ -215,6 +223,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
fixListScrolling(multiplexedNode)
}
}
self.updateMultiplexedNodeLayout(changedIsExpanded: false, transition: .immediate)
}
}
}

View File

@ -757,7 +757,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
if self.iconNode != nil {
statusForegroundColor = presentationData.theme.chat.message.mediaOverlayControlColors.foregroundColor
} else {
statusForegroundColor = bubbleColorComponents(theme: presentationData.theme, incoming: incoming, wallpaper: !presentationData.wallpaper.isEmpty).fill
statusForegroundColor = incoming ? presentationData.theme.chat.message.incoming.mediaControlInnerBackgroundColor : presentationData.theme.chat.message.outgoing.mediaControlInnerBackgroundColor
}
switch resourceStatus.mediaStatus {
case var .fetchStatus(fetchStatus):

View File

@ -405,10 +405,15 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
}
if viewClassName == ChatMessageBubbleItemNode.self && self.presentationData.largeEmoji && self.message.media.isEmpty {
if self.message.text.count == 1, let _ = self.associatedData.animatedEmojiStickers[self.message.text.basicEmoji.0] {
viewClassName = ChatMessageAnimatedStickerItemNode.self
} else if messageIsElligibleForLargeEmoji(self.message) {
viewClassName = ChatMessageStickerItemNode.self
if case let .message(_, _, _, attributes) = self.content {
switch attributes.contentTypeHint {
case .largeEmoji:
viewClassName = ChatMessageStickerItemNode.self
case .animatedEmoji:
viewClassName = ChatMessageAnimatedStickerItemNode.self
default:
break
}
}
}

View File

@ -21,6 +21,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
private var actions: ChatAvailableMessageActions?
private var theme: PresentationTheme
private let peerMedia: Bool
private let canDeleteMessagesDisposable = MetaDisposable()
@ -50,8 +51,9 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
}
}
init(theme: PresentationTheme, strings: PresentationStrings) {
init(theme: PresentationTheme, strings: PresentationStrings, peerMedia: Bool = false) {
self.theme = theme
self.peerMedia = peerMedia
self.deleteButton = HighlightableButtonNode()
self.deleteButton.isEnabled = false
@ -143,24 +145,34 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
self.forwardButton.isEnabled = actions.options.contains(.forward)
self.shareButton.isEnabled = false
self.deleteButton.isEnabled = true
if self.peerMedia {
self.deleteButton.isEnabled = !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty
} else {
self.deleteButton.isEnabled = true
}
self.shareButton.isEnabled = !actions.options.intersection([.forward]).isEmpty
self.reportButton.isEnabled = !actions.options.intersection([.report]).isEmpty
self.deleteButton.isHidden = false
if self.peerMedia {
self.deleteButton.isHidden = !self.deleteButton.isEnabled
} else {
self.deleteButton.isHidden = false
}
self.reportButton.isHidden = !self.reportButton.isEnabled
} else {
self.deleteButton.isEnabled = false
self.deleteButton.isHidden = false
self.deleteButton.isHidden = self.peerMedia
self.reportButton.isEnabled = false
self.reportButton.isHidden = true
self.forwardButton.isEnabled = false
self.shareButton.isEnabled = false
}
if self.reportButton.isHidden {
if self.reportButton.isHidden || (self.peerMedia && self.deleteButton.isHidden && self.reportButton.isHidden) {
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel, case .broadcast = peer.info {
self.reportButton.isHidden = false
} else if self.peerMedia {
self.deleteButton.isHidden = false
}
}

View File

@ -61,7 +61,9 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
var files: [FileMediaReference] = [] {
didSet {
let startTime = CFAbsoluteTimeGetCurrent()
self.updateVisibleItems()
print("MultiplexedVideoNode files updateVisibleItems: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
}
}
private var displayItems: [VisibleVideoItem] = []
@ -219,7 +221,9 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
self.validSize = size
self.contextContainerNode.frame = CGRect(origin: CGPoint(), size: size)
self.scrollNode.frame = CGRect(origin: CGPoint(), size: size)
let startTime = CFAbsoluteTimeGetCurrent()
self.updateVisibleItems(transition: transition)
print("MultiplexedVideoNode layout updateVisibleItems: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms")
}
}
@ -465,7 +469,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
@objc func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
if case .ended = recognizer.state {
let point = recognizer.location(in: self.view)
if let (file, rect) = self.offsetFileAt(point: point) {
if let (file, rect) = self.fileAt(point: point) {
self.fileSelected?(file, self, rect)
}
}

View File

@ -224,8 +224,6 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
}
}
shouldHide = false
return (result, shouldHide)
}
@ -323,13 +321,13 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
let translation = recognizer.translation(in: self.view)
var nodeFrame = draggingNode.frame
nodeFrame.origin = self.draggingStartPosition.offsetBy(dx: translation.x, dy: translation.y)
// if nodeFrame.midX < 0.0 {
// draggingNode.updateMinimizedEdge(.left, adjusting: true)
// } else if nodeFrame.midX > validLayout.size.width {
// draggingNode.updateMinimizedEdge(.right, adjusting: true)
// } else {
if nodeFrame.midX < 0.0 {
draggingNode.updateMinimizedEdge(.left, adjusting: true)
} else if nodeFrame.midX > validLayout.size.width {
draggingNode.updateMinimizedEdge(.right, adjusting: true)
} else {
draggingNode.updateMinimizedEdge(nil, adjusting: true)
// }
}
draggingNode.frame = nodeFrame
}
case .ended, .cancelled:
@ -339,13 +337,13 @@ final class OverlayMediaControllerNode: ASDisplayNode, UIGestureRecognizerDelega
let (updatedLocation, shouldDismiss) = self.nodeLocationForPosition(layout: validLayout, position: CGPoint(x: previousFrame.midX, y: previousFrame.midY), velocity: recognizer.velocity(in: self.view), size: nodeSize, tempExtendedTopInset: draggingNode.tempExtendedTopInset)
// if shouldDismiss && draggingNode.isMinimizeable {
// draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
// self.videoNodes[index].isMinimized = true
// } else {
if shouldDismiss && draggingNode.isMinimizeable {
draggingNode.updateMinimizedEdge(updatedLocation.x.isZero ? .left : .right, adjusting: false)
self.videoNodes[index].isMinimized = true
} else {
draggingNode.updateMinimizedEdge(nil, adjusting: true)
self.videoNodes[index].isMinimized = false
// }
}
if let group = draggingNode.group {
self.locationByGroup[group] = updatedLocation

View File

@ -273,7 +273,7 @@ class PeerMediaCollectionControllerNode: ASDisplayNode {
self.addSubnode(selectionPanelBackgroundNode)
self.selectionPanelBackgroundNode = selectionPanelBackgroundNode
let selectionPanel = ChatMessageSelectionInputPanelNode(theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings)
let selectionPanel = ChatMessageSelectionInputPanelNode(theme: self.chatPresentationInterfaceState.theme, strings: self.chatPresentationInterfaceState.strings, peerMedia: true)
selectionPanel.context = self.context
selectionPanel.backgroundColor = self.presentationData.theme.chat.inputPanel.panelBackgroundColor
selectionPanel.interfaceInteraction = self.interfaceInteraction

View File

@ -164,11 +164,12 @@ public class ShareRootControllerImpl {
let internalContext: InternalContext
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
if let globalInternalContext = globalInternalContext {
internalContext = globalInternalContext
} else {
initializeAccountManagement()
let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata")
var initialPresentationDataAndSettings: InitialPresentationDataAndSettings?
let semaphore = DispatchSemaphore(value: 0)
let systemUserInterfaceStyle: WindowUserInterfaceStyle
@ -205,16 +206,28 @@ public class ShareRootControllerImpl {
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
return sharedContext.activeAccountsWithInfo
return combineLatest(sharedContext.activeAccountsWithInfo, accountManager.transaction { transaction -> Set<AccountRecordId> in
return Set(transaction.getRecords().map { record in
return record.id
})
})
|> castError(ShareAuthorizationError.self)
|> take(1)
|> mapToSignal { primary, accounts -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in
guard let primary = primary else {
|> mapToSignal { primaryAndAccounts, validAccountIds -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in
var (maybePrimary, accounts) = primaryAndAccounts
for i in (0 ..< accounts.count).reversed() {
if !validAccountIds.contains(accounts[i].account.id) {
accounts.remove(at: i)
}
}
guard let primary = maybePrimary, validAccountIds.contains(primary) else {
return .fail(.unauthorized)
}
guard let info = accounts.first(where: { $0.account.id == primary }) else {
return .fail(.unauthorized)
}
return .single((sharedContext, info.account, Array(accounts)))
}
}

View File

@ -24,7 +24,7 @@ final class StickerPanePeerSpecificSetupGridItem: GridItem {
self.dismiss = dismiss
self.fillsRowWithDynamicHeight = { width in
let makeDescriptionLayout = TextNode.asyncLayout(nil)
let params = ListViewItemLayoutParams(width: width, leftInset: 0.0, rightInset: 0.0)
let params = ListViewItemLayoutParams(width: width, leftInset: 0.0, rightInset: 0.0, availableHeight: 0.0)
let leftInset: CGFloat = 12.0
let rightInset: CGFloat = 16.0
let (descriptionLayout, _) = makeDescriptionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Stickers_GroupStickersHelp, font: statusFont, textColor: .black), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - leftInset - rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@ -127,7 +127,7 @@ class StickerPanePeerSpecificSetupGridItemNode: GridItemNode {
return
}
let params = ListViewItemLayoutParams(width: self.bounds.size.width, leftInset: 0.0, rightInset: 0.0)
let params = ListViewItemLayoutParams(width: self.bounds.size.width, leftInset: 0.0, rightInset: 0.0, availableHeight: self.bounds.size.height)
let makeInstallLayout = TextNode.asyncLayout(self.installTextNode)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)

View File

@ -191,7 +191,7 @@ class StickerPaneSearchGlobalItemNode: GridItemNode {
return
}
let params = ListViewItemLayoutParams(width: self.bounds.size.width, leftInset: 0.0, rightInset: 0.0)
let params = ListViewItemLayoutParams(width: self.bounds.size.width, leftInset: 0.0, rightInset: 0.0, availableHeight: self.bounds.height)
let makeInstallLayout = TextNode.asyncLayout(self.installTextNode)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)

View File

@ -51,7 +51,7 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: baseFont, NSAttributedString.Key.foregroundColor: baseColor])
var skipEntity = false
var underlineAllLinks = false
if linkColor.isEqual(baseColor) {
if linkColor.argb == baseColor.argb {
underlineAllLinks = true
}
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]

View File

@ -31,7 +31,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let effectView: UIView
private let animationBackgroundColor: UIColor
private var originalRemainingSeconds: Int
private var remainingSeconds: Int
private var timer: SwiftSignalKit.Timer?
@ -261,7 +261,12 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let firstLayout = self.validLayout == nil
self.validLayout = layout
let leftInset: CGFloat = 50.0
var leftInset: CGFloat = 50.0
if let animationNode = self.animationNode, let iconSize = animationNode.preferredSize() {
if iconSize.width > leftInset {
leftInset = iconSize.width - 8.0
}
}
let rightInset: CGFloat = 16.0
var contentHeight: CGFloat = 20.0

View File

@ -78,7 +78,7 @@ class WalletAmountItemNode: ListViewItemNode, UITextFieldDelegate, ItemListItemN
private let measureNode: TextNode
private var item: WalletAmountItem?
private var validLayout: (CGFloat, CGFloat, CGFloat)?
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat)?
var tag: ItemListItemTag? {
return self.item?.tag
@ -166,7 +166,7 @@ class WalletAmountItemNode: ListViewItemNode, UITextFieldDelegate, ItemListItemN
return
}
let makeInputFieldLayout = self.inputFieldAsyncLayout()
let (_, _, inputFieldApply) = makeInputFieldLayout(item, ListViewItemLayoutParams(width: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2))
let (_, _, inputFieldApply) = makeInputFieldLayout(item, ListViewItemLayoutParams(width: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, availableHeight: validLayout.3))
inputFieldApply()
}
@ -196,7 +196,7 @@ class WalletAmountItemNode: ListViewItemNode, UITextFieldDelegate, ItemListItemN
return (layout, { [weak self] in
if let strongSelf = self {
strongSelf.item = item
strongSelf.validLayout = (params.width, params.leftInset, params.rightInset)
strongSelf.validLayout = (params.width, params.leftInset, params.rightInset, params.availableHeight)
if let _ = updatedTheme {
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor

View File

@ -216,7 +216,7 @@ func walletCreateInvoiceScreen(context: WalletContext, address: String) -> ViewC
}
return state
}
ensureItemVisibleImpl?(tag, false)
ensureItemVisibleImpl?(tag, true)
}, dismissInput: {
dismissInputImpl?()
}, scrollToBottom: {

View File

@ -118,21 +118,16 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
}
func asyncLayout() -> (_ item: WalletInfoEmptyItem, _ params: ListViewItemLayoutParams) -> (ListViewItemNodeLayout, () -> Void) {
let sideInset: CGFloat = 32.0
let buttonSideInset: CGFloat = 48.0
let iconSpacing: CGFloat = 5.0
let titleSpacing: CGFloat = 19.0
let termsSpacing: CGFloat = 11.0
let buttonHeight: CGFloat = 50.0
let iconSize = CGSize(width: 140.0, height: 140.0)
self.animationNode.updateLayout(size: iconSize)
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeTextLayout = TextNode.asyncLayout(self.textNode)
let makeAddressLayout = TextNode.asyncLayout(self.addressNode)
return { [weak self] item, params in
var iconSpacing: CGFloat = 5.0
var titleSpacing: CGFloat = 19.0
var iconSize = CGSize(width: 140.0, height: 140.0)
let contentVerticalOrigin: CGFloat = 10.0
let sideInset: CGFloat = 16.0
var iconOffset = CGPoint()
@ -149,14 +144,25 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
addressString.insert("\n", at: addressString.index(addressString.startIndex, offsetBy: addressString.count / 2))
let (addressLayout, addressApply) = makeAddressLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: addressString, font: Font.monospace(16.0), textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - sideInset * 2.0, height: .greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.1, cutout: nil, insets: UIEdgeInsets()))
let contentVerticalOrigin: CGFloat = 32.0
let availableHeight = params.availableHeight - contentVerticalOrigin * 2.0
let rawContentHeight: CGFloat = iconSize.height + titleLayout.size.height + textLayout.size.height + addressLayout.size.height
let contentSpacing = iconSpacing + titleSpacing + titleSpacing
let contentSpacingFactor = max(0.2, min(1.0, (availableHeight - rawContentHeight) / contentSpacing))
if contentSpacingFactor < 0.25 {
iconSize = CGSize(width: 90.0, height: 90.0)
}
iconSpacing = floor(iconSpacing * contentSpacingFactor)
titleSpacing = floor(titleSpacing * contentSpacingFactor)
let iconFrame = CGRect(origin: CGPoint(x: floor((params.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
let titleFrame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.size.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleLayout.size)
let textFrame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.size.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textLayout.size)
let addressFrame = CGRect(origin: CGPoint(x: floor((params.width - addressLayout.size.width) / 2.0), y: textFrame.maxY + titleSpacing), size: addressLayout.size)
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: (item.loading ? iconFrame.maxY : addressFrame.maxY) + 32.0), insets: UIEdgeInsets())
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: (item.loading ? iconFrame.maxY : addressFrame.maxY) + contentVerticalOrigin), insets: UIEdgeInsets())
return (layout, {
guard let strongSelf = self else {
@ -179,6 +185,8 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
strongSelf.item = item
strongSelf.animationNode.updateLayout(size: iconSize)
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
let _ = titleApply()

View File

@ -1117,6 +1117,10 @@ private final class WalletApplicationSplashScreenNode: ASDisplayNode {
private let headerBackgroundNode: ASDisplayNode
private let headerCornerNode: ASImageNode
private var isDismissed = false
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
init(theme: WalletTheme) {
self.headerBackgroundNode = ASDisplayNode()
self.headerBackgroundNode.backgroundColor = .black
@ -1141,11 +1145,32 @@ private final class WalletApplicationSplashScreenNode: ASDisplayNode {
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
if self.isDismissed {
return
}
self.validLayout = (layout, navigationHeight)
let headerHeight = navigationHeight + 260.0
transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: -1.0, y: 0), size: CGSize(width: layout.size.width + 2.0, height: headerHeight)))
transition.updateFrame(node: self.headerCornerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: headerHeight), size: CGSize(width: layout.size.width, height: 10.0)))
}
func animateOut(completion: @escaping () -> Void) {
guard let (layout, navigationHeight) = self.validLayout else {
completion()
return
}
self.isDismissed = true
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
let headerHeight = navigationHeight + 260.0
transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: -1.0, y: -headerHeight - 10.0), size: CGSize(width: layout.size.width + 2.0, height: headerHeight)), completion: { _ in
completion()
})
transition.updateFrame(node: self.headerCornerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -10.0), size: CGSize(width: layout.size.width, height: 10.0)))
}
}
public final class WalletApplicationSplashScreen: ViewController {
@ -1174,4 +1199,9 @@ public final class WalletApplicationSplashScreen: ViewController {
(self.displayNode as! WalletApplicationSplashScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationHeight, transition: transition)
}
public func animateOut(completion: @escaping () -> Void) {
self.statusBar.statusBarStyle = .Black
(self.displayNode as! WalletApplicationSplashScreenNode).animateOut(completion: completion)
}
}

View File

@ -61,7 +61,7 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
case address(WalletTheme, String, String)
case addressInfo(WalletTheme, String)
case commentHeader(WalletTheme, String)
case comment(WalletTheme, String, String)
case comment(WalletTheme, String, String, Bool)
var section: ItemListSectionId {
switch self {
@ -131,8 +131,8 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
} else {
return false
}
case let .comment(lhsTheme, lhsPlaceholder, lhsText):
if case let .comment(rhsTheme, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText {
case let .comment(lhsTheme, lhsPlaceholder, lhsText, lhsSendEnabled):
if case let .comment(rhsTheme, rhsPlaceholder, rhsText, rhsSendEnabled) = rhs, lhsTheme === rhsTheme, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText, lhsSendEnabled == rhsSendEnabled {
return true
} else {
return false
@ -245,7 +245,7 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section)
case let .commentHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .comment(theme, placeholder, value):
case let .comment(theme, placeholder, value, sendEnabled):
return ItemListMultilineInputItem(theme: theme, text: value, placeholder: placeholder, maxLength: ItemListMultilineInputItemTextLimit(value: walletTextLimit, display: true, mode: .bytes), sectionId: self.section, style: .blocks, returnKeyType: .send, textUpdated: { text in
arguments.updateText(WalletSendScreenEntryTag.comment, text)
}, updatedFocus: { focus in
@ -253,7 +253,9 @@ private enum WalletSendScreenEntry: ItemListNodeEntry {
arguments.scrollToBottom()
}
}, tag: WalletSendScreenEntryTag.comment, action: {
arguments.proceed()
if sendEnabled {
arguments.proceed()
}
})
}
}
@ -265,7 +267,7 @@ private struct WalletSendScreenState: Equatable {
var comment: String
}
private func walletSendScreenEntries(presentationData: WalletPresentationData, balance: Int64?, state: WalletSendScreenState) -> [WalletSendScreenEntry] {
private func walletSendScreenEntries(presentationData: WalletPresentationData, balance: Int64?, state: WalletSendScreenState, sendEnabled: Bool) -> [WalletSendScreenEntry] {
if balance == nil {
return []
}
@ -281,7 +283,7 @@ private func walletSendScreenEntries(presentationData: WalletPresentationData, b
entries.append(.addressInfo(presentationData.theme, presentationData.strings.Wallet_Send_AddressInfo))
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_Receive_CommentHeader))
entries.append(.comment(presentationData.theme, presentationData.strings.Wallet_Receive_CommentInfo, state.comment))
entries.append(.comment(presentationData.theme, presentationData.strings.Wallet_Receive_CommentInfo, state.comment, sendEnabled))
return entries
}
@ -374,6 +376,10 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
let presentationData = context.presentationData
let state = stateValue.with { $0 }
let amount = amountValue(state.amount)
guard amount > 0 else {
return
}
let commentData = state.comment.data(using: .utf8)
let formattedAddress = String(state.address[state.address.startIndex..<state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)] + " \n " + state.address[state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)..<state.address.endIndex])
let destinationAddress = state.address
@ -571,7 +577,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Send_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Wallet_Navigation_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: walletState?.balance, state: state), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: emptyItem, animateChanges: false)
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: walletState?.balance, state: state, sendEnabled: sendEnabled), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: emptyItem, animateChanges: false)
return (controllerState, (listState, arguments))
}

View File

@ -187,267 +187,267 @@ public final class WalletStrings: Equatable {
private let _s: [Int: String]
private let _r: [Int: [(Int, NSRange)]]
private let _ps: [Int: String]
public var Wallet_Created_ExportErrorText: String { return self._s[0]! }
public var Wallet_Send_ConfirmationConfirm: String { return self._s[2]! }
public var Wallet_Month_GenJuly: String { return self._s[3]! }
public var Wallet_Month_GenDecember: String { return self._s[4]! }
public var Wallet_Month_ShortJanuary: String { return self._s[5]! }
public var Wallet_WordCheck_Title: String { return self._s[6]! }
public var Wallet_Month_ShortMarch: String { return self._s[7]! }
public var Wallet_Month_GenSeptember: String { return self._s[8]! }
public var Wallet_Info_Address: String { return self._s[9]! }
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[10]! }
public var Wallet_Intro_NotNow: String { return self._s[11]! }
public var Wallet_AccessDenied_Camera: String { return self._s[12]! }
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[13]! }
public var Wallet_Receive_ShareAddress: String { return self._s[14]! }
public var Wallet_Sent_ViewWallet: String { return self._s[15]! }
public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[16]! }
public var Wallet_Completed_Text: String { return self._s[17]! }
public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[18]!, self._r[18]!, [_0])
public var Wallet_Updated_JustNow: String { return self._s[0]! }
public var Wallet_WordCheck_IncorrectText: String { return self._s[1]! }
public var Wallet_Month_ShortNovember: String { return self._s[2]! }
public var Wallet_Configuration_BlockchainIdPlaceholder: String { return self._s[3]! }
public var Wallet_Info_Send: String { return self._s[4]! }
public var Wallet_TransactionInfo_SendGrams: String { return self._s[5]! }
public func Wallet_Info_TransactionBlockchainFee(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[6]!, self._r[6]!, [_0])
}
public var Wallet_Receive_AmountText: String { return self._s[19]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[20]! }
public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[21]!, self._r[21]!, [_0])
}
public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[22]! }
public var Wallet_SecureStorageReset_Title: String { return self._s[23]! }
public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[24]! }
public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[25]! }
public var Wallet_Month_GenApril: String { return self._s[26]! }
public var Wallet_AccessDenied_Settings: String { return self._s[27]! }
public var Wallet_Configuration_BlockchainIdPlaceholder: String { return self._s[28]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[29]! }
public var Wallet_Qr_Title: String { return self._s[30]! }
public var Wallet_Intro_ImportExisting: String { return self._s[31]! }
public var Wallet_Send_OwnAddressAlertText: String { return self._s[32]! }
public var Wallet_Month_GenAugust: String { return self._s[33]! }
public var Wallet_Month_ShortDecember: String { return self._s[34]! }
public var Wallet_Info_Receive: String { return self._s[35]! }
public var Wallet_Send_Send: String { return self._s[36]! }
public var Wallet_RestoreFailed_Text: String { return self._s[37]! }
public var Wallet_Navigation_Cancel: String { return self._s[38]! }
public var Wallet_CreateInvoice_Title: String { return self._s[39]! }
public var Wallet_Sent_Title: String { return self._s[40]! }
public var Wallet_WordCheck_Continue: String { return self._s[41]! }
public var Wallet_Send_SyncInProgress: String { return self._s[43]! }
public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[44]!, self._r[44]!, [_0])
}
public var Wallet_Month_GenMay: String { return self._s[45]! }
public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[46]! }
public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[47]! }
public var Wallet_Receive_AddressHeader: String { return self._s[48]! }
public var Wallet_Settings_BackupWallet: String { return self._s[50]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[51]! }
public var Wallet_Info_Send: String { return self._s[52]! }
public var Wallet_Intro_Title: String { return self._s[53]! }
public var Wallet_Receive_Title: String { return self._s[54]! }
public var Wallet_Configuration_SourceHeader: String { return self._s[55]! }
public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[56]! }
public var Wallet_Alert_OK: String { return self._s[57]! }
public var Wallet_Send_NetworkErrorText: String { return self._s[58]! }
public var Wallet_Receive_CommentInfo: String { return self._s[59]! }
public var Wallet_TransactionInfo_Title: String { return self._s[60]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[61]! }
public var Wallet_Completed_Title: String { return self._s[62]! }
public var Wallet_Info_YourBalance: String { return self._s[63]! }
public var Wallet_Configuration_Title: String { return self._s[64]! }
public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[65]!, self._r[65]!, [_1, _2, _3])
}
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[66]!, self._r[66]!, [_0])
}
public var Wallet_Month_ShortJune: String { return self._s[67]! }
public var Wallet_ContextMenuCopy: String { return self._s[68]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[69]! }
public var Wallet_Send_Title: String { return self._s[70]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[71]! }
public var Wallet_WordImport_IncorrectText: String { return self._s[72]! }
public var Wallet_Weekday_Yesterday: String { return self._s[73]! }
public var Wallet_Send_AddressInfo: String { return self._s[74]! }
public var Wallet_UnknownError: String { return self._s[75]! }
public var Wallet_Receive_CopyAddress: String { return self._s[76]! }
public var Wallet_Month_ShortFebruary: String { return self._s[77]! }
public var Wallet_Intro_CreateWallet: String { return self._s[78]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[80]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[81]! }
public var Wallet_Info_TransactionFrom: String { return self._s[82]! }
public var Wallet_Month_ShortNovember: String { return self._s[83]! }
public var Wallet_Month_ShortSeptember: String { return self._s[84]! }
public var Wallet_Sent_Title: String { return self._s[7]! }
public var Wallet_Receive_ShareUrlInfo: String { return self._s[8]! }
public var Wallet_RestoreFailed_Title: String { return self._s[9]! }
public var Wallet_TransactionInfo_CopyAddress: String { return self._s[11]! }
public var Wallet_Settings_BackupWallet: String { return self._s[12]! }
public var Wallet_Send_NetworkErrorTitle: String { return self._s[13]! }
public var Wallet_Month_ShortJune: String { return self._s[14]! }
public var Wallet_TransactionInfo_StorageFeeInfo: String { return self._s[15]! }
public var Wallet_Created_Title: String { return self._s[16]! }
public func Wallet_Configuration_ApplyErrorTextURLUnreachable(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[85]!, self._r[85]!, [_0])
return formatWithArgumentRanges(self._s[17]!, self._r[17]!, [_0])
}
public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
public var Wallet_Send_SyncInProgress: String { return self._s[18]! }
public var Wallet_Info_YourBalance: String { return self._s[19]! }
public var Wallet_Configuration_ApplyErrorTextURLInvalidData: String { return self._s[20]! }
public var Wallet_TransactionInfo_CommentHeader: String { return self._s[21]! }
public var Wallet_TransactionInfo_OtherFeeHeader: String { return self._s[22]! }
public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[23]!, self._r[23]!, [_1, _2, _3])
}
public var Wallet_Settings_ConfigurationInfo: String { return self._s[24]! }
public var Wallet_WordImport_IncorrectText: String { return self._s[25]! }
public var Wallet_Month_GenJanuary: String { return self._s[26]! }
public var Wallet_Send_OwnAddressAlertTitle: String { return self._s[27]! }
public var Wallet_Receive_ShareAddress: String { return self._s[28]! }
public var Wallet_WordImport_Title: String { return self._s[29]! }
public var Wallet_TransactionInfo_Title: String { return self._s[30]! }
public var Wallet_Words_NotDoneText: String { return self._s[32]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[33]! }
public var Wallet_WordImport_Text: String { return self._s[34]! }
public var Wallet_RestoreFailed_Text: String { return self._s[36]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[37]! }
public var Wallet_Navigation_Back: String { return self._s[38]! }
public var Wallet_Intro_Terms: String { return self._s[39]! }
public func Wallet_Send_Balance(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[40]!, self._r[40]!, [_0])
}
public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[41]!, self._r[41]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[42]! }
public func Wallet_Info_TransactionDateHeaderYear(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[43]!, self._r[43]!, [_1, _2, _3])
}
public var Wallet_Send_NetworkErrorText: String { return self._s[44]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[45]! }
public var Wallet_Intro_ImportExisting: String { return self._s[46]! }
public var Wallet_Receive_CommentInfo: String { return self._s[47]! }
public var Wallet_WordCheck_Continue: String { return self._s[48]! }
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[49]! }
public var Wallet_Completed_Text: String { return self._s[50]! }
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[52]! }
public var Wallet_Configuration_SourceHeader: String { return self._s[53]! }
public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[54]! }
public var Wallet_Receive_Title: String { return self._s[55]! }
public var Wallet_Info_WalletCreated: String { return self._s[56]! }
public var Wallet_Navigation_Cancel: String { return self._s[57]! }
public var Wallet_CreateInvoice_Title: String { return self._s[58]! }
public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[59]!, self._r[59]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[60]! }
public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[61]!, self._r[61]!, [_1, _2, _3])
}
public var Wallet_Month_GenAugust: String { return self._s[62]! }
public var Wallet_Info_UnknownTransaction: String { return self._s[63]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[64]! }
public var Wallet_Month_GenSeptember: String { return self._s[65]! }
public var Wallet_Month_GenJuly: String { return self._s[66]! }
public var Wallet_Receive_AddressHeader: String { return self._s[67]! }
public var Wallet_Send_AmountText: String { return self._s[68]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[69]! }
public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[70]!, self._r[70]!, [_1, _2, _3])
}
public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[71]!, self._r[71]!, [_0])
}
public var Wallet_Configuration_Title: String { return self._s[73]! }
public var Wallet_Configuration_BlockchainIdHeader: String { return self._s[74]! }
public var Wallet_Words_Title: String { return self._s[75]! }
public var Wallet_Month_ShortMay: String { return self._s[76]! }
public var Wallet_WordCheck_Title: String { return self._s[77]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[78]! }
public var Wallet_Configuration_SourceURL: String { return self._s[79]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[80]! }
public var Wallet_Receive_CreateInvoiceInfo: String { return self._s[81]! }
public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[82]!, self._r[82]!, [_1, _2, _3])
}
public var Wallet_Info_Address: String { return self._s[83]! }
public var Wallet_Intro_CreateWallet: String { return self._s[84]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[85]! }
public func Wallet_SecureStorageReset_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[86]!, self._r[86]!, [_0])
}
public var Wallet_Send_AmountText: String { return self._s[87]! }
public var Wallet_Info_TransactionTo: String { return self._s[88]! }
public var Wallet_Words_Done: String { return self._s[89]! }
public var Wallet_Created_Text: String { return self._s[90]! }
public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[91]! }
public var Wallet_Month_ShortJuly: String { return self._s[92]! }
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[93]!, self._r[93]!, [_0])
public var Wallet_Send_SendAnyway: String { return self._s[87]! }
public var Wallet_UnknownError: String { return self._s[88]! }
public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[89]! }
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[90]! }
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[92]! }
public var Wallet_Configuration_SourceInfo: String { return self._s[93]! }
public var Wallet_Words_NotDoneOk: String { return self._s[94]! }
public var Wallet_Intro_Title: String { return self._s[95]! }
public var Wallet_Info_Receive: String { return self._s[96]! }
public var Wallet_Completed_ViewWallet: String { return self._s[97]! }
public var Wallet_Month_ShortJuly: String { return self._s[98]! }
public var Wallet_Month_ShortApril: String { return self._s[99]! }
public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[100]!, self._r[100]!, [_1, _2])
}
public var Wallet_Created_Title: String { return self._s[94]! }
public func Wallet_Time_PreciseDate_m9(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[95]!, self._r[95]!, [_1, _2, _3])
}
public var Wallet_Info_RefreshErrorTitle: String { return self._s[96]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[97]! }
public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[98]! }
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[99]! }
public var Wallet_SecureStorageChanged_ImportWallet: String { return self._s[100]! }
public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[101]! }
public func Wallet_Time_PreciseDate_m8(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
public var Wallet_Receive_ShareInvoiceUrl: String { return self._s[101]! }
public func Wallet_Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[102]!, self._r[102]!, [_1, _2, _3])
}
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[103]! }
public var Wallet_Send_UninitializedText: String { return self._s[104]! }
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[105]! }
public var Wallet_Send_ErrorInvalidAddress: String { return self._s[106]! }
public var Wallet_Words_NotDoneTitle: String { return self._s[107]! }
public var Wallet_Navigation_Back: String { return self._s[108]! }
public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[109]!, self._r[109]!, [_1, _2, _3])
public func Wallet_Sent_Text(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[105]!, self._r[105]!, [_0])
}
public var Wallet_Info_UnknownTransaction: String { return self._s[110]! }
public var Wallet_RestoreFailed_Title: String { return self._s[111]! }
public func Wallet_Updated_TodayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[112]!, self._r[112]!, [_0])
}
public var Wallet_TransactionInfo_CopyAddress: String { return self._s[113]! }
public var Wallet_Navigation_Done: String { return self._s[114]! }
public var Wallet_Send_UninitializedTitle: String { return self._s[115]! }
public var Wallet_Send_AddressHeader: String { return self._s[117]! }
public func Wallet_WordCheck_Text(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[118]!, self._r[118]!, [_1, _2, _3])
}
public var Wallet_Alert_Cancel: String { return self._s[119]! }
public var Wallet_Send_NetworkErrorTitle: String { return self._s[120]! }
public var Wallet_Configuration_SourceInfo: String { return self._s[121]! }
public var Wallet_Month_ShortAugust: String { return self._s[122]! }
public var Wallet_Words_NotDoneResponse: String { return self._s[123]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[124]! }
public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[125]!, self._r[125]!, [_1, _2, _3])
}
public var Wallet_Words_Text: String { return self._s[126]! }
public var Wallet_Month_GenNovember: String { return self._s[106]! }
public func Wallet_Time_PreciseDate_m5(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[127]!, self._r[127]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[107]!, self._r[107]!, [_1, _2, _3])
}
public var Wallet_Month_ShortOctober: String { return self._s[128]! }
public var Wallet_Created_Proceed: String { return self._s[129]! }
public var Wallet_Month_GenApril: String { return self._s[108]! }
public var Wallet_Month_ShortMarch: String { return self._s[109]! }
public var Wallet_Month_GenFebruary: String { return self._s[110]! }
public var Wallet_Qr_ScanCode: String { return self._s[111]! }
public var Wallet_Receive_AddressCopied: String { return self._s[112]! }
public var Wallet_Send_UninitializedTitle: String { return self._s[113]! }
public var Wallet_AccessDenied_Title: String { return self._s[114]! }
public var Wallet_AccessDenied_Settings: String { return self._s[115]! }
public var Wallet_Send_Send: String { return self._s[116]! }
public var Wallet_Info_RefreshErrorTitle: String { return self._s[117]! }
public var Wallet_Month_GenJune: String { return self._s[118]! }
public var Wallet_Send_AddressHeader: String { return self._s[119]! }
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[120]! }
public var Wallet_Send_Confirmation: String { return self._s[121]! }
public var Wallet_Completed_Title: String { return self._s[122]! }
public var Wallet_Alert_OK: String { return self._s[123]! }
public var Wallet_Settings_DeleteWallet: String { return self._s[124]! }
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[125]! }
public var Wallet_Month_ShortSeptember: String { return self._s[126]! }
public var Wallet_Info_TransactionTo: String { return self._s[127]! }
public var Wallet_Send_ConfirmationConfirm: String { return self._s[128]! }
public var Wallet_TransactionInfo_OtherFeeInfo: String { return self._s[129]! }
public var Wallet_Receive_AmountText: String { return self._s[130]! }
public var Wallet_Receive_CopyAddress: String { return self._s[131]! }
public var Wallet_Intro_Text: String { return self._s[133]! }
public var Wallet_Configuration_Apply: String { return self._s[134]! }
public func Wallet_SecureStorageChanged_BiometryText(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[130]!, self._r[130]!, [_0])
return formatWithArgumentRanges(self._s[135]!, self._r[135]!, [_0])
}
public var Wallet_Month_ShortApril: String { return self._s[131]! }
public var Wallet_Navigation_Close: String { return self._s[132]! }
public var Wallet_WordCheck_IncorrectHeader: String { return self._s[133]! }
public var Wallet_SecureStorageReset_BiometryTouchId: String { return self._s[134]! }
public func Wallet_Time_PreciseDate_m4(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[135]!, self._r[135]!, [_1, _2, _3])
}
public var Wallet_Send_AddressText: String { return self._s[137]! }
public func Wallet_Time_PreciseDate_m3(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[139]!, self._r[139]!, [_1, _2, _3])
}
public var Wallet_Month_ShortMay: String { return self._s[140]! }
public var Wallet_Intro_CreateErrorText: String { return self._s[142]! }
public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[143]! }
public var Wallet_Intro_Text: String { return self._s[144]! }
public var Wallet_Month_GenJune: String { return self._s[145]! }
public var Wallet_Receive_ShareUrlInfo: String { return self._s[146]! }
public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[147]!, self._r[147]!, [_1, _2, _3])
}
public var Wallet_AccessDenied_Title: String { return self._s[149]! }
public var Wallet_Send_SendAnyway: String { return self._s[150]! }
public var Wallet_Configuration_SourceJSON: String { return self._s[151]! }
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[152]! }
public func Wallet_Time_PreciseDate_m1(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[153]!, self._r[153]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[136]!, self._r[136]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_SendGrams: String { return self._s[154]! }
public var Wallet_Words_NotDoneText: String { return self._s[155]! }
public var Wallet_VoiceOver_Editing_ClearText: String { return self._s[156]! }
public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[158]! }
public var Wallet_Qr_ScanCode: String { return self._s[159]! }
public var Wallet_WordImport_Title: String { return self._s[160]! }
public func Wallet_Send_ConfirmationText(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
public var Wallet_RestoreFailed_CreateWallet: String { return self._s[137]! }
public var Wallet_Weekday_Yesterday: String { return self._s[138]! }
public var Wallet_Receive_AmountHeader: String { return self._s[139]! }
public var Wallet_TransactionInfo_OtherFeeInfoUrl: String { return self._s[140]! }
public var Wallet_Month_ShortFebruary: String { return self._s[141]! }
public var Wallet_Configuration_SourceJSON: String { return self._s[142]! }
public var Wallet_Alert_Cancel: String { return self._s[143]! }
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[144]! }
public var Wallet_Configuration_ApplyErrorTextJSONInvalidData: String { return self._s[145]! }
public var Wallet_Info_TransactionFrom: String { return self._s[146]! }
public var Wallet_Send_ErrorDecryptionFailed: String { return self._s[147]! }
public var Wallet_Send_OwnAddressAlertText: String { return self._s[148]! }
public var Wallet_Words_NotDoneTitle: String { return self._s[149]! }
public var Wallet_Month_ShortOctober: String { return self._s[150]! }
public var Wallet_Month_GenMay: String { return self._s[151]! }
public var Wallet_Intro_CreateErrorTitle: String { return self._s[152]! }
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[153]! }
public var Wallet_Month_ShortJanuary: String { return self._s[154]! }
public var Wallet_Month_GenMarch: String { return self._s[155]! }
public var Wallet_AccessDenied_Camera: String { return self._s[156]! }
public var Wallet_Sending_Text: String { return self._s[157]! }
public var Wallet_Month_GenOctober: String { return self._s[158]! }
public var Wallet_Receive_CopyInvoiceUrl: String { return self._s[159]! }
public var Wallet_ContextMenuCopy: String { return self._s[160]! }
public func Wallet_Time_PreciseDate_m6(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[161]!, self._r[161]!, [_1, _2, _3])
}
public var Wallet_Intro_Terms: String { return self._s[162]! }
public var Wallet_SecureStorageChanged_CreateWallet: String { return self._s[163]! }
public var Wallet_Receive_CreateInvoice: String { return self._s[164]! }
public var Wallet_Send_Confirmation: String { return self._s[165]! }
public var Wallet_Month_GenNovember: String { return self._s[166]! }
public func Wallet_Info_TransactionDateHeader(_ _1: String, _ _2: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[167]!, self._r[167]!, [_1, _2])
public var Wallet_Info_Updating: String { return self._s[163]! }
public var Wallet_Created_ExportErrorTitle: String { return self._s[164]! }
public var Wallet_SecureStorageNotAvailable_Title: String { return self._s[165]! }
public var Wallet_Sending_Title: String { return self._s[166]! }
public var Wallet_Navigation_Done: String { return self._s[167]! }
public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[168]! }
public var Wallet_Configuration_BlockchainNameChangedTitle: String { return self._s[169]! }
public var Wallet_Settings_Title: String { return self._s[170]! }
public func Wallet_Receive_ShareInvoiceUrlInfo(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[171]!, self._r[171]!, [_0])
}
public func Wallet_Time_PreciseDate_m12(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[168]!, self._r[168]!, [_1, _2, _3])
public var Wallet_Info_RefreshErrorNetworkText: String { return self._s[172]! }
public var Wallet_Weekday_Today: String { return self._s[174]! }
public var Wallet_Month_ShortDecember: String { return self._s[175]! }
public var Wallet_Words_Text: String { return self._s[176]! }
public var Wallet_Configuration_BlockchainNameChangedProceed: String { return self._s[177]! }
public var Wallet_WordCheck_ViewWords: String { return self._s[178]! }
public var Wallet_Send_AddressInfo: String { return self._s[179]! }
public func Wallet_Updated_AtDate(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[180]!, self._r[180]!, [_0])
}
public var Wallet_WordCheck_IncorrectText: String { return self._s[170]! }
public var Wallet_Completed_ViewWallet: String { return self._s[171]! }
public var Wallet_WordImport_Text: String { return self._s[172]! }
public var Wallet_Words_NotDoneOk: String { return self._s[173]! }
public var Wallet_SecureStorageChanged_PasscodeText: String { return self._s[174]! }
public var Wallet_TransactionInfo_SenderHeader: String { return self._s[175]! }
public var Wallet_Info_WalletCreated: String { return self._s[176]! }
public var Wallet_Configuration_BlockchainIdInfo: String { return self._s[177]! }
public var Wallet_Sending_Text: String { return self._s[178]! }
public var Wallet_Words_Title: String { return self._s[179]! }
public var Wallet_Receive_CommentHeader: String { return self._s[180]! }
public var Wallet_Receive_InvoiceUrlCopied: String { return self._s[181]! }
public var Wallet_Intro_CreateErrorTitle: String { return self._s[182]! }
public var Wallet_Intro_NotNow: String { return self._s[181]! }
public var Wallet_Send_OwnAddressAlertProceed: String { return self._s[182]! }
public var Wallet_Navigation_Close: String { return self._s[183]! }
public var Wallet_Month_GenDecember: String { return self._s[185]! }
public var Wallet_Send_ErrorNotEnoughFundsTitle: String { return self._s[186]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[187]! }
public var Wallet_Send_AddressText: String { return self._s[188]! }
public var Wallet_Receive_AmountInfo: String { return self._s[189]! }
public func Wallet_Time_PreciseDate_m2(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[190]!, self._r[190]!, [_1, _2, _3])
}
public var Wallet_Month_ShortAugust: String { return self._s[191]! }
public var Wallet_Qr_Title: String { return self._s[192]! }
public var Wallet_Settings_Configuration: String { return self._s[193]! }
public var Wallet_WordCheck_TryAgain: String { return self._s[194]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[195]! }
public var Wallet_Receive_InvoiceUrlHeader: String { return self._s[196]! }
public var Wallet_Configuration_ApplyErrorTitle: String { return self._s[197]! }
public var Wallet_Send_TransactionInProgress: String { return self._s[198]! }
public var Wallet_Created_Text: String { return self._s[199]! }
public var Wallet_Created_Proceed: String { return self._s[200]! }
public var Wallet_Words_Done: String { return self._s[201]! }
public var Wallet_WordImport_Continue: String { return self._s[202]! }
public var Wallet_TransactionInfo_StorageFeeHeader: String { return self._s[203]! }
public var Wallet_WordImport_CanNotRemember: String { return self._s[204]! }
public func Wallet_Time_PreciseDate_m11(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[183]!, self._r[183]!, [_1, _2, _3])
return formatWithArgumentRanges(self._s[205]!, self._r[205]!, [_1, _2, _3])
}
public var Wallet_TransactionInfo_StorageFeeInfoUrl: String { return self._s[184]! }
public var Wallet_Configuration_Apply: String { return self._s[185]! }
public var Wallet_WordImport_IncorrectTitle: String { return self._s[186]! }
public var Wallet_Settings_Configuration: String { return self._s[187]! }
public var Wallet_SecureStorageReset_BiometryFaceId: String { return self._s[189]! }
public var Wallet_Month_GenJanuary: String { return self._s[190]! }
public var Wallet_Settings_DeleteWallet: String { return self._s[191]! }
public var Wallet_Month_GenOctober: String { return self._s[192]! }
public var Wallet_Send_ErrorNotEnoughFundsText: String { return self._s[193]! }
public var Wallet_Receive_AddressCopied: String { return self._s[194]! }
public var Wallet_Configuration_ApplyErrorTextURLInvalid: String { return self._s[195]! }
public func Wallet_Time_PreciseDate_m10(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[196]!, self._r[196]!, [_1, _2, _3])
public func Wallet_Send_ConfirmationText(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[206]!, self._r[206]!, [_1, _2, _3])
}
public var Wallet_Receive_AmountInfo: String { return self._s[197]! }
public var Wallet_Info_TransactionPendingHeader: String { return self._s[198]! }
public var Wallet_SecureStorageReset_PasscodeText: String { return self._s[199]! }
public var Wallet_Updated_JustNow: String { return self._s[200]! }
public var Wallet_Info_RefreshErrorText: String { return self._s[201]! }
public var Wallet_SecureStorageNotAvailable_Text: String { return self._s[202]! }
public var Wallet_Settings_DeleteWalletInfo: String { return self._s[203]! }
public var Wallet_RestoreFailed_EnterWords: String { return self._s[204]! }
public var Wallet_TransactionInfo_RecipientHeader: String { return self._s[205]! }
public var Wallet_Send_TransactionInProgress: String { return self._s[206]! }
public var Wallet_Month_GenMarch: String { return self._s[207]! }
public var Wallet_Receive_AmountHeader: String { return self._s[208]! }
public var Wallet_TransactionInfo_NoAddress: String { return self._s[209]! }
public var Wallet_Weekday_Today: String { return self._s[210]! }
public var Wallet_Configuration_SourceURL: String { return self._s[211]! }
public var Wallet_TransactionInfo_AddressCopied: String { return self._s[212]! }
public var Wallet_WordImport_Continue: String { return self._s[213]! }
public var Wallet_Month_GenFebruary: String { return self._s[214]! }
public var Wallet_Settings_Title: String { return self._s[215]! }
public var Wallet_Info_Updating: String { return self._s[216]! }
public var Wallet_Settings_ConfigurationInfo: String { return self._s[217]! }
public var Wallet_WordImport_CanNotRemember: String { return self._s[218]! }
public var Wallet_Sending_Title: String { return self._s[219]! }
public func Wallet_Info_TransactionBlockchainFee(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[220]!, self._r[220]!, [_0])
public var Wallet_Created_ExportErrorText: String { return self._s[208]! }
public func Wallet_Updated_YesterdayAt(_ _0: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[209]!, self._r[209]!, [_0])
}
public var Wallet_Settings_DeleteWalletInfo: String { return self._s[210]! }
public var Wallet_Intro_CreateErrorText: String { return self._s[211]! }
public var Wallet_Sent_ViewWallet: String { return self._s[212]! }
public var Wallet_Send_ErrorInvalidAddress: String { return self._s[213]! }
public var Wallet_Configuration_BlockchainNameChangedText: String { return self._s[214]! }
public func Wallet_Time_PreciseDate_m7(_ _1: String, _ _2: String, _ _3: String) -> (String, [(Int, NSRange)]) {
return formatWithArgumentRanges(self._s[215]!, self._r[215]!, [_1, _2, _3])
}
public var Wallet_Send_Title: String { return self._s[216]! }
public var Wallet_Info_RefreshErrorText: String { return self._s[217]! }
public var Wallet_SecureStorageReset_Title: String { return self._s[218]! }
public var Wallet_Receive_CommentHeader: String { return self._s[219]! }
public var Wallet_Info_ReceiveGrams: String { return self._s[220]! }
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)

View File

@ -402,7 +402,7 @@ final class WatchMediaHandler: WatchRequestHandler {
if let peer = peer, let representation = peer.smallProfileImage {
let imageData = peerAvatarImageData(account: context.account, peer: peer, authorOfMessage: nil, representation: representation, synchronousLoad: false)
if let imageData = imageData {
return imageData |> deliverOn(Queue.concurrentDefaultQueue())
return imageData
|> map { data -> UIImage? in
if let data = data, let image = generateImage(targetSize, contextGenerator: { size, context -> Void in
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
@ -429,7 +429,7 @@ final class WatchMediaHandler: WatchRequestHandler {
let disposable = signal.start(next: { image in
if let image = image, let imageData = image.jpegData(compressionQuality: compressionRate) {
sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage)
sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: true)
}
subscriber?.putNext(key)
}, completed: {